diff --git a/data/gresource.xml b/data/gresource.xml index 353c87343..4d1403ca0 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -4,6 +4,6 @@ Application.css - LocationMarker.svg + LocationMarker.svg diff --git a/data/gschema.xml b/data/gschema.xml index 8f5148c29..469694029 100644 --- a/data/gschema.xml +++ b/data/gschema.xml @@ -6,10 +6,15 @@ Whether the window was maximized on last run Whether the window was maximized on last run - - (1000, 800) - Most recent window size - Most recent window size (width, height) + + 1024 + Most recent window width + Most recent window width + + + 750 + Most recent window height + Most recent window height 256 diff --git a/io.elementary.tasks.json b/io.elementary.tasks.json index 295dd9c9b..8096e4da8 100644 --- a/io.elementary.tasks.json +++ b/io.elementary.tasks.json @@ -95,87 +95,6 @@ } ] }, - { - "name": "clutter-gtk", - "cleanup": [ - "/share/gtk-doc" - ], - "sources": [ - { - "type": "archive", - "url": "https://download.gnome.org/sources/clutter-gtk/1.8/clutter-gtk-1.8.4.tar.xz", - "sha256": "521493ec038973c77edcb8bc5eac23eed41645117894aaee7300b2487cb42b06" - } - ], - "modules": [ - { - "name": "cogl", - "config-opts": [ - "--disable-cogl-gst", - "--disable-gtk-doc", - "--enable-xlib-egl-platform", - "--enable-wayland-egl-platform" - ], - "cleanup": [ - "/share/gtk-doc", - "/share/cogl/examples-data" - ], - "sources": [ - { - "type": "archive", - "url": "https://download.gnome.org/sources/cogl/1.22/cogl-1.22.8.tar.xz", - "sha256": "a805b2b019184710ff53d0496f9f0ce6dcca420c141a0f4f6fcc02131581d759" - } - ] - }, - { - "name": "clutter", - "config-opts": [ - "--disable-gtk-doc", - "--enable-egl-backend", - "--enable-wayland-backend", - "--enable-deprecated=no" - ], - "cleanup": [ - "/share/gtk-doc" - ], - "sources": [ - { - "type": "archive", - "url": "https://download.gnome.org/sources/clutter/1.26/clutter-1.26.4.tar.xz", - "sha256": "8b48fac159843f556d0a6be3dbfc6b083fc6d9c58a20a49a6b4919ab4263c4e6" - } - ] - }, - { - "name": "clutter-gst", - "config-opts": [ - "--disable-gtk-doc" - ], - "cleanup": [ - "/share/gtk-doc" - ], - "sources": [ - { - "type": "archive", - "url": "https://download.gnome.org/sources/clutter-gst/3.0/clutter-gst-3.0.27.tar.xz", - "sha256": "fe69bd6c659d24ab30da3f091eb91cd1970026d431179b0724f13791e8ad9f9d" - } - ] - } - ] - }, - { - "name": "champlain", - "buildsystem": "meson", - "sources": [ - { - "type": "archive", - "url": "https://download.gnome.org/sources/libchamplain/0.12/libchamplain-0.12.21.tar.xz", - "sha256": "a915cd172a0c52944c5579fcb4683f8a878c571bf5e928254b5dafefc727e5a7" - } - ] - }, { "name": "geocode-glib-2.0", "buildsystem": "meson", @@ -211,6 +130,27 @@ } ] }, + { + "name": "libshumate", + "buildsystem": "meson", + "config-opts": [ + "-Ddemos=false", + "-Dgtk_doc=false", + "-Dvapi=false" + ], + "sources": [ + { + "type": "archive", + "url": "https://download.gnome.org/sources/libshumate/1.2/libshumate-1.2.1.tar.xz", + "sha256": "1105ee077e2147f2a039cddfa616fa5cb9438883dd940427e11699dcd6549c11", + "x-checker-data": { + "type": "gnome", + "stable-only": false, + "name": "libshumate" + } + } + ] + }, { "name": "tasks", "buildsystem": "meson", diff --git a/meson.build b/meson.build index 4ee94be3b..b76277a5e 100644 --- a/meson.build +++ b/meson.build @@ -28,21 +28,17 @@ config_file = configure_file( libecal_dep = dependency('libecal-2.0') tasks_deps = [ - dependency('champlain-0.12'), - dependency('champlain-gtk-0.12'), - dependency('clutter-1.0'), - dependency('clutter-gtk-1.0'), dependency('glib-2.0'), dependency('gobject-2.0'), - dependency('granite', version: '>=6.2.0'), - dependency('gtk+-3.0'), + dependency('granite-7', version: '>=7.0.0'), + dependency('gtk4', version: '>=4.12'), libecal_dep, dependency('libedataserver-1.2'), dependency('libgeoclue-2.0'), - dependency('libhandy-1', version: '>=0.90.0'), + dependency('shumate-1.0'), dependency('libical-glib'), dependency('libportal'), - dependency('libportal-gtk3') + dependency('libportal-gtk4') ] if libecal_dep.version().version_compare('>=3.46.0') @@ -66,19 +62,20 @@ executable( 'src/Application.vala', 'src/Location.vala', 'src/MainWindow.vala', + 'src/TaskModel.vala', 'src/TodayTaskMonitor.vala', 'src/Util.vala', - 'src/Widgets/EditableLabel.vala', 'src/Widgets/EntryPopover/DateTime.vala', 'src/Widgets/EntryPopover/Generic.vala', 'src/Widgets/EntryPopover/Location.vala', + 'src/Widgets/EditableLabel.vala', + 'src/Widgets/ListSettingsPopover.vala', + 'src/Widgets/PopoverButton.vala', 'src/Widgets/ScheduledRow.vala', - 'src/Widgets/SourceRow.vala', 'src/Widgets/ScheduledTaskListBox.vala', + 'src/Widgets/SourceRow.vala', 'src/Widgets/TaskListGrid.vala', 'src/Widgets/TaskRow.vala', - 'src/Widgets/ListSettingsPopover.vala', - 'src/TaskModel.vala', dependencies: tasks_deps, install : true ) diff --git a/src/Application.vala b/src/Application.vala index 2a002daba..9a561683c 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -28,12 +28,7 @@ public class Tasks.Application : Gtk.Application { public static Tasks.TaskModel model; public static bool run_in_background = false; - public const Gtk.TargetEntry[] DRAG_AND_DROP_TASK_DATA = { - { "text/uri-list", Gtk.TargetFlags.SAME_APP | Gtk.TargetFlags.OTHER_WIDGET, 0 } // TODO: TEXT_URI - }; - private bool first_activation = true; - public Application () { Object ( application_id: "io.elementary.tasks", @@ -58,7 +53,7 @@ public class Tasks.Application : Gtk.Application { protected override void startup () { base.startup (); - Hdy.init (); + Granite.init (); unowned var granite_settings = Granite.Settings.get_default (); unowned var gtk_settings = Gtk.Settings.get_default (); @@ -69,11 +64,6 @@ public class Tasks.Application : Gtk.Application { gtk_settings.gtk_application_prefer_dark_theme = ((Granite.Settings) obj).prefers_color_scheme == DARK; }); - var css_provider = new Gtk.CssProvider (); - css_provider.load_from_resource ("io/elementary/tasks/Application.css"); - - Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - var quit_action = new SimpleAction ("quit", null); quit_action.activate.connect (() => { if (active_window != null) { @@ -105,15 +95,19 @@ public class Tasks.Application : Gtk.Application { var main_window = new MainWindow (this); add_window (main_window); - var rect = Gtk.Allocation (); - settings.get ("window-size", "(ii)", out rect.width, out rect.height); - main_window.set_allocation (rect); + /* + * This is very finicky. Bind size after present else set_titlebar gives us bad sizes + * Set maximize after height/width else window is min size on unmaximize + * Bind maximize as SET else get get bad sizes + */ + settings.bind ("window-height", main_window, "default-height", DEFAULT); + settings.bind ("window-width", main_window, "default-width", DEFAULT); if (settings.get_boolean ("window-maximized")) { main_window.maximize (); } - main_window.show_all (); + settings.bind ("window-maximized", main_window, "maximized", SET); } active_window.present (); @@ -153,7 +147,7 @@ public class Tasks.Application : Gtk.Application { if (providers == null) { providers = new Gee.HashMap (); } - var task_list = (E.SourceTaskList?) source.get_extension (E.SOURCE_EXTENSION_TASK_LIST); + unowned var task_list = (E.SourceTaskList?) source.get_extension (E.SOURCE_EXTENSION_TASK_LIST); // Ensure we get a valid CSS color, not including FF var color = task_list.dup_color ().slice (0, 7); if (!providers.has_key (color)) { @@ -162,21 +156,16 @@ public class Tasks.Application : Gtk.Application { @define-color accent_color %s; """.printf (color, color); - try { - var style_provider = new Gtk.CssProvider (); - style_provider.load_from_data (style, style.length); + var style_provider = new Gtk.CssProvider (); + style_provider.load_from_string (style); - providers[color] = style_provider; - } catch (Error e) { - critical ("Unable to set color: %s", e.message); - } + providers[color] = style_provider; } widget.get_style_context ().add_provider (providers[color], Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); } public static int main (string[] args) { - GtkClutter.init (ref args); var app = new Application (); int res = app.run (args); ICal.Object.free_global_objects (); diff --git a/src/MainWindow.vala b/src/MainWindow.vala index a54f36dd5..4c7edcc77 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -3,7 +3,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -public class Tasks.MainWindow : Hdy.ApplicationWindow { +public class Tasks.MainWindow : Gtk.ApplicationWindow { public const string ACTION_GROUP_PREFIX = "win"; public const string ACTION_PREFIX = ACTION_GROUP_PREFIX + "."; public const string ACTION_DELETE_SELECTED_LIST = "action-delete-selected-list"; @@ -16,12 +16,12 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { private static Gee.MultiMap action_accelerators = new Gee.HashMultiMap (); - private uint configure_id; private Gtk.ListBox listbox; private Gee.HashMap? source_rows; private Gee.Collection? collection_sources; private Gtk.Stack task_list_grid_stack; private Gtk.Box add_tasklist_buttonbox; + private Gtk.Popover add_tasklist_popover; public MainWindow (Gtk.Application application) { Object ( @@ -45,33 +45,29 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { ); } - var sidebar_header = new Hdy.HeaderBar () { - has_subtitle = false, - show_close_button = true + var sidebar_header = new Gtk.HeaderBar () { + title_widget = new Gtk.Label (null), + show_title_buttons = false }; - sidebar_header.get_style_context ().add_class ("default-decoration"); - sidebar_header.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + sidebar_header.add_css_class (Granite.STYLE_CLASS_DEFAULT_DECORATION); + sidebar_header.add_css_class (Granite.STYLE_CLASS_FLAT); + sidebar_header.pack_start (new Gtk.WindowControls (Gtk.PackType.START)); - var main_header = new Hdy.HeaderBar () { - has_subtitle = false, - show_close_button = true + var main_header = new Gtk.HeaderBar () { + title_widget = new Gtk.Label (null), + show_title_buttons = false }; - main_header.get_style_context ().add_class ("default-decoration"); - main_header.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); - - // Create a header group that automatically assigns the right decoration controls to the - // right headerbar automatically - var header_group = new Hdy.HeaderGroup (); - header_group.add_header_bar (sidebar_header); - header_group.add_header_bar (main_header); + main_header.add_css_class (Granite.STYLE_CLASS_DEFAULT_DECORATION); + main_header.add_css_class (Granite.STYLE_CLASS_FLAT); + main_header.pack_end (new Gtk.WindowControls (Gtk.PackType.END)); listbox = new Gtk.ListBox (); listbox.set_sort_func (sort_function); var scheduled_row = new Tasks.Widgets.ScheduledRow (); - listbox.add (scheduled_row); + listbox.append (scheduled_row); - var scrolledwindow = new Gtk.ScrolledWindow (null, null) { + var scrolledwindow = new Gtk.ScrolledWindow () { child = listbox, hexpand = true, vexpand = true, @@ -80,66 +76,72 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { add_tasklist_buttonbox = new Gtk.Box (VERTICAL, 3); - var online_accounts_button = new Gtk.ModelButton () { - text = _("Online Accounts Settings…") - }; + var online_accounts_button = new Widgets.PopoverButton (); + online_accounts_button.append (new Gtk.Label (_("Online Accounts Settings…"))); - var add_tasklist_box = new Gtk.Box (VERTICAL, 3) { - margin_top = 3, - margin_bottom = 3 - }; - add_tasklist_box.add (add_tasklist_buttonbox); - add_tasklist_box.add (new Gtk.Separator (HORIZONTAL)); - add_tasklist_box.add (online_accounts_button); - add_tasklist_box.show_all (); + var add_tasklist_box = new Gtk.Box (VERTICAL, 0); + add_tasklist_box.append (add_tasklist_buttonbox); + add_tasklist_box.append (new Gtk.Separator (HORIZONTAL)); + add_tasklist_box.append (online_accounts_button); - var add_tasklist_popover = new Gtk.Popover (null) { + add_tasklist_popover = new Gtk.Popover () { child = add_tasklist_box }; + add_tasklist_popover.add_css_class (Granite.STYLE_CLASS_MENU); var add_list_label = new Gtk.Label (_("Add Task List…")); var add_list_button_box = new Gtk.Box (HORIZONTAL, 0); - add_list_button_box.add (new Gtk.Image.from_icon_name ("list-add-symbolic", SMALL_TOOLBAR)); - add_list_button_box.add (add_list_label); + add_list_button_box.append (new Gtk.Image.from_icon_name ("list-add-symbolic")); + add_list_button_box.append (add_list_label); var add_tasklist_button = new Gtk.MenuButton () { - child = add_list_button_box, - popover = add_tasklist_popover + popover = add_tasklist_popover, + direction = UP, + child = add_list_button_box }; add_list_label.mnemonic_widget = add_tasklist_button; var actionbar = new Gtk.ActionBar (); - actionbar.add (add_tasklist_button); - actionbar.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + actionbar.add_css_class (Granite.STYLE_CLASS_FLAT); + actionbar.pack_start (add_tasklist_button); var sidebar = new Gtk.Box (VERTICAL, 0); - sidebar.get_style_context ().add_class (Gtk.STYLE_CLASS_SIDEBAR); - sidebar.add (sidebar_header); - sidebar.add (scrolledwindow); - sidebar.add (actionbar); + sidebar.add_css_class (Granite.STYLE_CLASS_SIDEBAR); + sidebar.append (sidebar_header); + sidebar.append (scrolledwindow); + sidebar.append (actionbar); task_list_grid_stack = new Gtk.Stack (); var main_box = new Gtk.Box (VERTICAL, 0); - main_box.get_style_context ().add_class (Gtk.STYLE_CLASS_BACKGROUND); - main_box.add (main_header); - main_box.add (task_list_grid_stack); - - var paned = new Gtk.Paned (HORIZONTAL); - paned.pack1 (sidebar, false, false); - paned.pack2 (main_box, true, false); + main_box.add_css_class (Granite.STYLE_CLASS_BACKGROUND); + main_box.append (main_header); + main_box.append (task_list_grid_stack); + + var paned = new Gtk.Paned (HORIZONTAL) { + start_child = sidebar, + end_child = main_box, + resize_start_child = false, + shrink_end_child = false, + shrink_start_child = false + }; child = paned; - delete_event.connect (() => { + // We need to hide the title area for the split headerbar + titlebar = new Gtk.Grid () { visible = false }; + + close_request.connect (() => { ((Application)application).request_background.begin (() => destroy ()); return Gdk.EVENT_STOP; }); online_accounts_button.clicked.connect (() => { + add_tasklist_popover.popdown (); + try { AppInfo.launch_default_for_uri ("settings://accounts/online", null); } catch (Error e) { @@ -189,9 +191,11 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { add_source (source); if (last_selected_list == "" && default_task_list == source) { + assert (source_rows[source] != null); listbox.select_row (source_rows[source]); } else if (last_selected_list == source.uid) { + assert (source_rows[source] != null); listbox.select_row (source_rows[source]); } } @@ -313,7 +317,7 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { }; unowned var trash_button = message_dialog.add_button (_("Delete Anyway"), Gtk.ResponseType.YES); - trash_button.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); + trash_button.add_css_class (Granite.STYLE_CLASS_DESTRUCTIVE_ACTION); message_dialog.response.connect ((response) => { if (response == Gtk.ResponseType.YES) { @@ -335,9 +339,8 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { }); message_dialog.present (); - } else { - Gdk.beep (); + Gdk.Display.get_default ().beep (); } } @@ -355,7 +358,6 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { } var header_label = new Granite.HeaderLabel (Util.get_esource_collection_display_name (row.source)) { - ellipsize = Pango.EllipsizeMode.MIDDLE, margin_start = 6 }; @@ -389,17 +391,18 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { } collection_sources.add (collection_source); - var source_button = new Gtk.ModelButton () { - text = Util.get_esource_collection_display_name (collection_source), + var source_button = new Widgets.PopoverButton () { sensitive = Application.model.is_add_task_list_supported (collection_source) }; + source_button.append (new Gtk.Label (Util.get_esource_collection_display_name (collection_source))); source_button.clicked.connect (() => { + add_tasklist_popover.popdown (); + add_new_list (collection_source); }); - add_tasklist_buttonbox.add (source_button); - add_tasklist_buttonbox.show_all (); + add_tasklist_buttonbox.append (source_button); } private void add_source (E.Source source) { @@ -411,11 +414,10 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { if (!source_rows.has_key (source)) { source_rows[source] = new Tasks.Widgets.SourceRow (source); - listbox.add (source_rows[source]); + listbox.append (source_rows[source]); Idle.add (() => { listbox.invalidate_sort (); listbox.invalidate_headers (); - listbox.show_all (); return Source.REMOVE; }); @@ -460,29 +462,4 @@ public class Tasks.MainWindow : Hdy.ApplicationWindow { return Source.REMOVE; }); } - - public override bool configure_event (Gdk.EventConfigure event) { - if (configure_id != 0) { - GLib.Source.remove (configure_id); - } - - configure_id = Timeout.add (100, () => { - configure_id = 0; - - if (is_maximized) { - Tasks.Application.settings.set_boolean ("window-maximized", true); - } else { - Tasks.Application.settings.set_boolean ("window-maximized", false); - - Gdk.Rectangle rect; - get_allocation (out rect); - Tasks.Application.settings.set ("window-size", "(ii)", rect.width, rect.height); - - } - - return false; - }); - - return base.configure_event (event); - } } diff --git a/src/TaskModel.vala b/src/TaskModel.vala index f7aa339c3..d3a1477b0 100644 --- a/src/TaskModel.vala +++ b/src/TaskModel.vala @@ -36,7 +36,7 @@ public class Tasks.TaskModel : Object { public delegate void TasksRemovedFunc (SList cids); private Gee.Future registry; - private NetworkMonitor network_monitor; + // private NetworkMonitor network_monitor; private HashTable task_list_client; private HashTable> task_list_client_views; @@ -133,14 +133,15 @@ public class Tasks.TaskModel : Object { construct { task_list_client = new HashTable (str_hash, str_equal); task_list_client_views = new HashTable> (direct_hash, direct_equal); // vala-lint=line-length - network_monitor = NetworkMonitor.get_default (); + // Failed to initialize portal (GNetworkMonitorPortal) for gio-network-monitor: Not using portals + // network_monitor = NetworkMonitor.get_default (); } public async void start () { var promise = new Gee.Promise (); registry = promise.future; yield init_registry (promise); - network_monitor.network_changed.connect (network_changed); + // network_monitor.network_changed.connect (network_changed); } private async void init_registry (Gee.Promise promise) { @@ -239,15 +240,18 @@ public class Tasks.TaskModel : Object { } private async bool refresh_collection (E.Source collection_source, Cancellable? cancellable = null) throws Error { - if (network_monitor.network_available && registry.ready) { + // if (network_monitor.network_available && registry.ready) { + if (registry.ready) { debug ("Scheduling collection refresh '%s'…", collection_source.dup_display_name ()); return yield registry.value.refresh_backend (collection_source.dup_uid (), cancellable); } + return false; } public async bool refresh_task_list (E.Source task_list, Cancellable? cancellable = null) throws Error { - if (network_monitor.network_available && task_list_client.contains (task_list.dup_uid ())) { + // if (network_monitor.network_available && task_list_client.contains (task_list.dup_uid ())) { + if (task_list_client.contains (task_list.dup_uid ())) { var client = task_list_client.get (task_list.dup_uid ()); if (client.check_refresh_supported ()) { diff --git a/src/Widgets/EditableLabel.vala b/src/Widgets/EditableLabel.vala index 42cecb812..2af96b857 100644 --- a/src/Widgets/EditableLabel.vala +++ b/src/Widgets/EditableLabel.vala @@ -4,16 +4,13 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ -public class Tasks.Widgets.EditableLabel : Gtk.EventBox { +public class Tasks.Widgets.EditableLabel : Gtk.Widget { public signal void changed (); private Gtk.Label title; private Gtk.Entry entry; private Gtk.Stack stack; - private Gtk.EventControllerMotion motion_controller; - private Gtk.GestureMultiPress click_gesture; - public string text { get; set; default = ""; } public bool editing { @@ -35,6 +32,7 @@ public class Tasks.Widgets.EditableLabel : Gtk.EventBox { } class construct { + set_layout_manager_type (typeof (Gtk.BinLayout)); set_css_name ("editable-label"); } @@ -54,29 +52,27 @@ public class Tasks.Widgets.EditableLabel : Gtk.EventBox { hhomogeneous = false, transition_type = CROSSFADE }; - stack.add (title); - stack.add (entry); - - add (stack); + stack.add_child (title); + stack.add_child (entry); + stack.set_parent (this); bind_property ("text", title, "label"); - motion_controller = new Gtk.EventControllerMotion (this) { + var motion_controller = new Gtk.EventControllerMotion () { propagation_phase = CAPTURE }; - click_gesture = new Gtk.GestureMultiPress (this); + var click_gesture = new Gtk.GestureClick (); + + add_controller (click_gesture); + add_controller (motion_controller); motion_controller.enter.connect (() => { - get_window ().set_cursor ( - new Gdk.Cursor.from_name (Gdk.Display.get_default (), "text") - ); + set_cursor (new Gdk.Cursor.from_name ("text", null)); }); motion_controller.leave.connect (() => { - get_window ().set_cursor ( - new Gdk.Cursor.from_name (Gdk.Display.get_default (), "default") - ); + set_cursor (new Gdk.Cursor.from_name ("default", null)); }); click_gesture.released.connect (() => { @@ -89,16 +85,23 @@ public class Tasks.Widgets.EditableLabel : Gtk.EventBox { } }); - entry.focus_out_event.connect ((event) => { + var focus_controller = new Gtk.EventControllerFocus (); + entry.add_controller (focus_controller); + + focus_controller.leave.connect (() => { if (stack.visible_child == entry) { editing = false; } - - return Gdk.EVENT_PROPAGATE; }); } - public override void grab_focus () { + ~EditableLabel () { + get_first_child ().unparent (); + } + + public override bool grab_focus () { editing = true; + + return Gdk.EVENT_STOP; } } diff --git a/src/Widgets/EntryPopover/DateTime.vala b/src/Widgets/EntryPopover/DateTime.vala index e74fb968c..d57c1638c 100644 --- a/src/Widgets/EntryPopover/DateTime.vala +++ b/src/Widgets/EntryPopover/DateTime.vala @@ -5,7 +5,7 @@ public class Tasks.Widgets.EntryPopover.DateTime : Generic { private Gtk.Calendar calendar; - private Granite.Widgets.TimePicker timepicker; + private Granite.TimePicker timepicker; private Gtk.Revealer timepicker_revealer; public DateTime () { @@ -22,44 +22,40 @@ public class Tasks.Widgets.EntryPopover.DateTime : Generic { margin_start = 6, margin_end = 6 }; - calendar.get_style_context ().add_class (Gtk.STYLE_CLASS_BACKGROUND); - timepicker = new Granite.Widgets.TimePicker () { - margin_top = 0, + timepicker = new Granite.TimePicker () { + margin_top = 12, margin_bottom = 12, margin_start = 12, margin_end = 12 }; timepicker_revealer = new Gtk.Revealer () { - reveal_child = true + reveal_child = true, + child = timepicker }; - timepicker_revealer.add (timepicker); - var today_separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL) { margin_bottom = 3, margin_top = 3 }; - var today_button = new Gtk.ModelButton () { - text = _("Today") - }; + var today_box = new Widgets.PopoverButton (); + today_box.append (new Gtk.Label (_("Today"))); var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) { margin_top = 3 }; - box.add (today_button); - box.add (today_separator); - box.add (calendar); - box.add (timepicker_revealer); - box.show_all (); + box.append (today_box); + box.append (today_separator); + box.append (calendar); + box.append (timepicker_revealer); - popover.add (box); + popover.child = box; popover.show.connect (on_popover_show); - today_button.button_release_event.connect (on_today_button_release_event); + today_box.clicked.connect (on_today_button_clicked); calendar.day_selected.connect (on_calendar_day_selected); timepicker.time_changed.connect (on_timepicker_time_changed); } @@ -77,18 +73,12 @@ public class Tasks.Widgets.EntryPopover.DateTime : Generic { value = selected_datetime; } - calendar.select_month (selected_datetime.get_month () - 1, selected_datetime.get_year ()); - calendar.select_day (selected_datetime.get_day_of_month ()); + calendar.select_day (selected_datetime); timepicker.time = selected_datetime; } - private bool on_today_button_release_event () { - var now_local = new GLib.DateTime.now_local (); - - calendar.select_month (now_local.get_month () - 1, now_local.get_year ()); - calendar.select_day (now_local.get_day_of_month ()); - - return Gdk.EVENT_STOP; + private void on_today_button_clicked () { + calendar.select_day (new GLib.DateTime.now_local ()); } private void on_calendar_day_selected () { diff --git a/src/Widgets/EntryPopover/Generic.vala b/src/Widgets/EntryPopover/Generic.vala index 9232417b3..7ba4097a4 100644 --- a/src/Widgets/EntryPopover/Generic.vala +++ b/src/Widgets/EntryPopover/Generic.vala @@ -3,7 +3,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -public abstract class Tasks.Widgets.EntryPopover.Generic : Gtk.Box { +public abstract class Tasks.Widgets.EntryPopover.Generic : Gtk.Widget { public signal string? value_format (T value); public signal void value_changed (T value); @@ -14,8 +14,6 @@ public abstract class Tasks.Widgets.EntryPopover.Generic : Gtk.Box { private T value_on_popover_show; - private Gtk.EventControllerMotion motion_controller; - protected Generic (string placeholder, string? icon_name = null) { Object ( icon_name: icon_name, @@ -25,31 +23,34 @@ public abstract class Tasks.Widgets.EntryPopover.Generic : Gtk.Box { class construct { set_css_name ("entry-popover"); + set_layout_manager_type (typeof (Gtk.BinLayout)); } construct { - popover = new Gtk.Popover (null); + popover = new Gtk.Popover () { + autohide = true + }; var label = new Gtk.Label (placeholder); var popover_button_box = new Gtk.Box (HORIZONTAL, 0); if (icon_name != null) { - popover_button_box.add (new Gtk.Image.from_icon_name (icon_name, BUTTON)); + popover_button_box.append (new Gtk.Image.from_icon_name (icon_name)); } - popover_button_box.add (label); + popover_button_box.append (label); var popover_button = new Gtk.MenuButton () { child = popover_button_box, + has_frame = false, popover = popover }; - popover_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); label.mnemonic_widget = popover_button; - var delete_button = new Gtk.Button.from_icon_name ("process-stop-symbolic", BUTTON) { + var delete_button = new Gtk.Button.from_icon_name ("process-stop-symbolic") { tooltip_text = _("Remove") }; - delete_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + delete_button.add_css_class ("delete-button"); var delete_button_revealer = new Gtk.Revealer () { child = delete_button, @@ -58,10 +59,9 @@ public abstract class Tasks.Widgets.EntryPopover.Generic : Gtk.Box { }; var button_box = new Gtk.Box (HORIZONTAL, 0); - button_box.add (popover_button); - button_box.add (delete_button_revealer); - - add (button_box); + button_box.append (popover_button); + button_box.append (delete_button_revealer); + button_box.set_parent (this); delete_button.clicked.connect (() => { var value_has_changed = value != null; @@ -71,7 +71,7 @@ public abstract class Tasks.Widgets.EntryPopover.Generic : Gtk.Box { } }); - popover_button.clicked.connect (() => { + popover_button.activate.connect (() => { if (delete_button_revealer.reveal_child) { delete_button_revealer.reveal_child = false; } @@ -91,9 +91,8 @@ public abstract class Tasks.Widgets.EntryPopover.Generic : Gtk.Box { } }); - motion_controller = new Gtk.EventControllerMotion (this) { - propagation_phase = CAPTURE - }; + var motion_controller = new Gtk.EventControllerMotion (); + add_controller (motion_controller); motion_controller.enter.connect (() => { if (value_format (value) != null) { @@ -123,4 +122,8 @@ public abstract class Tasks.Widgets.EntryPopover.Generic : Gtk.Box { }); }); } + + ~Generic () { + get_last_child ().unparent (); + } } diff --git a/src/Widgets/EntryPopover/Location.vala b/src/Widgets/EntryPopover/Location.vala index bc2a2d210..a3d5ba8c1 100644 --- a/src/Widgets/EntryPopover/Location.vala +++ b/src/Widgets/EntryPopover/Location.vala @@ -1,5 +1,5 @@ /* -* Copyright 2021 elementary, Inc. (https://elementary.io) +* Copyright 2021-2023 elementary, Inc. (https://elementary.io) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public @@ -18,11 +18,11 @@ */ public class Tasks.Widgets.EntryPopover.Location : Generic { - private GtkChamplain.Embed map_embed; + // private GtkChamplain.Embed map_embed; private Gtk.SearchEntry search_entry; - private GLib.Cancellable search_cancellable; - private Granite.Widgets.ModeButton location_mode; - private Marker point; + // private GLib.Cancellable search_cancellable; + // private Granite.Widgets.ModeButton location_mode; + // private Marker point; public Location () { @@ -33,28 +33,28 @@ public class Tasks.Widgets.EntryPopover.Location : Generic { } construct { - map_embed = new GtkChamplain.Embed () { - height_request = 140, - width_request = 260 - }; + // map_embed = new GtkChamplain.Embed () { + // height_request = 140, + // width_request = 260 + // }; - point = new Marker (); + // point = new Marker (); - var marker_layer = new Champlain.MarkerLayer.full (Champlain.SelectionMode.SINGLE); - marker_layer.add_marker (point); + // var marker_layer = new Champlain.MarkerLayer.full (Champlain.SelectionMode.SINGLE); + // marker_layer.add_marker (point); - var map_view = map_embed.champlain_view; - map_view.zoom_level = 10; - map_view.goto_animation_duration = 500; - map_view.add_layer (marker_layer); - map_view.center_on (point.latitude, point.longitude); + // var map_view = map_embed.champlain_view; + // map_view.zoom_level = 10; + // map_view.goto_animation_duration = 500; + // map_view.add_layer (marker_layer); + // map_view.center_on (point.latitude, point.longitude); - var map_frame = new Gtk.Frame (null); - map_frame.add (map_embed); + // var map_frame = new Gtk.Frame (null); + // map_frame.add (map_embed); - location_mode = new Granite.Widgets.ModeButton (); - location_mode.append_text (_("Arriving")); - location_mode.append_text (_("Leaving")); + // location_mode = new Granite.Widgets.ModeButton (); + // location_mode.append_text (_("Arriving")); + // location_mode.append_text (_("Leaving")); search_entry = new Gtk.SearchEntry () { placeholder_text = _("John Smith OR Example St."), @@ -68,182 +68,181 @@ public class Tasks.Widgets.EntryPopover.Location : Generic { margin_end = 12 }; - box.add (search_entry); - box.add (location_mode); - box.add (map_frame); - box.show_all (); - - popover.add (box); - popover.show.connect (on_popover_show); + box.append (search_entry); + // box.append (location_mode); + // box.append (map_frame); - notify["value"].connect (on_value_changed); + popover.child = box; + // popover.show.connect (on_popover_show); - search_entry.activate.connect (on_search_entry_activate); - location_mode.mode_changed.connect (on_location_mode_changed); - } + // notify["value"].connect (on_value_changed); - private void on_popover_show () { - search_entry.text = (value == null ? "" : value.postal_address); - - if (search_entry.text != null && search_entry.text.strip ().length > 0) { - search_location.begin (search_entry.text); - } else { - // Use geoclue to find approximate location - discover_current_location.begin (); - } - } - - private void on_value_changed () { - if (value == null) { - return; - } - - var value_has_postal_address = value.postal_address != null && value.postal_address.strip ().length > 0; - if (value_has_postal_address && search_entry.text != value.postal_address) { - search_entry.text = value.postal_address; - } - - switch (value.proximity) { - case Tasks.LocationProximity.ARRIVE: - if (location_mode.selected != 0) { - location_mode.selected = 0; - } - break; - - default: - if (location_mode.selected != 1) { - location_mode.selected = 1; - } - break; - } - - bool need_relocation = true; - if (value.latitude >= Champlain.MIN_LATITUDE && value.longitude >= Champlain.MIN_LONGITUDE && - value.latitude <= Champlain.MAX_LATITUDE && value.longitude <= Champlain.MAX_LONGITUDE) { - - point.latitude = value.latitude; - point.longitude = value.longitude; - - need_relocation = (value.latitude == 0 && value.longitude == 0); - } - - if (need_relocation == true) { - if (value_has_postal_address) { - search_location.begin (value.postal_address); - } else { - // Use geoclue to find approximate location - discover_current_location.begin (); - } - } - } - - private void on_search_entry_activate () { - value = Tasks.Location () { - postal_address = search_entry.text, - display_name = search_entry.text, - longitude = 0, - latitude = 0, - accuracy = (value == null ? Geocode.LocationAccuracy.UNKNOWN : value.accuracy), - proximity = (value == null ? Tasks.LocationProximity.DEPART : value.proximity) - }; + // search_entry.activate.connect (on_search_entry_activate); + // location_mode.mode_changed.connect (on_location_mode_changed); } - private void on_location_mode_changed () { - var proximity = (value == null ? Tasks.LocationProximity.DEPART : value.proximity); - - switch (location_mode.selected) { - case 0: proximity = Tasks.LocationProximity.ARRIVE; break; - case 1: proximity = Tasks.LocationProximity.DEPART; break; - default: break; - } - - value = Tasks.Location () { - postal_address = (value == null ? search_entry.text : value.postal_address), - display_name = (value == null ? search_entry.text : value.display_name), - longitude = (value == null ? 0 : value.longitude), - latitude = (value == null ? 0 : value.latitude), - accuracy = (value == null ? Geocode.LocationAccuracy.UNKNOWN : value.accuracy), - proximity = proximity - }; - } - - private async void search_location (string location) { - if (search_cancellable != null) { - search_cancellable.cancel (); - } - search_cancellable = new GLib.Cancellable (); - - var forward = new Geocode.Forward.for_string (location); - try { - forward.set_answer_count (1); - var places = yield forward.search_async (search_cancellable); - foreach (var place in places) { - point.latitude = place.location.latitude; - point.longitude = place.location.longitude; - - if (value != null) { - value.latitude = place.location.latitude; - value.longitude = place.location.longitude; - } - - Idle.add (() => { - if (search_cancellable.is_cancelled () == false) { - map_embed.champlain_view.go_to (point.latitude, point.longitude); - } - return GLib.Source.REMOVE; - }); - } - - search_entry.has_focus = true; - } catch (Error error) { - debug (error.message); - } - } - - private async void discover_current_location () { - if (search_cancellable != null) { - search_cancellable.cancel (); - } - search_cancellable = new GLib.Cancellable (); - - try { - var simple = yield new GClue.Simple ("io.elementary.tasks", GClue.AccuracyLevel.CITY, null); - - point.latitude = simple.location.latitude; - point.longitude = simple.location.longitude; - - Idle.add (() => { - if (search_cancellable.is_cancelled () == false) { - map_embed.champlain_view.go_to (point.latitude, point.longitude); - } - return GLib.Source.REMOVE; - }); - - } catch (Error e) { - warning ("Failed to connect to GeoClue2 service: %s", e.message); - // Fallback to timezone location - search_location.begin (ECal.util_get_system_timezone_location ()); - } - } - - private class Marker : Champlain.Marker { - public Marker () { - try { - weak Gtk.IconTheme icon_theme = Gtk.IconTheme.get_default (); - var pixbuf = icon_theme.load_icon ("location-marker", 32, Gtk.IconLookupFlags.GENERIC_FALLBACK); - Clutter.Image image = new Clutter.Image (); - image.set_data (pixbuf.get_pixels (), - pixbuf.has_alpha ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888, - pixbuf.width, - pixbuf.height, - pixbuf.rowstride); - content = image; - set_size (pixbuf.width, pixbuf.height); - translation_x = -pixbuf.width / 2; - translation_y = -pixbuf.height; - } catch (Error e) { - critical (e.message); - } - } - } + // private void on_popover_show () { + // search_entry.text = (value == null ? "" : value.postal_address); + + // if (search_entry.text != null && search_entry.text.strip ().length > 0) { + // search_location.begin (search_entry.text); + // } else { + // // Use geoclue to find approximate location + // discover_current_location.begin (); + // } + // } + + // private void on_value_changed () { + // if (value == null) { + // return; + // } + + // var value_has_postal_address = value.postal_address != null && value.postal_address.strip ().length > 0; + // if (value_has_postal_address && search_entry.text != value.postal_address) { + // search_entry.text = value.postal_address; + // } + + // switch (value.proximity) { + // case Tasks.LocationProximity.ARRIVE: + // if (location_mode.selected != 0) { + // location_mode.selected = 0; + // } + // break; + + // default: + // if (location_mode.selected != 1) { + // location_mode.selected = 1; + // } + // break; + // } + + // bool need_relocation = true; + // if (value.latitude >= Champlain.MIN_LATITUDE && value.longitude >= Champlain.MIN_LONGITUDE && + // value.latitude <= Champlain.MAX_LATITUDE && value.longitude <= Champlain.MAX_LONGITUDE) { + + // point.latitude = value.latitude; + // point.longitude = value.longitude; + + // need_relocation = (value.latitude == 0 && value.longitude == 0); + // } + + // if (need_relocation == true) { + // if (value_has_postal_address) { + // search_location.begin (value.postal_address); + // } else { + // // Use geoclue to find approximate location + // discover_current_location.begin (); + // } + // } + // } + + // private void on_search_entry_activate () { + // value = Tasks.Location () { + // postal_address = search_entry.text, + // display_name = search_entry.text, + // longitude = 0, + // latitude = 0, + // accuracy = (value == null ? Geocode.LocationAccuracy.UNKNOWN : value.accuracy), + // proximity = (value == null ? Tasks.LocationProximity.DEPART : value.proximity) + // }; + // } + + // private void on_location_mode_changed () { + // var proximity = (value == null ? Tasks.LocationProximity.DEPART : value.proximity); + + // switch (location_mode.selected) { + // case 0: proximity = Tasks.LocationProximity.ARRIVE; break; + // case 1: proximity = Tasks.LocationProximity.DEPART; break; + // default: break; + // } + + // value = Tasks.Location () { + // postal_address = (value == null ? search_entry.text : value.postal_address), + // display_name = (value == null ? search_entry.text : value.display_name), + // longitude = (value == null ? 0 : value.longitude), + // latitude = (value == null ? 0 : value.latitude), + // accuracy = (value == null ? Geocode.LocationAccuracy.UNKNOWN : value.accuracy), + // proximity = proximity + // }; + // } + + // private async void search_location (string location) { + // if (search_cancellable != null) { + // search_cancellable.cancel (); + // } + // search_cancellable = new GLib.Cancellable (); + + // var forward = new Geocode.Forward.for_string (location); + // try { + // forward.set_answer_count (1); + // var places = yield forward.search_async (search_cancellable); + // foreach (var place in places) { + // point.latitude = place.location.latitude; + // point.longitude = place.location.longitude; + + // if (value != null) { + // value.latitude = place.location.latitude; + // value.longitude = place.location.longitude; + // } + + // Idle.add (() => { + // if (search_cancellable.is_cancelled () == false) { + // map_embed.champlain_view.go_to (point.latitude, point.longitude); + // } + // return GLib.Source.REMOVE; + // }); + // } + + // search_entry.has_focus = true; + // } catch (Error error) { + // debug (error.message); + // } + // } + + // private async void discover_current_location () { + // if (search_cancellable != null) { + // search_cancellable.cancel (); + // } + // search_cancellable = new GLib.Cancellable (); + + // try { + // var simple = yield new GClue.Simple ("io.elementary.tasks", GClue.AccuracyLevel.CITY, null); + + // point.latitude = simple.location.latitude; + // point.longitude = simple.location.longitude; + + // Idle.add (() => { + // if (search_cancellable.is_cancelled () == false) { + // map_embed.champlain_view.go_to (point.latitude, point.longitude); + // } + // return GLib.Source.REMOVE; + // }); + + // } catch (Error e) { + // warning ("Failed to connect to GeoClue2 service: %s", e.message); + // // Fallback to timezone location + // search_location.begin (ECal.util_get_system_timezone_location ()); + // } + // } + + // private class Marker : Champlain.Marker { + // public Marker () { + // try { + // weak Gtk.IconTheme icon_theme = Gtk.IconTheme.get_default (); + // var pixbuf = icon_theme.load_icon ("location-marker", 32, Gtk.IconLookupFlags.GENERIC_FALLBACK); + // Clutter.Image image = new Clutter.Image (); + // image.set_data (pixbuf.get_pixels (), + // pixbuf.has_alpha ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888, + // pixbuf.width, + // pixbuf.height, + // pixbuf.rowstride); + // content = image; + // set_size (pixbuf.width, pixbuf.height); + // translation_x = -pixbuf.width / 2; + // translation_y = -pixbuf.height; + // } catch (Error e) { + // critical (e.message); + // } + // } + // } } diff --git a/src/Widgets/ListSettingsPopover.vala b/src/Widgets/ListSettingsPopover.vala index f7e62e894..5063e2532 100644 --- a/src/Widgets/ListSettingsPopover.vala +++ b/src/Widgets/ListSettingsPopover.vala @@ -4,79 +4,107 @@ */ public class Tasks.Widgets.ListSettingsPopover : Gtk.Popover { - public E.Source source { get; set; } - - private Gtk.RadioButton color_button_red; - private Gtk.RadioButton color_button_orange; - private Gtk.RadioButton color_button_yellow; - private Gtk.RadioButton color_button_green; - private Gtk.RadioButton color_button_mint; - private Gtk.RadioButton color_button_blue; - private Gtk.RadioButton color_button_purple; - private Gtk.RadioButton color_button_pink; - private Gtk.RadioButton color_button_brown; - private Gtk.RadioButton color_button_slate; - private Gtk.RadioButton color_button_none; + public E.Source source { get; construct set; } + + private Gtk.CheckButton color_button_red; + private Gtk.CheckButton color_button_orange; + private Gtk.CheckButton color_button_yellow; + private Gtk.CheckButton color_button_green; + private Gtk.CheckButton color_button_mint; + private Gtk.CheckButton color_button_blue; + private Gtk.CheckButton color_button_purple; + private Gtk.CheckButton color_button_pink; + private Gtk.CheckButton color_button_brown; + private Gtk.CheckButton color_button_slate; + private Gtk.CheckButton color_button_none; + + public ListSettingsPopover (E.Source source) { + Object (source: source); + } construct { - color_button_blue = new Gtk.RadioButton (null); - color_button_blue.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_blue.get_style_context ().add_class ("blue"); + autohide = true; - color_button_mint = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_mint.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_mint.get_style_context ().add_class ("mint"); + color_button_blue = new Gtk.CheckButton (); + color_button_blue.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_blue.add_css_class ("blue"); - color_button_green = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_green.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_green.get_style_context ().add_class ("green"); + color_button_mint = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_mint.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_mint.add_css_class ("mint"); - color_button_yellow = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_yellow.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_yellow.get_style_context ().add_class ("yellow"); + color_button_green = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_green.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_green.add_css_class ("green"); - color_button_orange = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_orange.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_orange.get_style_context ().add_class ("orange"); - color_button_red = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_red.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_red.get_style_context ().add_class ("red"); + color_button_yellow = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_yellow.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_yellow.add_css_class ("yellow"); - color_button_pink = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_pink.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_pink.get_style_context ().add_class ("pink"); + color_button_orange = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_orange.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_orange.add_css_class ("orange"); - color_button_purple = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_purple.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_purple.get_style_context ().add_class ("purple"); + color_button_red = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_red.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_red.add_css_class ("red"); - color_button_brown = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_brown.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_brown.get_style_context ().add_class ("brown"); + color_button_pink = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_pink.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_pink.add_css_class ("pink"); - color_button_slate = new Gtk.RadioButton.from_widget (color_button_blue); - color_button_slate.get_style_context ().add_class (Granite.STYLE_CLASS_COLOR_BUTTON); - color_button_slate.get_style_context ().add_class ("slate"); + color_button_purple = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_purple.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_purple.add_css_class ("purple"); - color_button_none = new Gtk.RadioButton.from_widget (color_button_blue); + color_button_brown = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_brown.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_brown.add_css_class ("brown"); + + color_button_slate = new Gtk.CheckButton () { + group = color_button_blue + }; + color_button_slate.add_css_class (Granite.STYLE_CLASS_COLOR_BUTTON); + color_button_slate.add_css_class ("slate"); + + // FIXME: this CheckButton is unused + color_button_none = new Gtk.CheckButton () { + group = color_button_blue + }; var color_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6) { margin_top = 12, margin_bottom = 12, margin_start = 12, - margin_end = 12 + margin_end = 12, }; - color_box.add (color_button_blue); - color_box.add (color_button_mint); - color_box.add (color_button_green); - color_box.add (color_button_yellow); - color_box.add (color_button_orange); - color_box.add (color_button_red); - color_box.add (color_button_pink); - color_box.add (color_button_purple); - color_box.add (color_button_brown); - color_box.add (color_button_slate); + color_box.append (color_button_blue); + color_box.append (color_button_mint); + color_box.append (color_button_green); + color_box.append (color_button_yellow); + color_box.append (color_button_orange); + color_box.append (color_button_red); + color_box.append (color_button_pink); + color_box.append (color_button_purple); + color_box.append (color_button_brown); + color_box.append (color_button_slate); var show_completed_button = new Granite.SwitchModelButton (_("Show Completed")) { margin_top = 3 @@ -87,23 +115,20 @@ public class Tasks.Widgets.ListSettingsPopover : Gtk.Popover { MainWindow.ACTION_PREFIX + MainWindow.ACTION_DELETE_SELECTED_LIST ); - var delete_list_menuitem = new Gtk.ModelButton (); - delete_list_menuitem.action_name = delete_list_accel_label.action_name; - delete_list_menuitem.get_child ().destroy (); - delete_list_menuitem.add (delete_list_accel_label); - delete_list_menuitem.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); + var delete_list_menuitem = new Widgets.PopoverButton (); + delete_list_menuitem.add_css_class (Granite.STYLE_CLASS_DESTRUCTIVE_ACTION); + delete_list_menuitem.append (delete_list_accel_label); var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) { margin_top = 3, margin_bottom = 3 }; - box.add (color_box); - box.add (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); - box.add (show_completed_button); - box.add (delete_list_menuitem); - box.show_all (); + box.append (color_box); + box.append (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); + box.append (show_completed_button); + box.append (delete_list_menuitem); - add (box); + child = box; color_button_red.toggled.connect (() => { if (color_button_red.active) { @@ -165,6 +190,13 @@ public class Tasks.Widgets.ListSettingsPopover : Gtk.Popover { } }); + delete_list_menuitem.clicked.connect (() => { + popdown (); + + unowned var main_window = (Gtk.ApplicationWindow) get_root (); + ((SimpleAction) main_window.lookup_action (MainWindow.ACTION_DELETE_SELECTED_LIST)).activate (null); + }); + notify["source"].connect (() => { select_task_list_color (get_task_list_color (source)); }); @@ -214,7 +246,7 @@ public class Tasks.Widgets.ListSettingsPopover : Gtk.Popover { private string get_task_list_color (E.Source source) { if (source.has_extension (E.SOURCE_EXTENSION_TASK_LIST)) { - var task_list = (E.SourceTaskList) source.get_extension (E.SOURCE_EXTENSION_TASK_LIST); + unowned var task_list = (E.SourceTaskList) source.get_extension (E.SOURCE_EXTENSION_TASK_LIST); return task_list.dup_color (); } return ""; @@ -244,7 +276,9 @@ public class Tasks.Widgets.ListSettingsPopover : Gtk.Popover { Gtk.ButtonsType.CLOSE ); error_dialog.show_error_details (e.message); - error_dialog.run (); - error_dialog.destroy (); + error_dialog.present (); + error_dialog.response.connect (() => { + error_dialog.destroy (); + }); } } diff --git a/src/Widgets/PopoverButton.vala b/src/Widgets/PopoverButton.vala new file mode 100644 index 000000000..379bbcef2 --- /dev/null +++ b/src/Widgets/PopoverButton.vala @@ -0,0 +1,33 @@ +/* +* Copyright 2023 elementary, Inc. (https://elementary.io) +* +* 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301 USA +* +*/ + +class Tasks.Widgets.PopoverButton : Gtk.Box { + public signal void clicked (); + + construct { + orientation = HORIZONTAL; + add_css_class (Granite.STYLE_CLASS_MENUITEM); + + var gesture_click = new Gtk.GestureClick (); + add_controller (gesture_click); + + gesture_click.released.connect (() => clicked ()); + } +} diff --git a/src/Widgets/ScheduledRow.vala b/src/Widgets/ScheduledRow.vala index c4059c707..645dc179f 100644 --- a/src/Widgets/ScheduledRow.vala +++ b/src/Widgets/ScheduledRow.vala @@ -6,7 +6,7 @@ public class Tasks.Widgets.ScheduledRow : Gtk.ListBoxRow { construct { - var icon = new Gtk.Image.from_icon_name ("appointment", Gtk.IconSize.MENU); + var icon = new Gtk.Image.from_icon_name ("appointment"); var display_name_label = new Gtk.Label (_("Scheduled")) { ellipsize = Pango.EllipsizeMode.MIDDLE, @@ -19,9 +19,9 @@ public class Tasks.Widgets.ScheduledRow : Gtk.ListBoxRow { margin_start = 12, margin_end = 6 }; - box.add (icon); - box.add (display_name_label); + box.append (icon); + box.append (display_name_label); - add (box); + child = box; } } diff --git a/src/Widgets/ScheduledTaskListBox.vala b/src/Widgets/ScheduledTaskListBox.vala index 634ed05f7..b2bd0d47e 100644 --- a/src/Widgets/ScheduledTaskListBox.vala +++ b/src/Widgets/ScheduledTaskListBox.vala @@ -33,10 +33,19 @@ public class Tasks.Widgets.ScheduledTaskListBox : Gtk.Box { } private void remove_view (E.Source source) { - foreach (unowned Gtk.Widget child in task_list.get_children ()) { + Gtk.Widget[] children_for_removal = {}; + unowned var child = get_first_child (); + while (child != null) { if (child is Tasks.Widgets.TaskRow && ((Tasks.Widgets.TaskRow) child).source == source) { - child.destroy (); + children_for_removal += child; } + + child = child.get_next_sibling (); + } + + for (int i = 0; i < children_for_removal.length; i++) { + remove (children_for_removal[i]); + children_for_removal[i].destroy (); } lock (views) { @@ -64,13 +73,13 @@ public class Tasks.Widgets.ScheduledTaskListBox : Gtk.Box { margin_bottom = 24, xalign = 0 }; - scheduled_title.get_style_context ().add_class (Granite.STYLE_CLASS_H1_LABEL); - scheduled_title.get_style_context ().add_class (Granite.STYLE_CLASS_ACCENT); + + scheduled_title.add_css_class (Granite.STYLE_CLASS_H1_LABEL); + scheduled_title.add_css_class (Granite.STYLE_CLASS_ACCENT); var placeholder = new Gtk.Label (_("No Tasks")); - placeholder.show (); - placeholder.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); - placeholder.get_style_context ().add_class (Granite.STYLE_CLASS_H2_LABEL); + placeholder.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); + placeholder.add_css_class (Granite.STYLE_CLASS_H2_LABEL); task_list = new Gtk.ListBox () { selection_mode = Gtk.SelectionMode.MULTIPLE, @@ -79,19 +88,18 @@ public class Tasks.Widgets.ScheduledTaskListBox : Gtk.Box { task_list.set_placeholder (placeholder); task_list.set_sort_func (sort_function); task_list.set_header_func (header_function); - task_list.get_style_context ().add_class (Gtk.STYLE_CLASS_BACKGROUND); + task_list.add_css_class (Granite.STYLE_CLASS_BACKGROUND); - var scrolled_window = new Gtk.ScrolledWindow (null, null) { + var scrolled_window = new Gtk.ScrolledWindow () { hexpand = true, vexpand = true, - hscrollbar_policy = Gtk.PolicyType.NEVER + hscrollbar_policy = Gtk.PolicyType.NEVER, + child = task_list }; - scrolled_window.add (task_list); orientation = Gtk.Orientation.VERTICAL; - add (scheduled_title); - add (scrolled_window); - show_all (); + append (scheduled_title); + append (scrolled_window); task_list.row_activated.connect (on_row_activated); @@ -139,9 +147,9 @@ public class Tasks.Widgets.ScheduledTaskListBox : Gtk.Box { var task_row = (Tasks.Widgets.TaskRow) row; task_row.reveal_child_request (true); - unowned GLib.ActionMap win_action_map = (GLib.ActionMap) get_action_group (MainWindow.ACTION_GROUP_PREFIX); - if (win_action_map != null) { - ((SimpleAction) win_action_map.lookup_action (MainWindow.ACTION_DELETE_SELECTED_LIST)).set_enabled (false); + unowned var main_window = (MainWindow) get_root (); + if (main_window != null) { + ((SimpleAction) main_window.lookup_action (MainWindow.ACTION_DELETE_SELECTED_LIST)).set_enabled (false); } } @@ -179,10 +187,11 @@ public class Tasks.Widgets.ScheduledTaskListBox : Gtk.Box { } } - var due_date_time = Util.ical_to_date_time_local (comp.get_due ()); - var header_label = new Granite.HeaderLabel (Tasks.Util.get_relative_date (due_date_time)); - header_label.ellipsize = Pango.EllipsizeMode.MIDDLE; - header_label.margin_start = 6; + var due_date_time = Tasks.Util.ical_to_date_time_local (comp.get_due ()); + var header_label = new Granite.HeaderLabel (Tasks.Util.get_relative_date (due_date_time)) { + margin_start = 6 + // ellipsize = Pango.EllipsizeMode.MIDDLE + }; row.set_header (header_label); } @@ -194,77 +203,70 @@ public class Tasks.Widgets.ScheduledTaskListBox : Gtk.Box { task_row.task_completed.connect ((task) => { Tasks.Application.model.complete_task.begin (source, task, (obj, res) => { - GLib.Idle.add (() => { - try { - Tasks.Application.model.complete_task.end (res); - } catch (Error e) { - var error_dialog = new Granite.MessageDialog ( - _("Completing task failed"), - _("The task registry may be unavailable or unable to be written to."), - new ThemedIcon ("dialog-error"), - Gtk.ButtonsType.CLOSE - ); - error_dialog.show_error_details (e.message); - error_dialog.run (); + try { + Tasks.Application.model.complete_task.end (res); + } catch (Error e) { + var error_dialog = new Granite.MessageDialog ( + _("Completing task failed"), + _("The task registry may be unavailable or unable to be written to."), + new ThemedIcon ("dialog-error"), + Gtk.ButtonsType.CLOSE + ); + error_dialog.show_error_details (e.message); + error_dialog.present (); + error_dialog.response.connect (() => { error_dialog.destroy (); - } - - return GLib.Source.REMOVE; - }); + }); + } }); }); task_row.task_changed.connect ((task) => { Tasks.Application.model.update_task.begin (source, task, ECal.ObjModType.THIS_AND_FUTURE, (obj, res) => { - GLib.Idle.add (() => { - try { - Tasks.Application.model.update_task.end (res); - } catch (Error e) { - var error_dialog = new Granite.MessageDialog ( - _("Updating task failed"), - _("The task registry may be unavailable or unable to be written to."), - new ThemedIcon ("dialog-error"), - Gtk.ButtonsType.CLOSE - ); - error_dialog.show_error_details (e.message); - error_dialog.run (); + try { + Tasks.Application.model.update_task.end (res); + } catch (Error e) { + var error_dialog = new Granite.MessageDialog ( + _("Updating task failed"), + _("The task registry may be unavailable or unable to be written to."), + new ThemedIcon ("dialog-error"), + Gtk.ButtonsType.CLOSE + ); + error_dialog.show_error_details (e.message); + error_dialog.present (); + error_dialog.response.connect (() => { error_dialog.destroy (); - } - - return GLib.Source.REMOVE; - }); + }); + } }); }); task_row.task_removed.connect ((task) => { Tasks.Application.model.remove_task.begin (source, task, ECal.ObjModType.ALL, (obj, res) => { - GLib.Idle.add (() => { - try { - Tasks.Application.model.remove_task.end (res); - } catch (Error e) { - var error_dialog = new Granite.MessageDialog ( - _("Removing task failed"), - _("The task registry may be unavailable or unable to be written to."), - new ThemedIcon ("dialog-error"), - Gtk.ButtonsType.CLOSE - ); - error_dialog.show_error_details (e.message); - error_dialog.run (); + try { + Tasks.Application.model.remove_task.end (res); + } catch (Error e) { + var error_dialog = new Granite.MessageDialog ( + _("Removing task failed"), + _("The task registry may be unavailable or unable to be written to."), + new ThemedIcon ("dialog-error"), + Gtk.ButtonsType.CLOSE + ); + error_dialog.show_error_details (e.message); + error_dialog.present (); + error_dialog.response.connect (() => { error_dialog.destroy (); - } - - return GLib.Source.REMOVE; - }); + }); + } }); }); - task_list.add (task_row); + task_list.append (task_row); return true; }); Idle.add (() => { task_list.invalidate_sort (); - task_list.show_all (); return Source.REMOVE; }); diff --git a/src/Widgets/SourceRow.vala b/src/Widgets/SourceRow.vala index 4887447a1..fe3f276c4 100644 --- a/src/Widgets/SourceRow.vala +++ b/src/Widgets/SourceRow.vala @@ -20,7 +20,7 @@ public class Tasks.Widgets.SourceRow : Gtk.ListBoxRow { source_color = new Gtk.Grid () { valign = Gtk.Align.CENTER }; - source_color.get_style_context ().add_class ("source-color"); + source_color.add_css_class ("source-color"); display_name_label = new Gtk.Label (source.display_name) { halign = Gtk.Align.START, @@ -33,7 +33,7 @@ public class Tasks.Widgets.SourceRow : Gtk.ListBoxRow { }; var spinner = new Gtk.Spinner () { - active = true, + spinning = true, tooltip_text = _("Connecting…") }; @@ -45,16 +45,16 @@ public class Tasks.Widgets.SourceRow : Gtk.ListBoxRow { margin_start = 12, margin_end = 6 }; - box.add (source_color); - box.add (display_name_label); - box.add (status_stack); + box.append (source_color); + box.append (display_name_label); + box.append (status_stack); revealer = new Gtk.Revealer () { - reveal_child = true + reveal_child = true, + child = box }; - revealer.add (box); - add (revealer); + child = revealer; build_drag_and_drop (); @@ -62,31 +62,36 @@ public class Tasks.Widgets.SourceRow : Gtk.ListBoxRow { } private void build_drag_and_drop () { - Gtk.drag_dest_set (this, Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.MOTION, Application.DRAG_AND_DROP_TASK_DATA, Gdk.DragAction.MOVE); + var drop_target = new Gtk.DropTarget (typeof (string), Gdk.DragAction.COPY); + add_controller (drop_target); + + drop_target.accept.connect (on_drop_accept); + drop_target.drop.connect (on_drag_drop); + drop_target.enter.connect (on_drag_enter); + drop_target.leave.connect (on_drag_leave); - drag_motion.connect (on_drag_motion); - drag_drop.connect (on_drag_drop); - drag_data_received.connect (on_drag_data_received); - drag_leave.connect (on_drag_leave); } - private bool on_drag_motion (Gdk.DragContext context, int x, int y, uint time) { - if (!get_style_context ().has_class ("drop-hover")) { - get_style_context ().add_class ("drop-hover"); - } + private bool on_drop_accept (Gdk.Drop drop) { return true; } - private void on_drag_leave (Gdk.DragContext context, uint time_) { - get_style_context ().remove_class ("drop-hover"); + private Gdk.DragAction on_drag_enter (double x, double y) { + if (!has_css_class ("drop-hover")) { + add_css_class ("drop-hover"); + } + + return Gdk.DragAction.COPY; + } + + private void on_drag_leave () { + remove_css_class ("drop-hover"); } private Gee.HashMultiMap received_drag_data; - private async bool on_drag_drop_move_tasks () throws Error { + private async void on_drag_drop_move_tasks () throws Error { E.SourceRegistry registry = yield Application.model.get_registry (); - var move_successful = true; - var source_uids = received_drag_data.get_keys (); foreach (var source_uid in source_uids) { var src_source = registry.ref_source (source_uid); @@ -94,28 +99,22 @@ public class Tasks.Widgets.SourceRow : Gtk.ListBoxRow { var component_uids = received_drag_data.get (source_uid); foreach (var component_uid in component_uids) { if (!yield Application.model.move_task (src_source, source, component_uid)) { - move_successful = false; + warning ("Couldn't move task %s to %s", component_uid, source.uid); } } } - return move_successful; } - private bool on_drag_drop (Gdk.DragContext context, int x, int y, uint time) { - var target = Gtk.drag_dest_find_target (this, context, null); - if (target != Gdk.Atom.NONE) { - Gtk.drag_get_data (this, context, target, time); - } + private bool on_drag_drop (GLib.Value value, double x, double y) { + parse_data ((string) value); var drop_successful = false; - var move_successful = false; if (received_drag_data != null && received_drag_data.size > 0) { drop_successful = true; on_drag_drop_move_tasks.begin ((obj, res) => { try { - move_successful = on_drag_drop_move_tasks.end (res); - + on_drag_drop_move_tasks.end (res); } catch (Error e) { var error_dialog = new Granite.MessageDialog ( _("Moving task failed"), @@ -124,11 +123,10 @@ public class Tasks.Widgets.SourceRow : Gtk.ListBoxRow { Gtk.ButtonsType.CLOSE ); error_dialog.show_error_details (e.message); - error_dialog.run (); - error_dialog.destroy (); - - } finally { - Gtk.drag_finish (context, drop_successful, move_successful, time); + error_dialog.present (); + error_dialog.response.connect (() => { + error_dialog.destroy (); + }); } }); } @@ -136,34 +134,31 @@ public class Tasks.Widgets.SourceRow : Gtk.ListBoxRow { return drop_successful; } - private void on_drag_data_received (Gdk.DragContext context, int x, int y, Gtk.SelectionData selection_data, uint info, uint time) { + private void parse_data (string uri) { received_drag_data = new Gee.HashMultiMap (); var uri_scheme = "task://"; - var uris = selection_data.get_uris (); - foreach (var uri in uris) { - string? source_uid = null; - string? component_uid = null; + string? source_uid = null; + string? component_uid = null; - if (uri.has_prefix (uri_scheme)) { - var uri_parts = uri.substring (uri_scheme.length).split ("/"); + if (uri.has_prefix (uri_scheme)) { + var uri_parts = uri.substring (uri_scheme.length).split ("/"); - if (uri_parts.length == 2) { - source_uid = uri_parts[0]; - component_uid = uri_parts[1]; - } + if (uri_parts.length == 2) { + source_uid = uri_parts[0]; + component_uid = uri_parts[1]; } + } - if (source_uid == null || component_uid == null) { - warning ("Can't handle drop data: Unexpected uri format: %s", uri); + if (source_uid == null || component_uid == null) { + warning ("Can't handle drop data: Unexpected uri format: %s", uri); - } else if (source_uid == source.uid) { - debug ("Dropped task onto the same list, so we have nothing to do."); + } else if (source_uid == source.uid) { + debug ("Dropped task onto the same list, so we have nothing to do."); - } else { - received_drag_data.set (source_uid, component_uid); - } + } else { + received_drag_data.set (source_uid, component_uid); } } @@ -201,7 +196,9 @@ public class Tasks.Widgets.SourceRow : Gtk.ListBoxRow { public void remove_request () { revealer.reveal_child = false; GLib.Timeout.add (revealer.transition_duration, () => { + ((Gtk.ListBox) parent).remove (this); destroy (); + return GLib.Source.REMOVE; }); } diff --git a/src/Widgets/TaskListGrid.vala b/src/Widgets/TaskListGrid.vala index 22ec3ed10..522d7a6ac 100644 --- a/src/Widgets/TaskListGrid.vala +++ b/src/Widgets/TaskListGrid.vala @@ -28,27 +28,27 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { editable_title = new EditableLabel () { margin_start = 24 }; - editable_title.get_style_context ().add_class (Granite.STYLE_CLASS_H1_LABEL); - editable_title.get_style_context ().add_class (Granite.STYLE_CLASS_ACCENT); + editable_title.add_css_class (Granite.STYLE_CLASS_H1_LABEL); + editable_title.add_css_class (Granite.STYLE_CLASS_ACCENT); - var list_settings_popover = new Tasks.Widgets.ListSettingsPopover (); + var list_settings_popover = new Tasks.Widgets.ListSettingsPopover (source); var settings_button = new Gtk.MenuButton () { halign = END, - image = new Gtk.Image.from_icon_name ("view-more-symbolic", Gtk.IconSize.MENU), + icon_name = "view-more-symbolic", margin_end = 24, popover = list_settings_popover, tooltip_text = _("Edit Name and Appearance"), valign = CENTER }; - settings_button.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); + settings_button.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); add_task_list = new Gtk.ListBox () { margin_top = 24, selection_mode = SINGLE, activate_on_single_click = true }; - add_task_list.get_style_context ().add_class (Gtk.STYLE_CLASS_BACKGROUND); + add_task_list.add_css_class (Granite.STYLE_CLASS_BACKGROUND); var add_task_row = new Tasks.Widgets.TaskRow.for_source (source); add_task_row.unselect.connect (on_row_unselect); @@ -74,12 +74,11 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { }); }); }); - add_task_list.add (add_task_row); + add_task_list.append (add_task_row); var placeholder = new Gtk.Label (_("No Tasks")); - placeholder.show (); - placeholder.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); - placeholder.get_style_context ().add_class (Granite.STYLE_CLASS_H2_LABEL); + placeholder.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); + placeholder.add_css_class (Granite.STYLE_CLASS_H2_LABEL); task_list = new Gtk.ListBox () { selection_mode = MULTIPLE, @@ -87,9 +86,9 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { }; task_list.set_placeholder (placeholder); task_list.set_sort_func (sort_function); - task_list.get_style_context ().add_class (Gtk.STYLE_CLASS_BACKGROUND); + task_list.add_css_class (Granite.STYLE_CLASS_BACKGROUND); - var scrolled_window = new Gtk.ScrolledWindow (null, null) { + var scrolled_window = new Gtk.ScrolledWindow () { child = task_list, hexpand = true, vexpand = true, @@ -117,7 +116,7 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { } else { /* * We can't immediate disable the action once the popover is closed, - * because this would lead to the action not beeing executed in case + * because this would lead to the action not being executed in case * the popover was closed because the user clicked on the action. * Therefore we wait a tiny bit using GLib.Idle to allow the action to * be executed if needed. @@ -142,29 +141,25 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { editable_title.changed.connect (() => { Application.model.update_task_list_display_name.begin (source, editable_title.text, (obj, res) => { - GLib.Idle.add (() => { - try { - Application.model.update_task_list_display_name.end (res); - } catch (Error e) { - editable_title.text = source.display_name; - - var error_dialog = new Granite.MessageDialog ( - _("Renaming task list failed"), - _("The task list registry may be unavailable or unable to be written to."), - new ThemedIcon ("dialog-error"), - Gtk.ButtonsType.CLOSE - ); - error_dialog.show_error_details (e.message); - error_dialog.run (); + try { + Application.model.update_task_list_display_name.end (res); + } catch (Error e) { + editable_title.text = source.display_name; + + var error_dialog = new Granite.MessageDialog ( + _("Renaming task list failed"), + _("The task list registry may be unavailable or unable to be written to."), + new ThemedIcon ("dialog-error"), + Gtk.ButtonsType.CLOSE + ); + error_dialog.show_error_details (e.message); + error_dialog.present (); + error_dialog.response.connect (() => { error_dialog.destroy (); - } - - return GLib.Source.REMOVE; - }); + }); + } }); }); - - show_all (); } private void on_show_completed_changed (bool show_completed) { @@ -176,9 +171,7 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { } private void set_view_for_query (string query) { - while (task_list.get_row_at_index (0) != null) { - task_list.remove (task_list.get_row_at_index (0)); - } + task_list.remove_all (); if (view != null) { Application.model.destroy_task_list_view (view); @@ -210,7 +203,7 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { } if (add_task_list.get_selected_rows ().length () == 0 && task_list.get_selected_rows ().length () == 0) { - unowned var main_window = (Gtk.ApplicationWindow) get_toplevel (); + unowned var main_window = (Gtk.ApplicationWindow) ((Gtk.Application) GLib.Application.get_default ()).active_window; ((SimpleAction) main_window.lookup_action (MainWindow.ACTION_DELETE_SELECTED_LIST)).set_enabled (true); } } @@ -220,12 +213,15 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { Tasks.Application.set_task_color (source, editable_title); - task_list.@foreach ((row) => { - if (row is Tasks.Widgets.TaskRow) { - var task_row = (row as Tasks.Widgets.TaskRow); + unowned var child = task_list.get_first_child (); + while (child != null) { + if (child is Tasks.Widgets.TaskRow) { + unowned var task_row = (child as Tasks.Widgets.TaskRow); task_row.update_request (); } - }); + + child = child.get_next_sibling (); + } } [CCode (instance_pos = -1)] @@ -336,14 +332,13 @@ public class Tasks.Widgets.TaskListGrid : Gtk.Grid { }); }); }); - task_list.add (task_row); + task_list.append (task_row); return true; }); Idle.add (() => { task_list.invalidate_sort (); - task_list.show_all (); return Source.REMOVE; }); diff --git a/src/Widgets/TaskRow.vala b/src/Widgets/TaskRow.vala index a40f51024..3ae6b8248 100644 --- a/src/Widgets/TaskRow.vala +++ b/src/Widgets/TaskRow.vala @@ -23,7 +23,6 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { private Tasks.Widgets.EntryPopover.Location location_popover; private Gtk.Revealer location_popover_revealer; - private Gtk.EventBox event_box; private Gtk.Stack state_stack; private Gtk.Image icon; private Gtk.CheckButton check; @@ -35,7 +34,7 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { private Gtk.Revealer task_form_revealer; private Gtk.TextBuffer description_textbuffer; - private Gtk.EventControllerKey key_controller; + private Gtk.DragSource drag_source; private TaskRow (ECal.Component task, E.Source source) { Object (task: task, source: source); @@ -53,7 +52,6 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { } construct { - can_focus = false; created = calcomponent_created (task); // GTasks tasks only have date on due time, so only show the date @@ -66,22 +64,22 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { warning ("unable to get the registry, assuming task is not from gtask"); } - icon = new Gtk.Image.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU); - icon.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); + icon = new Gtk.Image.from_icon_name ("list-add-symbolic"); + icon.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); check = new Gtk.CheckButton () { valign = Gtk.Align.CENTER }; + check.add_css_class ("task-row-check"); state_stack = new Gtk.Stack () { transition_type = Gtk.StackTransitionType.CROSSFADE }; - state_stack.add (icon); - state_stack.add (check); + state_stack.add_child (icon); + state_stack.add_child (check); summary_entry = new Gtk.Entry (); - - summary_entry.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + summary_entry.add_css_class (Granite.STYLE_CLASS_FLAT); due_datetime_popover = new Tasks.Widgets.EntryPopover.DateTime (); @@ -97,13 +95,13 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { }; due_datetime_popover.value_format.connect ((value) => { - due_datetime_popover.get_style_context ().remove_class ("error"); + due_datetime_popover.remove_css_class ("error"); if (value == null) { return null; } var today = new GLib.DateTime.now_local (); if (today.compare (value) > 0 && !completed) { - due_datetime_popover.get_style_context ().add_class ("error"); + due_datetime_popover.add_css_class ("error"); } if (is_gtask) { @@ -180,7 +178,7 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { lines = 1, ellipsize = Pango.EllipsizeMode.END }; - description_label.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); + description_label.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); // Should not use a transition that varies the width else label aligning and ellipsizing is incorrect. description_label_revealer = new Gtk.Revealer () { @@ -190,9 +188,9 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { }; var task_box = new Gtk.Box (HORIZONTAL, 0); - task_box.add (due_datetime_popover_revealer); - task_box.add (location_popover_revealer); - task_box.add (description_label_revealer); + task_box.append (due_datetime_popover_revealer); + task_box.append (location_popover_revealer); + task_box.append (description_label_revealer); task_detail_revealer = new Gtk.Revealer () { child = task_box, @@ -220,28 +218,28 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { var cancel_button = new Gtk.Button.with_label (_("Cancel")); var save_button = new Gtk.Button.with_label (created ? _("Save Changes") : _("Add Task")); - save_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); + save_button.add_css_class (Granite.STYLE_CLASS_SUGGESTED_ACTION); var right_buttons_box = new Gtk.Box (HORIZONTAL, 6) { hexpand = true, halign = END, homogeneous = true }; - right_buttons_box.add (cancel_button); - right_buttons_box.add (save_button); + right_buttons_box.append (cancel_button); + right_buttons_box.append (save_button); var button_box = new Gtk.Box (HORIZONTAL, 6) { margin_top = 12 }; - button_box.get_style_context ().add_class ("button-box"); - button_box.pack_end (right_buttons_box); + button_box.add_css_class ("button-box"); + button_box.append (right_buttons_box); var form_box = new Gtk.Box (VERTICAL, 12) { margin_top = 6, margin_bottom = 6 }; - form_box.add (description_frame); - form_box.add (button_box); + form_box.append (description_frame); + form_box.append (button_box); task_form_revealer = new Gtk.Revealer () { child = form_box, @@ -267,23 +265,12 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { transition_type = SLIDE_UP }; - event_box = new Gtk.EventBox () { - hexpand = true, - vexpand = true, - above_child = false - }; - event_box.add_events ( - Gdk.EventMask.BUTTON_PRESS_MASK | - Gdk.EventMask.BUTTON_RELEASE_MASK - ); - event_box.add (revealer); - - child = event_box; + child = revealer; margin_start = 12; margin_end = 12; - get_style_context ().add_class ("task"); - get_style_context ().add_class (Granite.STYLE_CLASS_ROUNDED); + add_css_class ("task"); + add_css_class (Granite.STYLE_CLASS_ROUNDED); if (created) { check.show (); @@ -292,9 +279,9 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { var delete_button = new Gtk.Button.with_label (_("Delete Task")) { halign = START }; - delete_button.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); + delete_button.add_css_class (Granite.STYLE_CLASS_DESTRUCTIVE_ACTION); - button_box.pack_start (delete_button); + button_box.prepend (delete_button); delete_button.clicked.connect (() => { end_editing (); @@ -305,7 +292,8 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { build_drag_and_drop (); - key_controller = new Gtk.EventControllerKey (this); + var key_controller = new Gtk.EventControllerKey (); + add_controller (key_controller); check.toggled.connect (() => { if (task == null) { @@ -321,7 +309,9 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { end_editing (); }); - summary_entry.grab_focus.connect (() => { + var summary_entry_focus_controller = new Gtk.EventControllerFocus (); + summary_entry.add_controller (summary_entry_focus_controller); + summary_entry_focus_controller.enter.connect (() => { activate (); }); @@ -405,18 +395,20 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { task_details_reveal_request (!value); if (value) { - get_style_context ().add_class ("collapsed"); - get_style_context ().add_class (Granite.STYLE_CLASS_CARD); + add_css_class ("collapsed"); + add_css_class (Granite.STYLE_CLASS_CARD); } else { - get_style_context ().remove_class (Granite.STYLE_CLASS_CARD); - get_style_context ().remove_class ("collapsed"); + remove_css_class (Granite.STYLE_CLASS_CARD); + remove_css_class ("collapsed"); } } public void update_request () { if (!is_scheduled_view) { - Tasks.Application.set_task_color (source, check); + // FIXME: check.get_first_child () is used because Gtk.StyleContext.add_provider works differently in Gtk4 + // Also, it's deprecated now, so we need to use Gtk.StyleContext.add_provider_for_display + Tasks.Application.set_task_color (source, check.get_first_child ()); } var default_due_datetime = new DateTime.now_local ().add_hours (1); @@ -429,10 +421,10 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { completed = false; check.active = completed; summary_entry.text = ""; - summary_entry.get_style_context ().remove_class (Gtk.STYLE_CLASS_DIM_LABEL); - summary_entry.get_style_context ().add_class ("add-task"); + summary_entry.remove_css_class (Granite.STYLE_CLASS_DIM_LABEL); + summary_entry.add_css_class ("add-task"); task_detail_revealer.reveal_child = false; - task_detail_revealer.get_style_context ().remove_class (Gtk.STYLE_CLASS_DIM_LABEL); + task_detail_revealer.remove_css_class (Granite.STYLE_CLASS_DIM_LABEL); due_datetime_popover_revealer.reveal_child = false; location_popover_revealer.reveal_child = false; @@ -454,14 +446,14 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { } summary_entry.text = ical_task.get_summary () == null ? "" : ical_task.get_summary (); - summary_entry.get_style_context ().remove_class ("add-task"); + summary_entry.remove_css_class ("add-task"); if (completed) { - summary_entry.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); - task_detail_revealer.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); + summary_entry.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); + task_detail_revealer.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); } else { - summary_entry.get_style_context ().remove_class (Gtk.STYLE_CLASS_DIM_LABEL); - task_detail_revealer.get_style_context ().remove_class (Gtk.STYLE_CLASS_DIM_LABEL); + summary_entry.remove_css_class (Granite.STYLE_CLASS_DIM_LABEL); + task_detail_revealer.remove_css_class (Granite.STYLE_CLASS_DIM_LABEL); } if (ical_task.get_due ().is_null_time ()) { @@ -538,45 +530,55 @@ public class Tasks.Widgets.TaskRow : Gtk.ListBoxRow { if (!created || is_scheduled_view) { return; } - Gtk.drag_source_set (event_box, Gdk.ModifierType.BUTTON1_MASK, Application.DRAG_AND_DROP_TASK_DATA, Gdk.DragAction.MOVE); - event_box.drag_begin.connect (on_drag_begin); - event_box.drag_data_get.connect (on_drag_data_get); - event_box.drag_data_delete.connect (on_drag_data_delete); + drag_source = new Gtk.DragSource (); + add_controller (drag_source); + + drag_source.prepare.connect (on_drag_prepare); + drag_source.drag_begin.connect (on_drag_begin); + drag_source.drag_cancel.connect (on_drag_cancel); + drag_source.drag_end.connect (on_drag_end); } - private void on_drag_begin (Gdk.DragContext context) { - Gtk.Allocation alloc; - get_allocation (out alloc); + private int drag_offset_x; + private int drag_offset_y; + private bool had_cards_class; + private bool is_canceled; - var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, alloc.width, alloc.height); - var cairo_context = new Cairo.Context (surface); + private Gdk.ContentProvider? on_drag_prepare (double x, double y) { + drag_offset_x = (int) x; + drag_offset_y = (int) y; - var had_cards_class = get_style_context ().has_class (Granite.STYLE_CLASS_CARD); + return new Gdk.ContentProvider.for_value ("task://%s/%s".printf (source.uid, task.get_uid ())); + } - get_style_context ().add_class ("drag-active"); - if (had_cards_class) { - get_style_context ().remove_class (Granite.STYLE_CLASS_CARD); - } - draw_to_cairo_context (cairo_context); - if (had_cards_class) { - get_style_context ().add_class (Granite.STYLE_CLASS_CARD); - } - get_style_context ().remove_class ("drag-active"); + private void on_drag_begin (Gdk.Drag drag) { + drag_source.set_icon (new Gtk.WidgetPaintable (this), drag_offset_x, drag_offset_y); - int drag_icon_x, drag_icon_y; - translate_coordinates (this, 0, 0, out drag_icon_x, out drag_icon_y); - surface.set_device_offset (-drag_icon_x, -drag_icon_y); + had_cards_class = has_css_class (Granite.STYLE_CLASS_CARD); + is_canceled = false; - Gtk.drag_set_icon_surface (context, surface); + add_css_class ("drag-active"); + if (had_cards_class) { + remove_css_class (Granite.STYLE_CLASS_CARD); + } } - private void on_drag_data_get (Gtk.Widget widget, Gdk.DragContext context, Gtk.SelectionData selection_data, uint target_type, uint time) { - var task_uri = "task://%s/%s".printf (source.uid, task.get_uid ()); - selection_data.set_uris ({ task_uri }); + private bool on_drag_cancel (Gdk.Drag drag, Gdk.DragCancelReason reason) { + is_canceled = true; + + return true; } - private void on_drag_data_delete (Gdk.DragContext context) { - destroy (); + private void on_drag_end (Gdk.Drag drag, bool delete_data) { + if (is_canceled) { + remove_css_class ("drag-active"); + if (had_cards_class) { + add_css_class (Granite.STYLE_CLASS_CARD); + } + } else { + ((Gtk.ListBox) parent).remove (this); + destroy (); + } } }