[gnome-calculator] Prepare for GTK4 porting.



commit 6a06beeb8833bd3989a228474897cc18073fcd72
Author: Robert Roth <robert roth off gmail com>
Date:   Fri Oct 1 17:12:25 2021 +0000

    Prepare for GTK4 porting.
    
    * refactored function and variable popover to have a common ancestor handling most of the gtk-specific 
work
    * changed the way space character is removed from function and variable name to use changed signal 
instead of key_press event to avoid the need for key controller
    * use a Glib ListStore and bind that as a model to the function/variable popover instead of working 
directly with ListBoxRows
    * use EventController key on the application window instead of catching the key_press event

 lib/math-function.vala          |  10 +++
 src/gnome-calculator.vala       |   2 +-
 src/math-buttons.vala           |  70 +++++++++++-------
 src/math-display.vala           |  58 ++++++++-------
 src/math-function-popover.vala  | 143 +++++++++++--------------------------
 src/math-popover.vala           |  92 ++++++++++++++++++++++++
 src/math-variable-popover.vala  | 153 +++++++++++++++-------------------------
 src/math-window.vala            |  25 ++++---
 src/meson.build                 |   1 +
 src/ui/math-function-popover.ui |   7 +-
 src/ui/math-variable-popover.ui |   3 +-
 11 files changed, 297 insertions(+), 267 deletions(-)
---
diff --git a/lib/math-function.vala b/lib/math-function.vala
index de3b44a3..473f6c93 100644
--- a/lib/math-function.vala
+++ b/lib/math-function.vala
@@ -31,6 +31,16 @@ public class MathFunction : Object
         get { return _description; }
     }
 
