Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions data/Application.css
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,8 @@ backgrounditem .close-button {
.running-indicator:disabled {
color: @fg_color;
}

dock-button:focus-visible {
background: alpha(@accent_color, 0.5);
border-radius: 6px;
}
4 changes: 3 additions & 1 deletion src/AppSystem/Background/BackgroundItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ public class Dock.BackgroundItem : BaseIconGroup {
}
});

gesture_click.released.connect (popover_menu.popup);
button.clicked.connect (popover_menu.popup);

button.update_property (Gtk.AccessibleProperty.LABEL, _("Background Apps"));
}

private Gtk.Widget create_widget_func (Object obj) {
Expand Down
27 changes: 16 additions & 11 deletions src/AppSystem/Launcher.vala
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,14 @@ public class Dock.Launcher : BaseItem {
transition_type = CROSSFADE
};

overlay.child = image;
var overlay = new Gtk.Overlay () {
child = image
};
overlay.add_overlay (badge_container);
overlay.add_overlay (progress_revealer);

button.child = overlay;

var running_indicator = new Gtk.Image.from_icon_name ("pager-checked-symbolic");
running_indicator.add_css_class ("running-indicator");

Expand Down Expand Up @@ -240,8 +244,7 @@ public class Dock.Launcher : BaseItem {
easing = EASE_IN_OUT_QUAD
};

gesture_click.button = 0;
gesture_click.released.connect (on_click_released);
button.clicked.connect (on_clicked);

var long_press = new Gtk.GestureLongPress () {
touch_only = true
Expand Down Expand Up @@ -301,6 +304,8 @@ public class Dock.Launcher : BaseItem {
add_controller (drop_controller_motion);
drop_controller_motion.enter.connect (queue_dnd_cycle);
drop_controller_motion.leave.connect (remove_dnd_cycle);

button.update_property (Gtk.AccessibleProperty.LABEL, app.app_info.get_display_name ());
}

~Launcher () {
Expand All @@ -320,12 +325,12 @@ public class Dock.Launcher : BaseItem {
remove_dnd_cycle ();
}

private void on_click_released (int n_press, double x, double y) {
var event_display = gesture_click.get_current_event ().get_display ();
var context = event_display.get_app_launch_context ();
context.set_timestamp (gesture_click.get_current_event_time ());
private void on_clicked (uint button, uint32 timestamp) {
unowned var display = Gdk.Display.get_default ();
var context = display.get_app_launch_context ();
context.set_timestamp (timestamp);

switch (gesture_click.get_current_button ()) {
switch (button) {
case Gdk.BUTTON_PRIMARY:
app.launch (context);
break;
Expand All @@ -334,7 +339,7 @@ public class Dock.Launcher : BaseItem {
animate_launch ();
} else {
animate_shake ();
event_display.beep ();
display.beep ();
}
break;
case Gdk.BUTTON_SECONDARY:
Expand All @@ -349,7 +354,7 @@ public class Dock.Launcher : BaseItem {
return;
}

bounce_up.value_to = -0.5 * overlay.get_height ();
bounce_up.value_to = -0.5 * button.get_height ();
bounce_down.value_from = bounce_up.value_to;

bounce_up.play ();
Expand All @@ -360,7 +365,7 @@ public class Dock.Launcher : BaseItem {
return;
}

shake.value_to = -0.1 * overlay.get_width ();
shake.value_to = -0.1 * button.get_width ();
shake.play ();

int repeat_count = 0;
Expand Down
19 changes: 10 additions & 9 deletions src/BaseItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class Dock.BaseItem : Gtk.Box {
bin.height_request = -1;
}

overlay.visible = !value;
button.visible = !value;
}
}

Expand All @@ -70,8 +70,7 @@ public class Dock.BaseItem : Gtk.Box {
* It's needed because top margin messes with dnd offsets and gsk transform.
*/
protected Gtk.Box actionable_box;
protected Gtk.Overlay overlay;
protected Gtk.GestureClick gesture_click;
protected Button button;

protected Granite.Bin bin { get; private set; }

Expand All @@ -91,12 +90,13 @@ public class Dock.BaseItem : Gtk.Box {

construct {
orientation = VERTICAL;
focusable = true;

overlay = new Gtk.Overlay ();
button = new Button ();

// We need the bin because we need the animation to run even if the overlay is not visible
bin = new Granite.Bin () {
child = overlay
child = button
};

actionable_box = new Gtk.Box (VERTICAL, 0);
Expand Down Expand Up @@ -185,9 +185,6 @@ public class Dock.BaseItem : Gtk.Box {

add_controller (motion_controller);

gesture_click = new Gtk.GestureClick ();
add_controller (gesture_click);

if (group == NONE) {
return;
}
Expand All @@ -214,6 +211,10 @@ public class Dock.BaseItem : Gtk.Box {
popover_tooltip.dispose ();
}

public override bool grab_focus () {
return button.grab_focus ();
}

public void set_revealed (bool revealed) {
fade.skip ();
reveal.skip ();
Expand Down Expand Up @@ -281,7 +282,7 @@ public class Dock.BaseItem : Gtk.Box {
}

private void on_drag_begin (Gtk.DragSource drag_source, Gdk.Drag drag) {
var paintable = new Gtk.WidgetPaintable (overlay);
var paintable = new Gtk.WidgetPaintable (button);
drag_source.set_icon (paintable.get_current_image (), drag_offset_x, drag_offset_y);

moving = true;
Expand Down
46 changes: 46 additions & 0 deletions src/Button.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: GPL-3.0
* SPDX-FileCopyrightText: 2026 elementary, Inc. (https://elementary.io)
*/

public class Dock.Button : Granite.Bin {
public signal void clicked (uint button, uint32 timestamp);

public uint mouse_button { get; set; default = 0; }

class construct {
set_accessible_role (BUTTON);
set_css_name ("dock-button");
}

construct {
focusable = true;

var gesture_click = new Gtk.GestureClick ();
bind_property ("mouse-button", gesture_click, "button", SYNC_CREATE);
gesture_click.released.connect (on_released);
add_controller (gesture_click);

var key_controller = new Gtk.EventControllerKey ();
key_controller.key_pressed.connect (on_key_pressed);
add_controller (key_controller);
}

private void on_released (Gtk.GestureClick gesture_click, int n_press, double x, double y) {
clicked (gesture_click.get_current_button (), gesture_click.get_current_event_time ());
}

private bool on_key_pressed (
Gtk.EventControllerKey key_controller,
uint keyval,
uint keycode,
Gdk.ModifierType state
) {
if (keyval == Gdk.Key.space || keyval == Gdk.Key.Return || keyval == Gdk.Key.KP_Enter) {
clicked (Gdk.BUTTON_PRIMARY, key_controller.get_current_event_time ());
return true;
}

return false;
}
}
2 changes: 1 addition & 1 deletion src/ContainerItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public abstract class Dock.ContainerItem : BaseItem {
bind_property ("icon-size", container, "width-request", SYNC_CREATE);
bind_property ("icon-size", container, "height-request", SYNC_CREATE);

overlay.child = container;
button.child = container;

notify["state"].connect (() => {
if ((state != HIDDEN) && !moving) {
Expand Down
2 changes: 1 addition & 1 deletion src/ItemGroup.vala
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

public ListModel items { get; construct; }
public CreateBaseItemFunc create_item_func { get; construct; }
public ListStore current_children { get; private set; }

private Sequence<BaseItem> item_store;
private ListStore current_children;

private Adw.TimedAnimation resize_animation;

Expand Down
87 changes: 86 additions & 1 deletion src/ItemManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
private Gtk.Separator separator;
private DynamicWorkspaceIcon dynamic_workspace_item;
#endif
private ListStore all_item_groups;
private Gtk.FlattenListModel all_items;
private bool changed_focus = false;

class construct {
set_accessible_role (LIST);
}

static construct {
settings = new Settings ("io.elementary.dock");
Expand All @@ -33,14 +40,16 @@
separator_box.append (new TopMargin ());
separator_box.append (separator);

var workspaces_group = new ItemGroup (WorkspaceSystem.get_default ().workspaces, (obj) => new WorkspaceIconGroup ((Workspace) obj));

dynamic_workspace_item = new DynamicWorkspaceIcon ();
#endif

append (app_group);
append (background_group);
#if WORKSPACE_SWITCHER
append (separator_box);
append (new ItemGroup (WorkspaceSystem.get_default ().workspaces, (obj) => new WorkspaceIconGroup ((Workspace) obj)));
append (workspaces_group);
append (dynamic_workspace_item);
#endif
overflow = VISIBLE;
Expand Down Expand Up @@ -149,6 +158,82 @@
WorkspaceSystem.get_default ().load.begin ();
#endif
});

all_item_groups = new GLib.ListStore (typeof (GLib.ListModel));
all_item_groups.append (app_group.current_children);
all_item_groups.append (background_group.current_children);
#if WORKSPACE_SWITCHER
all_item_groups.append (workspaces_group.current_children);

var dynamic_workspace_item_list = new GLib.ListStore (typeof (DynamicWorkspaceIcon));
dynamic_workspace_item_list.append (dynamic_workspace_item);

all_item_groups.append (dynamic_workspace_item_list);
#endif

all_items = new Gtk.FlattenListModel (all_item_groups);
all_items.items_changed.connect ((all_items, position, removed, added) => {
if (!changed_focus) {
((BaseItem) all_items.get_item (0)).grab_focus ();
}
});

var key_controller = new Gtk.EventControllerKey ();
key_controller.key_pressed.connect (on_key_pressed);
add_controller (key_controller);
}

private bool on_key_pressed (uint keyval, uint keycode, Gdk.ModifierType state) {
unowned var current_focus = ((Gtk.Window) root).get_focus ();
if (current_focus == null ||
!(current_focus.is_ancestor (this))
) {
return false;
}

unowned var current_item = current_focus.get_ancestor (typeof (BaseItem));
if (current_item == null) {
return false;
}

int current_position = -1;
var n_items = all_items.n_items;
for (var i = 0; i < n_items; i++) {
var item = (BaseItem) all_items.get_item (i);
if (item == current_item) {
current_position = i;
break;
}
}

if (current_position == -1) {
return false;
}

BaseItem? next_widget = null;
switch (keyval) {
case Gdk.Key.Left:
if (current_position != 0) {
next_widget = (BaseItem) all_items.get_item (current_position - 1);
}
break;
case Gdk.Key.Right:
if (current_position != all_items.n_items - 1) {
next_widget = (BaseItem) all_items.get_item (current_position + 1);
}
break;
default:
return false;
}

if (next_widget != null) {
next_widget.grab_focus ();
changed_focus = true;
queue_draw ();
return true;
}

return false;
}

public void move_launcher_after (BaseItem source, int target_index) {
Expand Down
3 changes: 2 additions & 1 deletion src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public class Dock.MainWindow : Gtk.ApplicationWindow {
construct {
overflow = VISIBLE;
resizable = false;
titlebar = new Gtk.Label ("") { visible = false };
title = _("Dock");
titlebar = new Gtk.Grid () { visible = false };

var dock_box = new Gtk.Box (VERTICAL, 0);
dock_box.append (new TopMargin ());
Expand Down
6 changes: 4 additions & 2 deletions src/WorkspaceSystem/DynamicWorkspaceItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class Dock.DynamicWorkspaceIcon : ContainerItem, WorkspaceItem {
_("New Workspace")
);

button.update_property (Gtk.AccessibleProperty.LABEL, _("New Workspace"));

WorkspaceSystem.get_default ().workspaces.items_changed.connect (update_active_state);
WindowSystem.get_default ().notify["active-workspace"].connect (update_active_state);

Expand All @@ -43,8 +45,8 @@ public class Dock.DynamicWorkspaceIcon : ContainerItem, WorkspaceItem {
null, null
);

gesture_click.button = Gdk.BUTTON_PRIMARY;
gesture_click.released.connect (switch_to_new_workspace);
button.mouse_button = Gdk.BUTTON_PRIMARY;
button.clicked.connect (switch_to_new_workspace);
}

private void update_active_state () {
Expand Down
Loading