From 9d18665ac9b2829ffbe9bf0af56883a450d17134 Mon Sep 17 00:00:00 2001 From: Marco Betschart Date: Tue, 19 Oct 2021 07:30:50 +0000 Subject: [PATCH] Very rough sketch of an AccountChooser portal --- src/AccountChooser/AccountButton.vala | 33 +++++++ src/AccountChooser/Dialog.vala | 125 ++++++++++++++++++++++++++ src/AccountChooser/Portal.vala | 68 ++++++++++++++ src/meson.build | 3 + 4 files changed, 229 insertions(+) create mode 100644 src/AccountChooser/AccountButton.vala create mode 100644 src/AccountChooser/Dialog.vala create mode 100644 src/AccountChooser/Portal.vala diff --git a/src/AccountChooser/AccountButton.vala b/src/AccountChooser/AccountButton.vala new file mode 100644 index 00000000..dac293ee --- /dev/null +++ b/src/AccountChooser/AccountButton.vala @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrigthText: 2021 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + + public class AccountChooser.AccountButton : Gtk.Button { + public string account_id { get; construct; } + + public AccountButton (string account_id) { + Object (account_id: account_id); + } + + construct { + get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + + var name = new Gtk.Label (account_id) { + halign = Gtk.Align.CENTER, + justify = Gtk.Justification.CENTER, + lines = 2, + max_width_chars = 16, + width_chars = 16, + wrap_mode = Pango.WrapMode.WORD_CHAR + }; + name.set_ellipsize (Pango.EllipsizeMode.END); + + var grid = new Gtk.Box (Gtk.Orientation.VERTICAL, 6) { + halign = Gtk.Align.CENTER + }; + grid.add (name); + + add (grid); + } +} diff --git a/src/AccountChooser/Dialog.vala b/src/AccountChooser/Dialog.vala new file mode 100644 index 00000000..c3a02c72 --- /dev/null +++ b/src/AccountChooser/Dialog.vala @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrigthText: 2021 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +[DBus (name = "org.freedesktop.impl.portal.Request")] +public class AccountChooser.Dialog : Hdy.Window { + public string app_id { get; construct; } + private DBusConnection connection; + private uint register_id; + + private HashTable buttons; + private AccountButton selected; + + private Hdy.Carousel carousel; + private weak Gtk.Box last_box; + + public signal void choiced (string account_id); + + public Dialog (DBusConnection conn, ObjectPath handle, string app_id, string parent_window) { + Object (app_id: app_id, default_width: 700, resizable: false); + connection = conn; + + try { + register_id = connection.register_object (handle, this); + } catch (Error e) { + critical (e.message); + } + + realize (); + + if (parent_window != "") { + var parent = ExternalWindow.from_handle (parent_window); + + if (parent == null) { + warning ("Failed to associate portal window with parent window %s", parent_window); + } else { + parent.set_parent_of (get_window ()); + } + } + } + + construct { + buttons = new HashTable (str_hash, str_equal); + AppInfo? info = app_id == "" ? null : new DesktopAppInfo (app_id + ".desktop"); + Hdy.init (); + + var handle = new Hdy.WindowHandle (); + + var cancel = new Gtk.Button.with_label ("Cancel"); + var select = new Gtk.Button.with_label ("Select"); + select.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); + + var button_box = new Gtk.ButtonBox (Gtk.Orientation.HORIZONTAL) { + layout_style = Gtk.ButtonBoxStyle.END, + valign = Gtk.Align.END, + margin_top = 12, + expand = true, + spacing = 6 + }; + + button_box.add (cancel); + button_box.add (select); + + var grid = new Gtk.Grid () { + orientation = Gtk.Orientation.VERTICAL, + column_spacing = 12, + row_spacing = 6, + margin = 12 + }; + + grid.attach (button_box, 0, 0); + + handle.add (grid); + add (handle); + + select.clicked.connect (() => choiced (selected.account_id)); + cancel.clicked.connect (() => choiced ("")); + + // close the dialog after a selection; + choiced.connect_after (() => destroy ()); + + destroy.connect (() => { + if (register_id != 0) { + connection.unregister_object (register_id); + register_id = 0; + } + }); + + create_box (); + } + + private void create_box () { + var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); + box.add.connect_after (() => { + if (box.get_children ().length () == 5) { + create_box (); + } + }); + + carousel.insert (box, -1); + last_box = box; + } + + private void add_choice (string choice) { + var button = new AccountButton (choice); + button.clicked.connect (() => { selected = button; }); + buttons[choice] = button; + last_box.add (button); + } + + [DBus (visible = false)] + public void update_choices (string[] choices) { + foreach (var choice in choices) { + if (!(choice in buttons)) { + add_choice (choice); + } + } + } + + [DBus (name = "Close")] + public void on_close () throws DBusError, IOError { + destroy (); + } +} diff --git a/src/AccountChooser/Portal.vala b/src/AccountChooser/Portal.vala new file mode 100644 index 00000000..838b8370 --- /dev/null +++ b/src/AccountChooser/Portal.vala @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrigthText: 2021 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +[DBus (name = "org.freedesktop.impl.portal.AccountChooser")] +public class AccountChooser.Portal : Object { + private HashTable handles; + private DBusConnection connection; + + public Portal (DBusConnection connection) { + handles = new HashTable (str_hash, str_equal); + this.connection = connection; + } + + public async void choose_application ( + ObjectPath handle, + string app_id, + string parent_window, + string[] choices, + HashTable options, + out uint response, + out HashTable results + ) throws DBusError, IOError { + var dialog = new Dialog (connection, handle, app_id, parent_window); + + /*if ("last_choice" in options && options["last_choice"].is_of_type (VariantType.STRING)) { + dialog.last_choice = options["last_choice"].get_string (); + } if ("modal" in options && options["modal"].is_of_type (VariantType.BOOLEAN)) { + dialog.modal = options["modal"].get_boolean (); + } if ("content_type" in options && options["content_type"].is_of_type (VariantType.STRING)) { + dialog.content_type = options["content_type"].get_string (); + } if ("filename" in options && options["filename"].is_of_type (VariantType.STRING)) { + dialog.filename = options["filename"].get_string (); + } + + dialog.update_choices (choices); + var _results = new HashTable (str_hash, str_equal); + uint _response = 2; + + var destroy_id = dialog.destroy.connect_after (() => { + _results["choice"] = ""; + choose_application.callback (); + }); + + dialog.choiced.connect ((app_id) => { + dialog.disconnect (destroy_id); + + _results["choice"] = app_id.replace (".desktop", ""); + _response = app_id == "" ? 1 : 0; + + choose_application.callback (); + });*/ + + handles[handle] = dialog; + dialog.show_all (); + yield; + + //results = _results; + //response = _response; + } + + /*public async void update_choices (ObjectPath handle, string[] choices) throws DBusError, IOError { + if (handle in handles) { + handles[handle].update_choices (choices); + } + }*/ +} diff --git a/src/meson.build b/src/meson.build index 5ae695d8..d27167f9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,6 +11,9 @@ executable( 'ExternalWindow.vala', 'Access/Portal.vala', 'Access/Dialog.vala', + 'AccountChooser/Portal.vala', + 'AccountChooser/Dialog.vala', + 'AccountChooser/AccountButton.vala', 'AppChooser/Portal.vala', 'AppChooser/Dialog.vala', 'AppChooser/AppButton.vala',