← Back to team overview

elementaryart team mailing list archive

[Merge] lp:~victored/granite/tool-button-with-menu-fixes into lp:granite

 

Victor Eduardo has proposed merging lp:~victored/granite/tool-button-with-menu-fixes into lp:granite.

Requested reviews:
  xapantu (xapantu)

For more details, see:
https://code.launchpad.net/~victored/granite/tool-button-with-menu-fixes/+merge/90152
-- 
https://code.launchpad.net/~victored/granite/tool-button-with-menu-fixes/+merge/90152
Your team elementaryart (old) is subscribed to branch lp:granite.
=== modified file 'lib/Widgets/ToolButtonWithMenu.vala'
--- lib/Widgets/ToolButtonWithMenu.vala	2012-01-15 21:04:11 +0000
+++ lib/Widgets/ToolButtonWithMenu.vala	2012-01-25 16:53:27 +0000
@@ -22,11 +22,12 @@
  *
  */
 
-/* 
- * ToolButtonWithMenu 
+/*
+ * ToolButtonWithMenu
  * - support long click / right click with depressed button states
  * - activate a GtkAction if any or popup a menu.
- * (used in history navigation buttons next/prev, appmenu) 
+ * (used in history navigation buttons next/prev, appmenu)
+ *
  */
 
 using Gdk;