+    public static int name_compare_func (MathFunction function1, MathFunction function2)
+    {
+        return strcmp (function1.name, function2.name);
+    }
+
+    public static bool name_equal_func (MathFunction function1, MathFunction function2)
+    {
+        return function1.name == function2.name;
+    }
+
     public MathFunction (string function_name, string[] arguments, string? expression, string? description)
     {
         _name = function_name;
diff --git a/src/gnome-calculator.vala b/src/gnome-calculator.vala
index 49b0b92b..befe3b73 100644
--- a/src/gnome-calculator.vala
+++ b/src/gnome-calculator.vala
@@ -306,7 +306,7 @@ public class Calculator : Gtk.Application
     {
         try
         {
-            Gtk.show_uri_on_window (get_active_window (), "help:gnome-calculator", 
Gtk.get_current_event_time ());
+            Gtk.show_uri (get_active_window ().get_screen (), "help:gnome-calculator", Gdk.CURRENT_TIME);
         }
         catch (Error e)
         {
diff --git a/src/math-buttons.vala b/src/math-buttons.vala
index 7e739f93..97d86f9d 100644
--- a/src/math-buttons.vala
+++ b/src/math-buttons.vala
@@ -327,28 +327,6 @@ public class MathButtons : Gtk.Box
             prog_view_more_button.visible = visible;
     }
 
-    private void update_view_more_active ()
-    {
-        switch (mode)
-        {
-        default:
-        case ButtonMode.BASIC:
-            break;
-        case ButtonMode.ADVANCED:
-            if (adv_panel != null)
-                converter.view_more_active = prog_view_more_button.active = (adv_panel as 
Hdy.Leaflet).visible_child_name == "advanced";
-            break;
-        case ButtonMode.FINANCIAL:
-            if (fin_panel != null)
-                converter.view_more_active = prog_view_more_button.active = (fin_panel as 
Hdy.Leaflet).visible_child_name == "advanced";
-            break;
-        case ButtonMode.PROGRAMMING:
-            if (prog_panel != null)
-                converter.view_more_active = prog_view_more_button.active = prog_leaflet.visible_child_name 
== "advanced";
-            break;
-        }
-    }
-
     private Gtk.Widget load_mode (ButtonMode mode)
     {
         Gtk.Builder builder;
@@ -447,10 +425,22 @@ public class MathButtons : Gtk.Box
             menu_button.menu_model = create_word_size_menu ();
         menu_button = builder.get_object ("calc_memory_button") as Gtk.MenuButton;
         if (menu_button != null)
-            menu_button.popover = new MathVariablePopover (equation);
+        {
+            var model = new ListStore(typeof(MathVariable));
+            MathVariablePopover math_popover = new MathVariablePopover (equation, model, (a,b) => 
MathVariable.name_compare_func(a as MathVariable, b as MathVariable));
+            fill_variables_model (model, math_popover, equation);
+            menu_button.popover = math_popover;
+        }
         menu_button = builder.get_object ("calc_function_button") as Gtk.MenuButton;
+
+        FunctionManager function_manager = FunctionManager.get_default_function_manager ();
         if (menu_button != null)
-            menu_button.popover = new MathFunctionPopover (equation);
+        {
+            var model = new ListStore(typeof(MathFunction));
+            MathFunctionPopover math_popover = new MathFunctionPopover (equation, model);
+            fill_functions_model (model, math_popover, function_manager);
+            menu_button.popover = math_popover;
+        }
 
         if (mode == ButtonMode.PROGRAMMING)
         {
@@ -494,6 +484,38 @@ public class MathButtons : Gtk.Box
         return panel;
     }
 
+    private void fill_functions_model (ListStore model, MathPopover<MathFunction> math_popover, 
FunctionManager function_manager)
+    {
+        var names = function_manager.get_names ();
+
+        for (var i = 0; names[i] != null; i++)
+        {
+            var function = function_manager[names[i]];
+            math_popover.item_added_cb (function);
+        }
+
+        function_manager.function_added.connect (f=>math_popover.item_added_cb(f as MathFunction));
+        function_manager.function_edited.connect (f=>math_popover.item_edited_cb(f as MathFunction));
+        function_manager.function_deleted.connect (f=>math_popover.item_deleted_cb(f as MathFunction));
+    }
+
+    private void fill_variables_model (ListStore model, MathPopover<MathVariable> math_popover, MathEquation 
equation)
+    {
+        // Fill variable list
+        var names = equation.variables.get_names ();
+        for (var i = 0; names[i] != null; i++)
+        {
+            var value = equation.variables[names[i]];
+            math_popover.item_added_cb (new MathVariable(names[i], value));
+        }
+
+        math_popover.item_added_cb (new MathVariable ("rand", null));
+        // Listen for variable changes
+        equation.variables.variable_added.connect ((name, value) => math_popover.item_added_cb (new 
MathVariable (name, value)));
+        equation.variables.variable_edited.connect ((name, value) => math_popover.item_edited_cb (new 
MathVariable (name, value)));
+        equation.variables.variable_deleted.connect ((name) => math_popover.item_deleted_cb (new 
MathVariable (name, null)));
+    }
+
     private void converter_changed_cb ()
     {
         Unit from_unit, to_unit;
diff --git a/src/math-display.vala b/src/math-display.vala
index 93f2dc39..d104e812 100644
--- a/src/math-display.vala
+++ b/src/math-display.vala
@@ -21,6 +21,8 @@ public class MathDisplay : Gtk.Box
     /* Buffer that shows errors etc */
     Gtk.TextBuffer info_buffer;
 
+    Gtk.EventControllerKey event_controller;
+
     /* Spinner widget that shows if we're calculating a response */
     Gtk.Spinner spinner;
     public bool completion_visible { get; set;}
@@ -65,7 +67,8 @@ public class MathDisplay : Gtk.Box
         source_view.set_size_request (20, 20);
         source_view.get_accessible ().set_role (Atk.Role.EDITBAR);
         //FIXME:<property name="AtkObject::accessible-description" translatable="yes" comments="Accessible 
description for the area in which results are displayed">Result Region</property>
-        source_view.key_press_event.connect (key_press_cb);
+        event_controller = new Gtk.EventControllerKey(source_view); //.key_press_event.connect 
(key_press_cb);
+        event_controller.key_pressed.connect (key_press_cb);
         create_autocompletion ();
         completion_visible = false;
         completion_selected = false;
@@ -152,31 +155,27 @@ public class MathDisplay : Gtk.Box
         }
     }
 
-    protected override bool key_press_event (Gdk.EventKey event)
-    {
-        return source_view.key_press_event (event);
-    }
-
-    private bool key_press_cb (Gdk.EventKey event)
+    private bool key_press_cb (Gtk.EventControllerKey controller, uint keyval, uint keycode, 
Gdk.ModifierType mod_state)
     {
+        info ("event\n");
         /* Clear on escape */
-        var state = event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK);
+        var state = mod_state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK);
 
-        if ((event.keyval == Gdk.Key.Escape && state == 0 && !completion_visible) ||
-            (event.keyval == Gdk.Key.Delete && (event.state & Gdk.ModifierType.CONTROL_MASK) == 
Gdk.ModifierType.CONTROL_MASK))
+        if ((keyval == Gdk.Key.Escape && state == 0 && !completion_visible) ||
+            (keyval == Gdk.Key.Delete && (mod_state & Gdk.ModifierType.CONTROL_MASK) == 
Gdk.ModifierType.CONTROL_MASK))
         {
             equation.clear ();
             status_changed_cb ();
             return true;
-        } else if (event.keyval == Gdk.Key.Escape && state == 0 && completion_visible)
+        } else if (keyval == Gdk.Key.Escape && state == 0 && completion_visible)
         /* If completion window is shown and escape is pressed, hide it */
         {
             Gtk.SourceCompletion completion = source_view.get_completion ();
             completion.hide ();
             return true;
-        } else if (state == Gdk.ModifierType.MOD1_MASK && (event.keyval == Gdk.Key.Left || event.keyval == 
Gdk.Key.Right))
+        } else if (state == Gdk.ModifierType.MOD1_MASK && (keyval == Gdk.Key.Left || keyval == 
Gdk.Key.Right))
         {
-            switch (event.keyval)
+            switch (keyval)
             {
             case Gdk.Key.Left:
                 history.current -= 1;
@@ -199,7 +198,7 @@ public class MathDisplay : Gtk.Box
 
         /* Treat keypad keys as numbers even when numlock is off */
         uint new_keyval = 0;
-        switch (event.keyval)
+        switch (keyval)
         {
         case Gdk.Key.KP_Insert:
             new_keyval = Gdk.Key.@0;
@@ -238,25 +237,24 @@ public class MathDisplay : Gtk.Box
 
         if (new_keyval != 0)
         {
-            var new_event = event; // FIXME: Does this copy?
-            new_event.keyval = new_keyval;
-            return key_press_event (new_event);
+            info ("forwarding\n");
+            return key_press_cb (controller, new_keyval, keycode, mod_state);
         }
 
-        var c = Gdk.keyval_to_unicode (event.keyval);
+        var c = Gdk.keyval_to_unicode (keyval);
 
         /* Solve on [=] if the input is not a variable name */
-        if (event.keyval == Gdk.Key.equal || event.keyval == Gdk.Key.KP_Equal)
+        if (keyval == Gdk.Key.equal || keyval == Gdk.Key.KP_Equal)
         {
             if (!(only_variable_name.match((string) equation.equation)
                 || only_function_definition.match((string) equation.equation)))
             {
-                event.keyval = Gdk.Key.KP_Enter;
+                keyval = Gdk.Key.KP_Enter;
             }
         }
 
         /* Solve on enter */
-        if (event.keyval == Gdk.Key.Return || event.keyval == Gdk.Key.KP_Enter)
+        if (keyval == Gdk.Key.Return || keyval == Gdk.Key.KP_Enter)
         {
             if (completion_visible && completion_selected)
                 return false;
@@ -265,11 +263,11 @@ public class MathDisplay : Gtk.Box
         }
 
         /* Numeric keypad will insert '.' or ',' depending on layout */
-        if ((event.keyval == Gdk.Key.KP_Decimal) ||
-            (event.keyval == Gdk.Key.KP_Separator) ||
-            (event.keyval == Gdk.Key.period) ||
-            (event.keyval == Gdk.Key.decimalpoint) ||
-            (event.keyval == Gdk.Key.comma))
+        if ((keyval == Gdk.Key.KP_Decimal) ||
+            (keyval == Gdk.Key.KP_Separator) ||
+            (keyval == Gdk.Key.period) ||
+            (keyval == Gdk.Key.decimalpoint) ||
+            (keyval == Gdk.Key.comma))
         {
             equation.insert_numeric_point ();
             return true;
@@ -308,7 +306,7 @@ public class MathDisplay : Gtk.Box
         /* Shortcuts */
         if (state == Gdk.ModifierType.CONTROL_MASK)
         {
-            switch (event.keyval)
+            switch (keyval)
             {
             case Gdk.Key.bracketleft:
                 equation.insert ("⌈");
@@ -350,7 +348,7 @@ public class MathDisplay : Gtk.Box
         }
         if (state == Gdk.ModifierType.MOD1_MASK)
         {
-            switch (event.keyval)
+            switch (keyval)
             {
             case Gdk.Key.bracketleft:
                 equation.insert ("⌊");
@@ -365,7 +363,7 @@ public class MathDisplay : Gtk.Box
         {
             if (!equation.has_selection)
                 equation.remove_trailing_spaces ();
-            switch (event.keyval)
+            switch (keyval)
             {
             case Gdk.Key.@0:
             case Gdk.Key.KP_0:
@@ -413,7 +411,7 @@ public class MathDisplay : Gtk.Box
         {
             if (!equation.has_selection)
                 equation.remove_trailing_spaces ();
-            switch (event.keyval)
+            switch (keyval)
             {
             case Gdk.Key.@0:
             case Gdk.Key.KP_0:
diff --git a/src/math-function-popover.vala b/src/math-function-popover.vala
index 159f99c2..98959996 100644
--- a/src/math-function-popover.vala
+++ b/src/math-function-popover.vala
@@ -8,14 +8,16 @@
  * license.
  */
 
+class Something
+{
+
+}
 [GtkTemplate (ui = "/org/gnome/calculator/math-function-popover.ui")]
-public class MathFunctionPopover : Gtk.Popover
+public class MathFunctionPopover : MathPopover<MathFunction>
 {
     // Used to pretty print function arguments, e.g. f(x, y, z)
     private static string[] FUNCTION_ARGS = {"x","y","z","u","v","w","a","b","c","d"};
 
-    private MathEquation equation;
-
     [GtkChild]
     private unowned Gtk.ListBox function_list;
 
@@ -28,51 +30,38 @@ public class MathFunctionPopover : Gtk.Popover
     [GtkChild]
     private unowned Gtk.SpinButton add_arguments_button;
 
-    public MathFunctionPopover (MathEquation equation)
+    public MathFunctionPopover (MathEquation equation, ListStore model)
     {
-        this.equation = equation;
-
-        FunctionManager function_manager = FunctionManager.get_default_function_manager ();
-        var names = function_manager.get_names ();
-
-        for (var i = 0; names[i] != null; i++)
-        {
-            var function = function_manager[names[i]];
-            function_list.add (make_function_row (function));
-        }
-
-        // Sort list
-        function_list.set_sort_func (function_list_sort);
+        base (equation, model, (a,b) => MathFunction.name_compare_func (a as MathFunction,b as 
MathFunction));
 
-        function_manager.function_added.connect ((function) => {
-            function_list.add (make_function_row (function));
-        });
-        function_manager.function_edited.connect ((function) => {
-            function_list.remove (find_row_for_function (function));
-            function_list.add (make_function_row (function));
-        });
-        function_manager.function_deleted.connect ((function) => {
-            function_list.remove (find_row_for_function (function));
-        });
+        function_list.bind_model (model, (item) => make_item_row(item as MathFunction));
 
         add_arguments_button.set_range (1, 10);
         add_arguments_button.set_increments (1, 1);
+        item_edited.connect (function_edited_cb);
+        item_deleted.connect (function_deleted_cb);
     }
 
-    private Gtk.ListBoxRow? find_row_for_function (MathFunction function)
+    private void function_edited_cb (MathFunction function)
     {
-        weak Gtk.ListBoxRow? row = null;
-        function_list.foreach ((child) => {
-            if (function.name == child.get_data<MathFunction> ("function").name)
-                row = child as Gtk.ListBoxRow;
-        });
-        return row;
+        var function_to_edit = "%s(%s)=%s@%s".printf (function.name,
+                                                      string.joinv (";", function.arguments),
+                                                      function.expression,
+                                                      function.description);
+        equation.clear ();
+        equation.insert (function_to_edit);
+    }
+
+    private void function_deleted_cb (MathFunction function)
+    {
+        var function_manager = FunctionManager.get_default_function_manager ();
+        function_manager.delete (function.name);
     }
 
     [GtkCallback]
     private void insert_function_cb (Gtk.ListBoxRow row)
     {
-        var function = row.get_data<MathFunction> ("function");
+        var function = model.get_item (row.get_index ()) as MathFunction;
         equation.insert (function.name + "()");
 
         // Place the cursor between the parentheses after inserting the function
@@ -83,7 +72,7 @@ public class MathFunctionPopover : Gtk.Popover
     }
 
     [GtkCallback]
-    private bool function_name_mouse_click_cb (Gtk.Widget widget, Gdk.EventButton event)
+    private bool function_name_focus_cb (Gtk.Widget widget, Gtk.DirectionType direction)
     {
         if (!this.function_name_entry_placeholder_reseted)
         {
@@ -95,21 +84,12 @@ public class MathFunctionPopover : Gtk.Popover
     }
 
     [GtkCallback]
-    private bool function_name_key_press_cb (Gtk.Widget widget, Gdk.EventKey event)
+    private void function_name_entry_changed_cb (Gtk.Editable editable)
     {
         this.function_name_entry_placeholder_reseted = true;
-
-        /* Can't have whitespace in names, so replace with underscores */
-        if (event.keyval == Gdk.Key.space || event.keyval == Gdk.Key.KP_Space)
-            event.keyval = Gdk.Key.underscore;
-
-        return false;
-    }
-
-    [GtkCallback]
-    private void function_name_changed_cb ()
-    {
-        add_function_button.sensitive = function_name_entry.get_text () != "";
+        var entry = editable as Gtk.Entry;
+        entry.text = entry.text.replace (" ", "_");
+        add_function_button.sensitive = entry.text != "";
     }
 
     [GtkCallback]
@@ -129,70 +109,33 @@ public class MathFunctionPopover : Gtk.Popover
         equation.insert (name);
     }
 
-    private void save_function_cb (Gtk.Widget widget)
+    protected override bool is_deletable (MathFunction function)
     {
-        var function = widget.get_data<MathFunction> ("function");
-        var function_to_edit = "%s(%s)=%s@%s".printf (function.name,
-                                                      string.joinv (";", function.arguments),
-                                                      function.expression,
-                                                      function.description);
-        equation.clear ();
-        equation.insert (function_to_edit);
+        return function.is_custom_function ();
     }
 
-    private void delete_function_cb (Gtk.Widget widget)
+    protected override bool is_editable (MathFunction function)
     {
-        var function = widget.get_data<MathFunction> ("function");
-
-        var function_manager = FunctionManager.get_default_function_manager ();
-        function_manager.delete (function.name);
+        return function.is_custom_function ();
     }
 
-    private Gtk.ListBoxRow make_function_row (MathFunction function)
+    protected override string get_item_text (MathFunction function)
     {
-        var row = new Gtk.ListBoxRow ();
-        row.get_style_context ().add_class ("popover-row");
-        row.set_data<MathFunction> ("function", function);
-        row.set_tooltip_text ("%s".printf (function.description));
-
-        var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
-
         var expression = "(x)";
         if (function.is_custom_function ())
             expression = "(%s)".printf (string.joinv (";", function.arguments));
 
-        var label = new Gtk.Label ("<b>%s</b>%s".printf (function.name, expression));
-        label.set_margin_start (6);
-        label.set_use_markup (true);
-        label.halign = Gtk.Align.START;
-        hbox.pack_start (label, true, true, 0);
-
-        if (function.is_custom_function ())
-        {
-            var button = new Gtk.Button.from_icon_name ("edit-symbolic");
-            button.get_style_context ().add_class ("flat");
-            button.set_data<MathFunction> ("function", function);
-            button.clicked.connect (save_function_cb);
-            hbox.pack_start (button, false, true, 0);
-
-            button = new Gtk.Button.from_icon_name ("list-remove-symbolic");
-            button.get_style_context ().add_class ("flat");
-            button.set_data<MathFunction> ("function", function);
-            button.clicked.connect (delete_function_cb);
-            hbox.pack_start (button, false, true, 0);
-        }
-
-        row.add (hbox);
-        row.show_all ();
-
-        return row;
+        string text = "<b>%s</b>%s".printf (function.name, expression);
+        return text;
     }
 
-    private int function_list_sort (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2)
+    protected override int get_item_index (MathFunction item)
     {
-        var function1 = row1.get_data<MathFunction> ("function");
-        var function2 = row2.get_data<MathFunction> ("function");
-
-        return strcmp (function1.name, function2.name);
+        uint position;
+        if (model.find_with_equal_func (item as Object, (a, b) => (MathFunction.name_equal_func(a as 
MathFunction, b as MathFunction)), out position))
+            return (int)position;
+        else
+            return -1;
     }
+
 }
diff --git a/src/math-popover.vala b/src/math-popover.vala
new file mode 100644
index 00000000..aba349c1
--- /dev/null
+++ b/src/math-popover.vala
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 Robert Roth
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public abstract class MathPopover<T> : Gtk.Popover
+{
+    protected MathEquation equation;
+
+    protected ListStore model;
+
+    private CompareDataFunc<T> compare_func;
+
+    protected MathPopover (MathEquation equation, ListStore model, CompareDataFunc<T> compare_func)
+    {
+        this.equation = equation;
+        this.model = model;
+        this.compare_func = (a,b) => compare_func(a,b);
+    }
+
+    public void item_added_cb (T item)
+    {
+        model.insert_sorted (item as Object, (a,b) => compare_func(a, b));
+    }
+
+    public void item_edited_cb (T item)
+    {
+        item_deleted_cb (item);
+        item_added_cb (item);
+    }
+
+    public void item_deleted_cb (T item)
+    {
+        int position = get_item_index (item);
+        if (position >= 0)
+            model.remove (position);
+    }
+
+    protected abstract int get_item_index (T item);
+
+    protected abstract bool is_deletable (T item);
+    protected abstract bool is_editable (T item);
+    protected abstract string get_item_text (T item);
+
+    public signal void item_edited(T item);
+    public signal void item_deleted(T item);
+
+    protected Gtk.Widget make_item_row (T item)
+    {
+        var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
+
+        var label = new Gtk.Label (get_item_text (item));
+        label.set_margin_start (6);
+        label.set_use_markup (true);
+        label.halign = Gtk.Align.START;
+        hbox.pack_start (label, true, true, 0);
+
+        if (is_editable (item))
+        {
+            var button = new Gtk.Button.from_icon_name ("document-edit-symbolic");
+            button.get_style_context ().add_class ("flat");
+            button.set_data<Object> ("object", item as Object);
+            button.clicked.connect (save_function_cb);
+            hbox.pack_start (button, false, true, 0);
+        }
+        if (is_deletable (item))
+        {
+            var button = new Gtk.Button.from_icon_name ("list-remove-symbolic");
+            button.get_style_context ().add_class ("flat");
+            button.set_data<Object> ("object", item as Object);
+            button.clicked.connect (delete_function_cb);
+            hbox.pack_start (button, false, true, 0);
+        }
+        hbox.show_all ();
+        return hbox;
+    }
+
+    private void save_function_cb (Gtk.Widget widget)
+    {
+        item_edited((T)widget.get_data<Object> ("object"));
+    }
+
+    private void delete_function_cb (Gtk.Widget widget)
+    {
+        item_deleted((T)widget.get_data<Object> ("object"));
+    }
+}
diff --git a/src/math-variable-popover.vala b/src/math-variable-popover.vala
index a6c67981..2318934d 100644
--- a/src/math-variable-popover.vala
+++ b/src/math-variable-popover.vala
@@ -8,13 +8,33 @@
  * license.
  */
 
+public class MathVariable : Object
+{
+    public string name;
+    public Number? value;
+
+    public MathVariable (string name, Number? value)
+    {
+        this.name = name;
+        this.value = value;
+    }
+
+    public static int name_compare_func (MathVariable var1, MathVariable var2)
+    {
+        return strcmp (var1.name, var2.name);
+    }
+
+    public static bool name_equal_func (MathVariable var1, MathVariable var2)
+    {
+        return var1.name == var2.name;
+    }
+}
+
 [GtkTemplate (ui = "/org/gnome/calculator/math-variable-popover.ui")]
-public class MathVariablePopover : Gtk.Popover
+public class MathVariablePopover : MathPopover<MathVariable>
 {
     private static string[] RESERVED_VARIABLE_NAMES = {"_", "rand"};
 
-    private MathEquation equation;
-
     [GtkChild]
     private unowned Gtk.ListBox variable_list;
     [GtkChild]
@@ -22,76 +42,42 @@ public class MathVariablePopover : Gtk.Popover
     [GtkChild]
     private unowned Gtk.Button store_variable_button;
 
-    public MathVariablePopover (MathEquation equation)
+    public MathVariablePopover (MathEquation equation, ListStore model, CompareDataFunc compare_func)
     {
-        this.equation = equation;
-        equation.history_signal.connect (this.handler);
-
-        // Fill variable list
-        var names = equation.variables.get_names ();
-        for (var i = 0; names[i] != null; i++)
-        {
-            var value = equation.variables[names[i]];
-            variable_list.add (make_variable_row (names[i], value));
-        }
+        base(equation, model, (a,b) => MathVariable.name_compare_func(a as MathVariable,b as MathVariable));
 
-        variable_list.add (make_variable_row ("rand", null));
-
-        // Sort list
-        variable_list.set_sort_func (variable_list_sort);
-
-        // Listen for variable changes
-        equation.variables.variable_added.connect ((name, value) => {
-            variable_list.add (make_variable_row (name, value));
-        });
-        equation.variables.variable_edited.connect ((name, value) => {
-            variable_list.remove (find_row_for_variable (name));
-            variable_list.add (make_variable_row (name, value));
-        });
-        equation.variables.variable_deleted.connect ((name) => {
-            variable_list.remove (find_row_for_variable (name));
-        });
+        variable_list.bind_model (model, (variable) => make_item_row (variable as MathVariable));
+        equation.history_signal.connect (this.handler);
+        item_deleted.connect (delete_variable_cb);
     }
 
-    private void handler (string answer, Number number, int number_base, uint representation_base)
+    protected override int get_item_index (MathVariable item)
     {
-        var row = find_row_for_variable ("_");
-        if (row != null)
-            variable_list.remove (row);
-        variable_list.add (make_variable_row ("_", number));
+        uint position;
+        if (model.find_with_equal_func (item as Object, (a, b) => (MathVariable.name_equal_func(a as 
MathVariable, b as MathVariable)), out position))
+            return (int)position;
+        else
+            return -1;
     }
 
-    private Gtk.ListBoxRow? find_row_for_variable (string name)
+    private void handler (string answer, Number number, int number_base, uint representation_base)
     {
-        weak Gtk.ListBoxRow? row = null;
-        variable_list.foreach ((child) => {
-            if (name == child.get_data<string> ("variable_name"))
-                row = child as Gtk.ListBoxRow;
-        });
-        return row;
+        item_edited_cb (new MathVariable("_", number));
     }
 
     [GtkCallback]
     private void insert_variable_cb (Gtk.ListBoxRow row)
     {
-        var name = row.get_data<string> ("variable_name");
-        equation.insert (name);
+        var variable = model.get_item (row.get_index ()) as MathVariable;
+        equation.insert (variable.name);
     }
 
     [GtkCallback]
-    private bool variable_name_key_press_cb (Gtk.Widget widget, Gdk.EventKey event)
+    private void variable_name_changed_cb (Gtk.Editable editable)
     {
-        /* Can't have whitespace in names, so replace with underscores */
-        if (event.keyval == Gdk.Key.space || event.keyval == Gdk.Key.KP_Space)
-            event.keyval = Gdk.Key.underscore;
-
-        return false;
-    }
-
-    [GtkCallback]
-    private void variable_name_changed_cb ()
-    {
-        store_variable_button.sensitive = (variable_name_entry.get_text () != "");
+        var entry = editable as Gtk.Entry;
+        entry.text = entry.text.replace (" ", "_");
+        store_variable_button.sensitive = (entry.text != "");
     }
 
     [GtkCallback]
@@ -112,55 +98,32 @@ public class MathVariablePopover : Gtk.Popover
         variable_name_entry.set_text ("");
     }
 
-    private void delete_variable_cb (Gtk.Widget widget)
+    private void delete_variable_cb (MathVariable variable)
     {
-        var name = widget.get_data<string> ("variable_name");
-        equation.variables.delete (name);
+        equation.variables.delete (variable.name);
     }
 
-    private Gtk.ListBoxRow make_variable_row (string name, Number? value)
+    protected override bool is_deletable (MathVariable variable)
     {
-        var row = new Gtk.ListBoxRow ();
-        row.get_style_context ().add_class ("popover-row");
-        row.set_data<string> ("variable_name", name);
+        return !(variable.name in RESERVED_VARIABLE_NAMES);
+    }
 
-        var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
+    protected override bool is_editable (MathVariable variable)
+    {
+        return false;
+    }
 
+    protected override string get_item_text (MathVariable variable)
+    {
         string text;
-        if (value != null)
+        if (variable.value != null)
         {
-            var value_text = equation.serializer.to_string (value);
-            text = "<b>%s</b> = %s".printf (name, value_text);
+            var value_text = equation.serializer.to_string (variable.value);
+            text = "<b>%s</b> = %s".printf (variable.name, value_text);
         }
         else
-            text = "<b>%s</b>".printf (name);
-
-        var label = new Gtk.Label (text);
-        label.set_margin_start (6);
-        label.set_use_markup (true);
-        label.halign = Gtk.Align.START;
-        hbox.pack_start (label, true, true, 0);
-
-        if (!(name in RESERVED_VARIABLE_NAMES))
-        {
-            var button = new Gtk.Button.from_icon_name ("list-remove-symbolic");
-            button.get_style_context ().add_class ("flat");
-            button.set_data<string> ("variable_name", name);
-            button.clicked.connect (delete_variable_cb);
-            hbox.pack_start (button, false, true, 0);
-        }
-
-        row.add (hbox);
-        row.show_all ();
-
-        return row;
+            text = "<b>%s</b>".printf (variable.name);
+        return text;
     }
 
-    private int variable_list_sort (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2)
-    {
-        string name1 = row1.get_data<string> ("variable_name");
-        string name2 = row2.get_data<string> ("variable_name");
-
-        return strcmp (name1, name2);
-    }
 }
diff --git a/src/math-window.vala b/src/math-window.vala
index e0f7c444..28c8198c 100644
--- a/src/math-window.vala
+++ b/src/math-window.vala
@@ -16,7 +16,7 @@ public class MathWindow : Hdy.ApplicationWindow
     public MathEquation equation { get { return _equation; } }
 
     private MathDisplay _display;
-    public MathDisplay display { get { return _display; } }
+    public MathDisplay math_display { get { return _display; } }
     private MathButtons _buttons;
     public MathButtons buttons { get { return _buttons; } }
     private bool right_aligned;
@@ -31,6 +31,8 @@ public class MathWindow : Hdy.ApplicationWindow
     [GtkChild]
     private unowned MathConverter converter;
 
+    private Gtk.EventControllerKey event_controller;
+
     private const ActionEntry[] window_entries =
     {
         { "copy", copy_cb, null, null, null },
@@ -56,13 +58,17 @@ public class MathWindow : Hdy.ApplicationWindow
         converter.set_category (null);
         converter.set_conversion (equation.source_units, equation.target_units);
 
+        event_controller = new Gtk.EventControllerKey (this as Gtk.Widget);
+        // (this as Gtk.Widget).add_controller (event_controller);
+        event_controller.key_pressed.connect (key_press_cb);
+
         _display = new MathDisplay (equation);
         grid.attach (_display, 0, 1, 1, 1);
         _display.show ();
         _display.grabfocus ();
 
         _buttons = new MathButtons (equation);
-        grid.add(_buttons);
+        grid.attach_next_to(_buttons, _display, Gtk.PositionType.BOTTOM);
 
         remove_buttons = (_buttons.mode != ButtonMode.KEYBOARD) ? true : false;
 
@@ -125,7 +131,7 @@ public class MathWindow : Hdy.ApplicationWindow
             converter.show ();
         }
 
-        display.set_enable_osk (remove_buttons);
+        _display.set_enable_osk (remove_buttons);
     }
 
     public void critical_error (string title, string contents)
@@ -142,13 +148,11 @@ public class MathWindow : Hdy.ApplicationWindow
         destroy ();
     }
 
-    protected override bool key_press_event (Gdk.EventKey event)
+    protected bool key_press_cb (Gtk.EventControllerKey controller, uint keyval, uint keycode, 
Gdk.ModifierType state)
     {
-        var result = base.key_press_event (event);
-
-        if (buttons.mode == ButtonMode.PROGRAMMING && (event.state & Gdk.ModifierType.CONTROL_MASK) == 
Gdk.ModifierType.CONTROL_MASK)
+        if (buttons.mode == ButtonMode.PROGRAMMING && (state & Gdk.ModifierType.CONTROL_MASK) == 
Gdk.ModifierType.CONTROL_MASK)
         {
-            switch (event.keyval)
+            switch (keyval)
             {
             /* Binary */
             case Gdk.Key.b:
@@ -168,8 +172,7 @@ public class MathWindow : Hdy.ApplicationWindow
                 return true;
             }
         }
-
-        return result;
+        return false;
     }
 
     [GtkCallback]
@@ -216,7 +219,7 @@ public class MathWindow : Hdy.ApplicationWindow
         popover.hide ();
         menu_button.set_active (false);
 
-        display.grab_focus ();
+        _display.grab_focus ();
 
         var mode = ButtonMode.BASIC;
         var mode_str = parameter.get_string (null);
diff --git a/src/meson.build b/src/meson.build
index d4ee5ffc..5c8fa032 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -5,6 +5,7 @@ gnome_calculator_vala_sources = [
   'math-converter.vala',
   'math-display.vala',
   'math-preferences.vala',
+  'math-popover.vala',
   'math-function-popover.vala',
   'math-variable-popover.vala',
   'math-window.vala',
diff --git a/src/ui/math-function-popover.ui b/src/ui/math-function-popover.ui
index 3cc88b76..2e3bbf14 100644
--- a/src/ui/math-function-popover.ui
+++ b/src/ui/math-function-popover.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.16"/>
-  <template class="MathFunctionPopover" parent="GtkPopover">
+  <template class="MathFunctionPopover" parent="MathPopover">
     <property name="can_focus">False</property>
     <child>
       <object class="GtkBox" id="vbox">
@@ -32,9 +32,8 @@
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="placeholder_text" translatable="yes">New function</property>
-            <signal name="button_press_event" handler="function_name_mouse_click_cb" swapped="no"/>
-            <signal name="key_press_event" handler="function_name_key_press_cb" swapped="no"/>
-            <signal name="changed" handler="function_name_changed_cb" swapped="no"/>
+            <signal name="focus" handler="function_name_focus_cb" swapped="no"/>
+            <signal name="changed" handler="function_name_entry_changed_cb" swapped="no"/>
             <signal name="activate" handler="add_function_cb" swapped="no"/>
           </object>
         </child>
diff --git a/src/ui/math-variable-popover.ui b/src/ui/math-variable-popover.ui
index 7a4c5d1a..1d375d29 100644
--- a/src/ui/math-variable-popover.ui
+++ b/src/ui/math-variable-popover.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.16"/>
-  <template class="MathVariablePopover" parent="GtkPopover">
+  <template class="MathVariablePopover" parent="MathPopover">
     <property name="can_focus">False</property>
     <child>
       <object class="GtkBox" id="content">
@@ -39,7 +39,6 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
-                <signal name="key_press_event" handler="variable_name_key_press_cb" swapped="no"/>
                 <signal name="changed" handler="variable_name_changed_cb" swapped="no"/>
                 <signal name="activate" handler="store_variable_cb" swapped="no"/>
               </object>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]