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
12 changes: 12 additions & 0 deletions data/Application.css
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,15 @@ backgrounditem .close-button {
.running-indicator:disabled {
color: @fg_color;
}

.application-menu {
background: alpha(@bg_color, 0.8);
}

.results-list {
background: none;
}

.app-grid {
/* border-spacing: 20px; */
}
5 changes: 5 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ endif
add_project_arguments(vala_flags, language: 'vala')

glib_version = '2.74'

detective_dep = dependency('detective')
gio_dep = dependency('gio-2.0', version: '>=@0@'.format(glib_version))
gio_unix_dep = dependency('gio-unix-2.0', version: '>=@0@'.format(glib_version))
glib_dep = dependency('glib-2.0', version: '>=@0@'.format(glib_version))
Expand All @@ -32,11 +34,13 @@ x11_dep = dependency('x11')
granite_dep = dependency('granite-7', version: '>=7.7.0')
adw_dep = dependency('libadwaita-1')
m_dep = cc.find_library('m')
tracker_dep = dependency('tracker-sparql-3.0')
wl_client_dep = dependency('wayland-client')

subdir('protocol')

dependencies = [
detective_dep,
gio_dep,
gio_unix_dep,
glib_dep,
Expand All @@ -48,6 +52,7 @@ dependencies = [
granite_dep,
adw_dep,
m_dep,
tracker_dep,
wl_client_dep,
pantheon_desktop_shell_dep
]
Expand Down
21 changes: 4 additions & 17 deletions src/AppSystem/App.vala
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ public class Dock.App : Object {
}
}

public signal void removed ();

public bool pinned { get; construct set; }
public GLib.DesktopAppInfo app_info { get; construct; }

public bool pinned { get; set; }

public bool count_visible { get; private set; default = false; }
public int64 current_count { get; private set; default = 0; }
public bool progress_visible { get; set; default = false; }
Expand Down Expand Up @@ -59,8 +58,8 @@ public class Dock.App : Object {

private string appstream_comp_id = "";

public App (GLib.DesktopAppInfo app_info, bool pinned) {
Object (app_info: app_info, pinned: pinned);
public App (GLib.DesktopAppInfo app_info) {
Object (app_info: app_info);
}

static construct {
Expand Down Expand Up @@ -123,10 +122,6 @@ public class Dock.App : Object {
on_appcenter_dbus_changed.begin ();
}

notify["pinned"].connect (() => {
check_remove ();
});

WindowSystem.get_default ().notify["active-workspace"].connect (() => {
notify_property ("running-on-active-workspace");
});
Expand Down Expand Up @@ -187,12 +182,6 @@ public class Dock.App : Object {
return false;
}

private void check_remove () {
if (!pinned && !running) {
removed ();
}
}

public void update_windows (GLib.GenericArray<Window>? new_windows) {
if (new_windows == null) {
windows = new GLib.GenericArray<Window> ();
Expand All @@ -206,8 +195,6 @@ public class Dock.App : Object {
if (launching && running) {
launching = false;
}

check_remove ();
}

public void perform_unity_update (VariantIter prop_iter) {
Expand Down
164 changes: 164 additions & 0 deletions src/AppSystem/AppIconWidget.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* SPDX-License-Identifier: GPL-3.0
* SPDX-FileCopyrightText: 2022-2025 elementary, Inc. (https://elementary.io)
*/

public class Dock.AppIconWidget : Granite.Bin {
private static Settings? notify_settings;

static construct {
if (SettingsSchemaSource.get_default ().lookup ("io.elementary.notifications", true) != null) {
notify_settings = new Settings ("io.elementary.notifications");
}
}

public App app { get; construct; }

public int icon_size { get; construct set; }

private Gtk.Label badge;
private Gtk.Revealer progress_revealer;
private Adw.TimedAnimation badge_fade;
private Adw.TimedAnimation badge_scale;

private Binding current_count_binding;

public AppIconWidget (App app) {
Object (app: app);
}

construct {
var image = new Gtk.Image ();
bind_property ("icon-size", image, "pixel-size", SYNC_CREATE);

var icon = app.app_info.get_icon ();
if (icon != null && Gtk.IconTheme.get_for_display (Gdk.Display.get_default ()).has_gicon (icon)) {
image.gicon = icon;
} else {
image.gicon = new ThemedIcon ("application-default-icon");
}

badge = new Gtk.Label ("!");
badge.add_css_class (Granite.STYLE_CLASS_BADGE);

var badge_container = new Granite.Bin () {
can_target = false,
child = badge,
halign = END,
valign = START,
overflow = VISIBLE
};

progress_revealer = new Gtk.Revealer () {
can_target = false,
transition_type = CROSSFADE
};

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

child = overlay;

// We have to destroy the progressbar when it is not needed otherwise it will
// cause continuous layouting of the surface see https://github.com/elementary/dock/issues/279
progress_revealer.notify["child-revealed"].connect (() => {
if (!progress_revealer.child_revealed) {
progress_revealer.child = null;
}
});

badge_scale = new Adw.TimedAnimation (
badge, 0.25, 1,
Granite.TRANSITION_DURATION_OPEN,
new Adw.CallbackAnimationTarget ((val) => {
var height = badge_container.get_height ();
var width = badge_container.get_width ();

var x = (float) (width - (val * width)) / 2;
var y = (float) (height - (val * height)) / 2;

badge.allocate (
width, height, -1,
new Gsk.Transform ().scale ((float) val, (float) val).translate (Graphene.Point ().init (x, y))
);
})
);

badge_fade = new Adw.TimedAnimation (
badge, 0, 1,
Granite.TRANSITION_DURATION_OPEN,
new Adw.CallbackAnimationTarget ((val) => {
badge.opacity = val;
})
) {
easing = EASE_IN_OUT_QUAD
};

app.notify["count-visible"].connect (update_badge_revealed);
update_badge_revealed ();
current_count_binding = app.bind_property ("current_count", badge, "label", SYNC_CREATE,
(binding, srcval, ref targetval) => {
var src = (int64) srcval;

if (src > 0) {
targetval.set_string ("%lld".printf (src));
} else {
targetval.set_string ("!");
}

return true;
}, null
);

if (notify_settings != null) {
notify_settings.changed["do-not-disturb"].connect (update_badge_revealed);
}

app.notify["progress-visible"].connect (update_progress_revealer);
update_progress_revealer ();
}

private void update_badge_revealed () {
badge_fade.skip ();
badge_scale.skip ();

// Avoid a stutter at the beginning
badge.opacity = 0;

if (app.count_visible && (notify_settings == null || !notify_settings.get_boolean ("do-not-disturb"))) {
badge_fade.duration = Granite.TRANSITION_DURATION_OPEN;
badge_fade.reverse = false;

badge_scale.duration = Granite.TRANSITION_DURATION_OPEN;
badge_scale.easing = EASE_OUT_BACK;
badge_scale.reverse = false;
} else {
badge_fade.duration = Granite.TRANSITION_DURATION_CLOSE;
badge_fade.reverse = true;

badge_scale.duration = Granite.TRANSITION_DURATION_CLOSE;
badge_scale.easing = EASE_OUT_QUAD;
badge_scale.reverse = true;
}

badge_fade.play ();
badge_scale.play ();
}

private void update_progress_revealer () {
progress_revealer.reveal_child = app.progress_visible;

// See comment above and https://github.com/elementary/dock/issues/279
if (progress_revealer.reveal_child && progress_revealer.child == null) {
var progress_bar = new Gtk.ProgressBar () {
valign = END
};
app.bind_property ("progress", progress_bar, "fraction", SYNC_CREATE);

progress_revealer.child = progress_bar;
}
}
}
34 changes: 34 additions & 0 deletions src/AppSystem/AppMenuFactory.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* SPDX-License-Identifier: GPL-3.0
* SPDX-FileCopyrightText: 2026 elementary, Inc. (https://elementary.io)
*/

namespace Dock.AppMenuFactory {
private const string ACTION_GROUP_PREFIX = "launcher";
private const string ACTION_PREFIX = ACTION_GROUP_PREFIX + ".";
private const string PINNED_ACTION = "pinned";

public Gtk.Popover create_app_menu (App app) {
var shell_section = new Menu ();
shell_section.append (_("Keep in Dock"), ACTION_PREFIX + PINNED_ACTION);

if (Environment.find_program_in_path ("io.elementary.appcenter") != null) {
shell_section.append (_("Uninstall"), App.ACTION_PREFIX + App.UNINSTALL_ACTION);
shell_section.append (_("View in AppCenter"), App.ACTION_PREFIX + App.VIEW_ACTION);
}

var menu = new Menu ();
menu.append_section (null, app.app_action_menu);
menu.append_section (null, shell_section);

var popover = new Gtk.PopoverMenu.from_model (menu);

var action_group = new SimpleActionGroup ();
action_group.add_action (new PropertyAction (PINNED_ACTION, app, "pinned"));
popover.insert_action_group (ACTION_GROUP_PREFIX, action_group);

popover.insert_action_group (App.ACTION_GROUP_PREFIX, app.app_action_group);

return popover;
}
}
Loading
Loading