@@ -34,124 +35,120 @@
 
 namespace Granite.Widgets {
 
-    public class ToolButtonWithMenu : ToggleToolButton 
-    {
+    public class ToolButtonWithMenu : Gtk.ToggleToolButton {
+
+        public signal void right_click (Gdk.EventButton ev);
+
+        /**
+         * MenuPosition:
+         * CENTER: Center-align the menu relative to the button's position.
+         * LEFT: Left-align the menu relative to the button's position.
+         * RIGHT: Right-align the menu relative to the button's position.
+         * INSIDE_WINDOW: Keep the menu inside the GtkWindow. Center-align when possible.
+         **/
+        public enum MenuPosition {
+            CENTER,
+            LEFT,
+            RIGHT,
+            INSIDE_WINDOW
+        }
+
         public Gtk.Action? myaction;
-
-        /* delegate function used to populate menu */
-        public delegate Gtk.Menu MenuFetcher();
-
-        private int long_press_time = Gtk.Settings.get_default().gtk_double_click_time * 2;
-        private Button button;
         public ulong toggled_sig_id;
+        public MenuPosition menu_position;
+
+        /* Delegate function used to populate menu */
+        public delegate Gtk.Menu MenuFetcher ();
+
+        public MenuFetcher fetcher {
+            set {
+                _fetcher = value;
+                has_fetcher = true;
+            }
+            get {
+                return _fetcher;
+            }
+        }
+
+        public Gtk.Menu menu {
+            get {
+                return _menu;
+            }
+            set {
+                if (has_fetcher) {
+                    warning ("Don't set the menu property on a ToolMenuButton when there is already a menu fetcher");
+                }
+                else {
+                    _menu = value;
+                    update_menu_properties ();
+                }
+            }
+        }
+
+        private int LONG_PRESS_TIME = Gtk.Settings.get_default ().gtk_double_click_time * 2;
         private int timeout = -1;
         private uint last_click_time = -1;
-
-        private PositionType _menu_orientation;
-        public PositionType menu_orientation{
-            set{
-                var orientation = value;
-                switch(orientation){
-                    case(PositionType.TOP):
-                    case(PositionType.BOTTOM):
-                        orientation = PositionType.LEFT;
-                    break;
-                }
-                _menu_orientation = orientation;
-            }
-            get{
-                return _menu_orientation;
-            }
-        }
-
-        public signal void right_click (Gdk.EventButton ev);
-
         private bool has_fetcher = false;
+
         private unowned MenuFetcher _fetcher;
-        public MenuFetcher fetcher{
-            set{
-                _fetcher = value;
-                has_fetcher = true;
-            }
-            get{
-                return _fetcher;
-            }
-        }
-
         private Gtk.Menu _menu;
-        public Gtk.Menu menu {
-            get {
-                    return _menu;
-                }
-            set {
-                    if(has_fetcher)
-                        warning ("Don't set the menu property on a ToolMenuButton when there is allready a menu fetcher");
-                    else{
-                        _menu = value;
-                        update_menu_properties();
-                }
-            }
-        }
-
-        public ToolButtonWithMenu.from_action (Gtk.Action action)
-        {
-            this.from_stock(action.stock_id, IconSize.MENU, action.label, new Gtk.Menu());
-
+        private Gtk.Button button;
+
+        public ToolButtonWithMenu.from_action (Gtk.Action action) {
+            this.from_stock (action.stock_id, IconSize.MENU, action.label, new Gtk.Menu ());
             use_action_appearance = true;
-
-            set_related_action(action);
-
-            action.connect_proxy(this);
+            set_related_action (action);
+            action.connect_proxy (this);
             myaction = action;
         }
 
-        public ToolButtonWithMenu.from_stock (string stock_image, IconSize size, string label, Gtk.Menu menu)
-        {
-            Image image = new Image.from_stock(stock_image, size);
-
-            this(image, label, menu);
+        public ToolButtonWithMenu.from_stock (string stock_image, IconSize size, string label, Gtk.Menu menu) {
+            Image image = new Image.from_stock (stock_image, size);
+            this (image, label, menu);
         }
 
-        private void update_menu_properties()
-        {
+        private void update_menu_properties () {
             menu.attach_to_widget (this, null);
-            menu.deactivate.connect(() => {
+            menu.deactivate.connect ( () => {
                 deactivate_menu ();
             });
-            menu.deactivate.connect(popdown_menu);
+            menu.deactivate.connect (popdown_menu);
         }
 
-        public ToolButtonWithMenu (Image image, string label, Gtk.Menu _menu, PositionType _menu_orientation = Gtk.PositionType.LEFT)
+        public ToolButtonWithMenu (Image image, string label, Gtk.Menu _menu,
+                                    MenuPosition menu_position = MenuPosition.CENTER)
         {
-            this.menu_orientation = _menu_orientation;
+            this.menu_position = menu_position;
 
             icon_widget = image;
-            label_widget = new Label (label);
-            ((Label) label_widget).use_underline = true;
+            label_widget = new Gtk.Label (label);
+            (label_widget as Gtk.Label).use_underline = true;
             can_focus = true;
             set_tooltip_text (label);
 
             menu = _menu;
 
-            mnemonic_activate.connect(on_mnemonic_activate);
+            mnemonic_activate.connect (on_mnemonic_activate);
 
-            button = (Button) get_child();
+            button = get_child () as Gtk.Button;
             button.events |= EventMask.BUTTON_PRESS_MASK
                           |  EventMask.BUTTON_RELEASE_MASK;
 
-            button.button_press_event.connect(on_button_press_event);
-            button.button_release_event.connect(on_button_release_event);
+            button.button_press_event.connect (on_button_press_event);
+            button.button_release_event.connect (on_button_release_event);
         }
 
-        public override void show_all(){
-            menu.show_all();
-            base.show_all();
+        public override void show_all () {
+            menu.show_all ();
+            base.show_all ();
         }
 
         private void deactivate_menu () {
             if (myaction != null)
                 myaction.block_activate ();
+
             active = false;
+
             if (myaction != null)
                 myaction.unblock_activate ();
         }
@@ -159,56 +156,59 @@
         private void popup_menu_and_depress_button (Gdk.EventButton ev) {
             if (myaction != null)
                 myaction.block_activate ();
+
             active = true;
+
             if (myaction != null)
                 myaction.unblock_activate ();
-            popup_menu(ev);
+
+            popup_menu (ev);
         }
 
-        private bool on_button_release_event (Gdk.EventButton ev)
-        {
-            if (ev.time - last_click_time < long_press_time) {
+        private bool on_button_release_event (Gdk.EventButton ev) {
+            if (ev.time - last_click_time < LONG_PRESS_TIME) {
                 if (myaction != null) {
                     myaction.activate ();
                 } else {
                     active = true;
-                    popup_menu(ev);
+                    popup_menu (ev);
                 }
             }
 
-            if(timeout != -1){
-                Source.remove((uint) timeout);
+            if (timeout != -1) {
+                Source.remove ((uint) timeout);
                 timeout = -1;
             }
 
             return true;
         }
 
-        private bool on_button_press_event (Gdk.EventButton ev)
-        {
-            if(timeout == -1 && ev.button == 1) {
+        private bool on_button_press_event (Gdk.EventButton ev) {
+            // If the button is kept pressed, don't make the user wait when there's no action
+            int max_press_time = (myaction != null)? LONG_PRESS_TIME : 0;
+
+            if (timeout == -1 && ev.button == 1) {
                 last_click_time = ev.time;
-                timeout = (int) Timeout.add(long_press_time, () => {
-                    /* long click */
+                timeout = (int) Timeout.add(max_press_time, () => {
+                    // long click
                     timeout = -1;
                     popup_menu_and_depress_button (ev);
                     return false;
                 });
             }
 
-            if(ev.button == 3){
-                /* right_click */
-                right_click(ev);
+            if (ev.button == 3) {
+                // right_click
+                right_click (ev);
+
                 if (myaction != null)
-                    popup_menu_and_depress_button (ev); 
+                    popup_menu_and_depress_button (ev);
             }
 
             return true;
         }
 
-        private bool on_mnemonic_activate (bool group_cycling)
-        {
-            //stdout.printf ("on mnemonic activate\n");
+        private bool on_mnemonic_activate (bool group_cycling) {
             // ToggleButton always grabs focus away from the editor,
             // so reimplement Widget's version, which only grabs the
             // focus if we are group cycling.
@@ -221,46 +221,43 @@
             return true;
         }
 
-        protected new void popup_menu(Gdk.EventButton? ev = null)
-        {
-            if(has_fetcher) fetch_menu();
-
-            /* FIXME select_first works ok but some gtk_return_fail make some nasty verbose lines in the terminal, origin gtk_device_grab_add gtk+-3 */
+        protected new void popup_menu (Gdk.EventButton? ev = null) {
+            if (has_fetcher)
+                fetch_menu ();
 
             try {
                 menu.popup (null,
                             null,
                             get_menu_position,
                             (ev == null) ? 0 : ev.button,
-                            (ev == null) ? get_current_event_time() : ev.time);
+                            (ev == null) ? get_current_event_time () : ev.time);
             } finally {
                 // Highlight the parent
                 if (menu.attach_widget != null)
-                    menu.attach_widget.set_state(StateType.SELECTED);
-                menu.select_first (true);
+                    menu.attach_widget.set_state_flags (StateFlags.SELECTED, true);
+
+                menu.select_first (false);
             }
         }
 
-        protected void popdown_menu ()
-        {
+        protected void popdown_menu () {
             menu.popdown ();
 
             // Unhighlight the parent
             if (menu.attach_widget != null)
-                menu.attach_widget.set_state(Gtk.StateType.NORMAL);
-        }
-
-        private void fetch_menu(){
-            _menu = fetcher();
-            update_menu_properties();
-        }
-
-        private void get_menu_position (Gtk.Menu menu, out int x, out int y, out bool push_in)
-        {
+                menu.attach_widget.set_state_flags (StateFlags.NORMAL, true);
+        }
+
+        private void fetch_menu () {
+            _menu = fetcher ();
+            update_menu_properties ();
+        }
+
+        private void get_menu_position (Gtk.Menu menu, out int x, out int y, out bool push_in) {
             Allocation menu_allocation;
             menu.get_allocation (out menu_allocation);
             if (menu.attach_widget == null ||
-                menu.attach_widget.get_window() == null) {
+                menu.attach_widget.get_window () == null) {
                 // Prevent null exception in weird cases
                 x = 0;
                 y = 0;
@@ -268,19 +265,60 @@
                 return;
             }
 
-            menu.attach_widget.get_window().get_origin (out x, out y);
+            menu.attach_widget.get_window ().get_origin (out x, out y);
             Allocation allocation;
-            menu.attach_widget.get_allocation(out allocation);
-
-            x += allocation.x;
-            x -= menu_allocation.width/2;
-            x += allocation.width/2;
+            menu.attach_widget.get_allocation (out allocation);
+
+            if (menu_position == MenuPosition.RIGHT) {
+                x += allocation.x;
+                x -= menu_allocation.width;
+                x += allocation.width;
+            }
+            else if (menu_position != MenuPosition.LEFT) {
+                /* Centered menu */
+                x += allocation.x;
+                x -= menu_allocation.width / 2;
+                x += allocation.width / 2;
+            }
+
+            int width, height;
+            menu.get_size_request (out width, out height);
+
+            if (menu_position == MenuPosition.INSIDE_WINDOW) {
+                /* Get window geometry */
+                Gtk.Widget? parent_widget = get_parent ();
+                Gtk.Widget? next_parent = parent_widget.get_parent ();
+
+                while (true) {
+                    if (parent_widget != null && next_parent != null) {
+                        parent_widget = parent_widget.get_parent();
+                        next_parent = parent_widget.get_parent();
+                    }
+
+                    if (parent_widget == null || next_parent == null)
+                        break;
+                }
+
+                Allocation window_allocation;
+                parent_widget.get_allocation (out window_allocation);
+
+                parent_widget.get_window ().get_origin (out x, out y);
+                int parent_window_x0 = x;
+                int parent_window_xf = parent_window_x0 + window_allocation.width;
+
+                // Now check if the menu is outside the window and un-center it
+                // if that's the case
+
+                if (x + menu_allocation.width > parent_window_xf)
+                    x = parent_window_xf - menu_allocation.width; // Move to left
+
+                if (x < parent_window_x0)
+                    x = parent_window_x0; // Move to right
+            }
+
             y += allocation.y;
 
-            int width, height;
-            menu.get_size_request(out width, out height);
-
-            if (y + height >= menu.attach_widget.get_screen().get_height())
+            if (y + height >= menu.attach_widget.get_screen ().get_height ())
                 y -= height;
             else
                 y += allocation.height;


Follow ups