diff --git a/meson.build b/meson.build index c69b81a..7519893 100644 --- a/meson.build +++ b/meson.build @@ -14,7 +14,8 @@ add_project_arguments([ add_project_arguments([ '-DGETTEXT_PACKAGE="wingpanel"', - '-DG_LOG_DOMAIN="wingpanel"' + '-DG_LOG_DOMAIN="wingpanel"', + '-DWNCK_I_KNOW_THIS_IS_UNSTABLE' ], language: 'c' ) @@ -30,6 +31,7 @@ gio_dep = dependency('gio-2.0') gio_unix_dep = dependency('gio-unix-2.0') gmodule_dep = dependency('gmodule-2.0') gtk_dep = dependency('gtk+-3.0', version: '>=3.10') +libwnck_dep = dependency('libwnck-3.0', version: '>=3.14') gee_dep = dependency('gee-0.8') granite_dep = dependency('granite') diff --git a/schemas/io.elementary.desktop.wingpanel.gschema.xml b/schemas/io.elementary.desktop.wingpanel.gschema.xml index a309a1d..aa150f7 100644 --- a/schemas/io.elementary.desktop.wingpanel.gschema.xml +++ b/schemas/io.elementary.desktop.wingpanel.gschema.xml @@ -6,5 +6,23 @@ Sets if the panel uses transparency. Disable this to provide higher contrasts and make indicators better readable. + + + + + + + + + 'Disabled' + Sets if and how the panel will autohide. + Enable this to increase available desktop area and reduce clutter. + + + + 200 + Sets how long before the panel will autohide in milliseconds. + Increase or decrease this value to your preference. + diff --git a/src/PanelWindow.vala b/src/PanelWindow.vala index c334fd0..0b56f3b 100644 --- a/src/PanelWindow.vala +++ b/src/PanelWindow.vala @@ -29,6 +29,13 @@ public class Wingpanel.PanelWindow : Gtk.Window { private int panel_height; private bool expanded = false; private int panel_displacement; + private uint timeout; + private bool hiding = false; + private bool delay = false; + private static GLib.Settings panel_settings = new GLib.Settings ("io.elementary.desktop.wingpanel"); + private string autohide_mode = panel_settings.get_string ("autohide"); + private int autohide_delay = panel_settings.get_int ("delay"); + private Wnck.Screen wnck_screen = Wnck.Screen.get_default (); public PanelWindow (Gtk.Application application) { Object ( @@ -78,16 +85,72 @@ public class Wingpanel.PanelWindow : Gtk.Window { application.set_accels_for_action ("app.cycle", {"Tab"}); application.set_accels_for_action ("app.cycle-back", {"Tab"}); + panel_settings.changed["autohide"].connect (() => { + autohide_mode = panel_settings.get_string ("autohide"); + update_autohide_mode (); + }); + + panel_settings.changed["delay"].connect (() => { + autohide_delay = panel_settings.get_int ("delay"); + }); + add (panel); } private bool animation_step () { - if (panel_displacement <= panel_height * (-1)) { - return false; + if (hiding) { + if (popover_manager.current_indicator != null) { + timeout = 0; + return false; + } + if (panel_displacement >= -1) { + timeout = 0; + update_struts (); + this.enter_notify_event.connect (show_panel); + this.motion_notify_event.connect (show_panel); + delay = true; + return false; + } + panel_displacement++; + } else { + if (panel_displacement <= panel_height * -1) { + timeout = 0; + switch (autohide_mode) { + case "Autohide": + update_struts (); + this.leave_notify_event.connect (hide_panel); + this.focus_out_event.connect (hide_panel); + break; + case "Float": + this.leave_notify_event.connect (hide_panel); + this.focus_out_event.connect (hide_panel); + break; + case "Dodge": + update_struts (); + if (should_hide_active_change (wnck_screen.get_active_window ())) { + this.leave_notify_event.connect (hide_panel); + this.focus_out_event.connect (hide_panel); + } + + break; + case "Dodge-Float": + if (should_hide_active_change (wnck_screen.get_active_window ())) { + this.leave_notify_event.connect (hide_panel); + this.focus_out_event.connect (hide_panel); + } + + break; + default: + this.leave_notify_event.disconnect (hide_panel); + this.focus_out_event.disconnect (hide_panel); + update_struts (); + break; + } + return false; + } + panel_displacement--; } - panel_displacement--; - update_panel_dimensions (); return true; @@ -98,7 +161,134 @@ public class Wingpanel.PanelWindow : Gtk.Window { Services.BackgroundManager.initialize (this.monitor_number, panel_height); - Timeout.add (300 / panel_height, animation_step); + panel_displacement--; + update_panel_dimensions (); + update_autohide_mode (); + } + + private void active_window_changed (Wnck.Window? prev_active_window) { + unowned Wnck.Window? active_window = wnck_screen.get_active_window (); + update_visibility_active_change (active_window); + + if (prev_active_window != null) + prev_active_window.state_changed.disconnect (active_window_state_changed); + if (active_window != null) + active_window.state_changed.connect (active_window_state_changed); + } + + private void active_workspace_changed (Wnck.Workspace? prev_active_workspace) { + unowned Wnck.Window? active_window = wnck_screen.get_active_window (); + update_visibility_active_change (active_window); + } + + private void viewports_changed (Wnck.Screen? screen) { + unowned Wnck.Window? active_window = wnck_screen.get_active_window (); + update_visibility_active_change (active_window); + } + + private void active_window_state_changed (Wnck.Window? window, + Wnck.WindowState changed_mask, Wnck.WindowState new_state) { + update_visibility_active_change (window); + } + + private void update_visibility_active_change (Wnck.Window? active_window) { + if (should_hide_active_change (active_window)) { + this.leave_notify_event.connect (hide_panel); + this.focus_out_event.connect (hide_panel); + delay = false; + hide_panel (); + } else { + this.leave_notify_event.disconnect (hide_panel); + this.focus_out_event.disconnect (hide_panel); + delay = false; + show_panel (); + } + } + + private bool should_hide_active_change (Wnck.Window? active_window) { + unowned Wnck.Workspace active_workspace = wnck_screen.get_active_workspace (); + + return ((active_window != null) && !active_window.is_minimized () && right_type (active_window) + && active_window.is_visible_on_workspace (active_workspace) + && active_window.is_in_viewport (active_workspace) + && is_maximized_at_all (active_window)); + } + + private bool right_type (Wnck.Window? active_window) { + unowned Wnck.WindowType type = active_window.get_window_type (); + return (type == Wnck.WindowType.NORMAL || type == Wnck.WindowType.DIALOG + || type == Wnck.WindowType.TOOLBAR || type == Wnck.WindowType.UTILITY); + } + + private bool is_maximized_at_all (Wnck.Window window) { + return (window.is_maximized_horizontally () + || window.is_maximized_vertically () + || window.is_fullscreen ()); + } + + private bool hide_panel () { + if (timeout > 0) { + Source.remove (timeout); + } + hiding = true; + if (delay) { + Thread.usleep (autohide_delay * 1000); + } + timeout = Timeout.add (100 / panel_height, animation_step); + return true; + } + + private bool show_panel () { + if (timeout > 0) { + Source.remove (timeout); + } + this.enter_notify_event.disconnect (show_panel); + this.motion_notify_event.disconnect (show_panel); + hiding = false; + if (autohide_mode != "Disabled") { + if (delay) { + Thread.usleep (autohide_delay * 1000); + } + timeout = Timeout.add (100 / panel_height, animation_step); + } else { + timeout = Timeout.add (300 / panel_height, animation_step); + } + return true; + } + + private void update_autohide_mode () { + switch (autohide_mode) { + case "Autohide": + case "Float": + delay = true; + wnck_screen.active_window_changed.disconnect (active_window_changed); + wnck_screen.active_workspace_changed.disconnect (active_workspace_changed); + wnck_screen.viewports_changed.disconnect (viewports_changed); + hide_panel (); + break; + case "Dodge": + case "Dodge-Float": + delay = false; + if (!should_hide_active_change (wnck_screen.get_active_window ())) { + this.leave_notify_event.disconnect (hide_panel); + this.focus_out_event.disconnect (hide_panel); + show_panel (); + } else { + hide_panel (); + } + wnck_screen.active_window_changed.connect (active_window_changed); + wnck_screen.active_workspace_changed.connect (active_workspace_changed); + wnck_screen.viewports_changed.connect (viewports_changed); + break; + default: + this.leave_notify_event.connect (hide_panel); + this.focus_out_event.connect (hide_panel); + wnck_screen.active_window_changed.disconnect (active_window_changed); + wnck_screen.active_workspace_changed.disconnect (active_workspace_changed); + wnck_screen.viewports_changed.disconnect (viewports_changed); + show_panel (); + break; + } } private void update_panel_dimensions () { @@ -116,9 +306,29 @@ public class Wingpanel.PanelWindow : Gtk.Window { monitor_x = monitor_dimensions.x; monitor_y = monitor_dimensions.y; - this.move (monitor_x, monitor_y - (panel_height + panel_displacement)); + int wx, wy; + get_position (out wx, out wy); + + /** + * Instead of constantly moving the window for the animation, + * we will only move the window when it has been hidden / shown + * The actual animation is handed off to the panel widget. + */ + if (panel_displacement == -1) { + int y = monitor_y - (panel_height + panel_displacement); + if (wx != monitor_x || wy != y) { + move (monitor_x, y); + } + + panel.draw_y = 0; + } else { + if (wx != 0 || wy != 0) { + move (0, 0); + } + + panel.draw_y = monitor_y - (panel_height + panel_displacement); + } - update_struts (); } private void update_visual () { diff --git a/src/Widgets/Panel.vala b/src/Widgets/Panel.vala index 90aeb78..79b0c0e 100644 --- a/src/Widgets/Panel.vala +++ b/src/Widgets/Panel.vala @@ -27,6 +27,18 @@ public class Wingpanel.Widgets.Panel : Gtk.EventBox { private Gtk.StyleContext style_context; private Gtk.CssProvider? style_provider = null; + private int _draw_y = 0; + public int draw_y { + get { + return _draw_y; + } + + set { + _draw_y = value; + queue_draw (); + } + } + private static Gtk.CssProvider resource_provider; public Panel (Services.PopoverManager popover_manager) { @@ -73,6 +85,11 @@ public class Wingpanel.Widgets.Panel : Gtk.EventBox { Services.BackgroundManager.get_default ().background_state_changed.connect (update_background); } + public override bool draw (Cairo.Context ctx) { + ctx.translate (0, draw_y); + return base.draw (ctx); + } + static construct { resource_provider = new Gtk.CssProvider (); resource_provider.load_from_resource ("io/elementary/wingpanel/panel.css"); diff --git a/src/meson.build b/src/meson.build index c3708dd..4357d4e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,6 +15,7 @@ wingpanel_files = files( wingpanel_deps = [ libwingpanel_dep, granite_dep, + libwnck_dep, gdk_x11_dep ] executable(meson.project_name(),