ui(): Port to new adwaita dialogs

This commit is contained in:
Julian Schmidhuber committed 2024-02-27 16:38:09 +01:00
1 parent 10a05fa20f
commit 028ffc92de
20 files changed
+242 -177

No files matched your search

+1 -1
View File
@@ -19,7 +19,7 @@ opt-level = 3
[dependencies]
gtk = { version = "0.8.0", package = "gtk4", features = ["v4_12"]}
gdk = { version = "0.8.0", package = "gdk4" }
adw = { version = "0.6.0", package = "libadwaita", features = ["v1_4"]}
adw = { version = "0.6.0", package = "libadwaita", features = ["v1_5"]}
ashpd = { version = "0.8.0", default-features = false, features = [ "gtk4", "tokio" ]}
zbus = { version = "4.1.2", default-features = false, features = [ "tokio" ]}
+1 -1
View File
@@ -1,7 +1,7 @@
using Gtk 4.0;
using Adw 1;
Adw.AboutWindow about {
Adw.AboutDialog about {
application-icon: "icon";
application-name: "Flare";
version: "@VERSION@";
+60 -47
View File
@@ -1,56 +1,69 @@
using Gtk 4.0;
using Adw 1;
template $FlChannelInfoDialog: Adw.MessageDialog {
heading: bind template.channel as <$FlChannel>.title;
template $FlChannelInfoDialog: Adw.Dialog {
can-focus: true;
content-height: 600;
content-width: 450;
extra-child: Box {
spacing: 5;
orientation: vertical;
Adw.PreferencesGroup {
title: _("Channel Information");
header-suffix:
Adw.Avatar avatar {
icon-name: "user-bookmarks-symbolic";
size: 30;
};
Adw.ActionRow row_phone {
styles ["property"]
title: _("Phone Number");
icon-name: "call-start-symbolic";
}
Adw.ActionRow row_disappearing {
styles ["property"]
title: _("Disappearing Messages");
icon-name: "appointment-missed-symbolic";
}
Adw.ActionRow row_description {
styles ["property"]
title: _("Description");
subtitle-lines: 2;
}
Adw.ToolbarView {
[top]
Adw.HeaderBar {
title-widget: Adw.WindowTitle {
title: bind template.channel as <$FlChannel>.title;
};
}
// Session Reset
Button button_reset_session {
accessibility {
label: C_("accessibility", "Reset Session");
Adw.Clamp {
orientation: horizontal;
tightening-threshold: 300;
maximum-size: 600;
Box {
spacing: 5;
orientation: vertical;
Adw.PreferencesGroup {
title: _("Channel Information");
header-suffix:
Adw.Avatar avatar {
icon-name: "user-bookmarks-symbolic";
size: 30;
};
Adw.ActionRow row_phone {
styles ["property"]
title: _("Phone Number");
icon-name: "call-start-symbolic";
}
Adw.ActionRow row_disappearing {
styles ["property"]
title: _("Disappearing Messages");
icon-name: "appointment-missed-symbolic";
}
Adw.ActionRow row_description {
styles ["property"]
title: _("Description");
subtitle-lines: 2;
}
}
// Session Reset
Button button_reset_session {
accessibility {
label: C_("accessibility", "Reset Session");
}
label: _("Reset Session");
clicked => $reset_session() swapped;
}
}
label: _("Reset Session");
clicked => $reset_session() swapped;
}
};
responses [
close: _("Close"),
]
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
using Gtk 4.0;
using Adw 1;
Adw.MessageDialog dialog {
Adw.AlertDialog dialog {
heading: _("Clear messages");
body: _("This will clear all locally stored messages. This will also close the application.");
default-response: "cancel";
+1 -1
View File
@@ -1,7 +1,7 @@
using Gtk 4.0;
using Adw 1;
Adw.MessageDialog dialog {
Adw.AlertDialog dialog {
heading: _("Unlink");
body: _("This will unlink this device and remove all locally saved data. This will also close the application such that it can be relinked when opening it again.");
default-response: "cancel";
+1 -1
View File
@@ -1,7 +1,7 @@
using Gtk 4.0;
using Adw 1;
template $FlErrorDialog: Adw.MessageDialog {
template $FlErrorDialog: Adw.AlertDialog {
heading: _("Error");
response => $handle_response();
+6 -8
View File
@@ -1,14 +1,12 @@
using Gtk 4.0;
using Adw 1;
template $FlLinkedDevicesWindow: Adw.Window {
template $FlLinkedDevicesWindow: Adw.Dialog {
can-focus: true;
default-height: 600;
default-width: 450;
destroy-with-parent: true;
modal: true;
content-height: 600;
content-width: 450;
content: Adw.ToolbarView {
Adw.ToolbarView {
[top]
Adw.HeaderBar {
title-widget: Adw.WindowTitle {
@@ -26,10 +24,10 @@ template $FlLinkedDevicesWindow: Adw.Window {
Gtk.ListView list_devices {}
}
};
}
}
Adw.MessageDialog add_device_dialog {
Adw.AlertDialog add_device_dialog {
heading: _("Add Linked Device");
body: _("Please insert the URL of the device to link.");
default-response: "cancel";
+1 -1
View File
@@ -1,7 +1,7 @@
using Gtk 4.0;
using Adw 1;
template $FlPreferencesWindow: Adw.PreferencesWindow {
template $FlPreferencesWindow: Adw.PreferencesDialog {
Adw.PreferencesPage {
title: _("General");
icon-name: "preferences-system-symbolic";
+5 -8
View File
@@ -1,15 +1,12 @@
using Gtk 4.0;
using Adw 1;
template $FlSetupWindow: Adw.Window {
template $FlSetupWindow: Adw.Dialog {
can-focus: true;
default-height: 600;
default-width: 450;
destroy-with-parent: true;
modal: true;
deletable: false;
content-height: 600;
content-width: 450;
content: Adw.ToastOverlay toast_overlay {
Adw.ToastOverlay toast_overlay {
child: Box {
orientation: vertical;
@@ -532,5 +529,5 @@ template $FlSetupWindow: Adw.Window {
}
}
};
};
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
using Gtk 4.0;
using Adw 1;
Adw.MessageDialog dialog {
Adw.AlertDialog dialog {
heading: _("Submit Captcha");
// Translators: Do not translate tags around "on this website".
body: _("Submit a Captcha challenge. The token can be obtained from the error message that was displayed from Flare. The captcha must be filled out <a href=\"https://signalcaptchas.org/challenge/generate.html\">on this website</a> and the link to open Signal must be pasted to the corresponding entry. Note that the captcha is only valid for about one minute.");
+25 -2
View File
@@ -11,6 +11,29 @@
pkgs = import nixpkgs {
inherit system;
};
libadwaita_1_5 = pkgs.libadwaita.overrideAttrs (finalAttrs: prevAttrs: {
version = "1.5.beta";
src = with pkgs; fetchFromGitLab {
domain = "gitlab.gnome.org";
owner = "GNOME";
repo = "libadwaita";
rev = "66edcd31c1acfb5a569db02870d1f7e471946fcf";
hash = "sha256-ipyifdRwZCO8H7qQOcEHOXW9PT9w+Ix+k374qHRfSjg=";
};
});
blueprint-compiler-git = pkgs.blueprint-compiler.overrideAttrs (finalAttrs: prevAttrs: {
version = "0.10.1.git";
src = with pkgs; fetchFromGitLab {
domain = "gitlab.gnome.org";
owner = "jwestman";
repo = "blueprint-compiler";
rev = "d47955c5a20b2f7cf85ff25a00b02160883aa0b1";
hash = "sha256-yNFgJpCGu2CrT6J004N4G+Nlt+3/DbEkSGOWkvSn4J8=";
};
});
name = "flare";
in
rec {
@@ -38,8 +61,8 @@
./flake.lock
]);
};
buildInputs = with pkgs; [ pkgs.libadwaita pkgs.protobuf pkgs.libsecret pkgs.gst_all_1.gstreamer pkgs.gst_all_1.gst-plugins-base pkgs.gst_all_1.gst-plugins-good pkgs.gst_all_1.gst-plugins-bad pkgs.gtksourceview5 pkgs.gtk4 ];
nativeBuildInputs = with pkgs; [ pkgs.appstream-glib pkgs.blueprint-compiler pkgs.desktop-file-utils pkgs.meson pkgs.ninja pkgs.pkg-config pkgs.wrapGAppsHook4 pkgs.rustPlatform.cargoSetupHook cargo rustc pkgs.glib ];
buildInputs = with pkgs; [ libadwaita_1_5 pkgs.protobuf pkgs.libsecret pkgs.gst_all_1.gstreamer pkgs.gst_all_1.gst-plugins-base pkgs.gst_all_1.gst-plugins-good pkgs.gst_all_1.gst-plugins-bad pkgs.gtksourceview5 pkgs.gtk4 ];
nativeBuildInputs = with pkgs; [ pkgs.appstream-glib blueprint-compiler-git pkgs.desktop-file-utils pkgs.meson pkgs.ninja pkgs.pkg-config pkgs.wrapGAppsHook4 pkgs.rustPlatform.cargoSetupHook cargo rustc pkgs.glib ];
PROTOC = "${pkgs.protobuf}/bin/protoc";
+3 -2
View File
@@ -31,6 +31,7 @@ pub fn backend_to_gui(attachment: &crate::backend::Attachment) -> Attachment {
pub mod imp {
use std::{cell::RefCell, os::fd::AsFd};
use adw::prelude::*;
use ashpd::{desktop::open_uri::OpenFileRequest, WindowIdentifier};
use gdk::{
gio::File,
@@ -43,7 +44,7 @@ pub mod imp {
ParamSpec, ParamSpecObject, Value,
};
use gtk::{gio, glib, FileDialog};
use gtk::{prelude::*, subclass::prelude::*, CompositeTemplate};
use gtk::{subclass::prelude::*, CompositeTemplate};
use once_cell::sync::Lazy;
use crate::{
@@ -139,7 +140,7 @@ pub mod imp {
if let Err(e) = attachment.save_to_file(&file).await {
let root = obj.imp().window();
let dialog = ErrorDialog::new(e.into(), &root);
dialog.present();
dialog.present(&root);
}
}));
} else {
+6 -12
View File
@@ -1,7 +1,4 @@
use gdk::{
glib::subclass::types::ObjectSubclassIsExt,
prelude::{IsA, ObjectExt},
};
use gdk::{glib::subclass::types::ObjectSubclassIsExt, prelude::ObjectExt};
use glib::Object;
use gtk::glib;
@@ -9,18 +6,17 @@ use crate::backend::{Channel, Manager};
glib::wrapper! {
pub struct ChannelInfoDialog(ObjectSubclass<imp::ChannelInfoDialog>)
@extends adw::MessageDialog, gtk::Window, gtk::Widget,
@extends adw::Dialog, gtk::Widget,
@implements gtk::gio::ActionGroup, gtk::gio::ActionMap, gtk::Accessible, gtk::Buildable,
gtk::ConstraintTarget;
}
impl ChannelInfoDialog {
pub fn new(channel: &Channel, manager: &Manager, parent: &impl IsA<gtk::Window>) -> Self {
pub fn new(channel: &Channel, manager: &Manager) -> Self {
log::trace!("Initializing `ChannelInfoDialog`");
let s = Object::builder::<Self>()
.property("channel", channel)
.property("manager", manager)
.property("transient-for", parent)
.build();
s.imp().setup();
s
@@ -33,7 +29,7 @@ impl ChannelInfoDialog {
pub mod imp {
use adw::prelude::ActionRowExt;
use adw::subclass::prelude::MessageDialogImpl;
use adw::subclass::dialog::AdwDialogImpl;
use std::cell::RefCell;
use glib::{subclass::InitializingObject, ParamSpec, ParamSpecObject, Value};
@@ -172,7 +168,7 @@ pub mod imp {
impl ObjectSubclass for ChannelInfoDialog {
const NAME: &'static str = "FlChannelInfoDialog";
type Type = super::ChannelInfoDialog;
type ParentType = adw::MessageDialog;
type ParentType = adw::Dialog;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
@@ -229,8 +225,6 @@ pub mod imp {
}
}
impl WindowImpl for ChannelInfoDialog {}
impl MessageDialogImpl for ChannelInfoDialog {}
impl AdwDialogImpl for ChannelInfoDialog {}
impl WidgetImpl for ChannelInfoDialog {}
impl BoxImpl for ChannelInfoDialog {}
}
+4 -3
View File
@@ -138,11 +138,12 @@ impl ChannelMessages {
pub mod imp {
use std::cell::{Cell, RefCell};
use adw::prelude::*;
use glib::{
clone, subclass::InitializingObject, ParamSpec, ParamSpecBoolean, ParamSpecObject, Value,
};
use gtk::{gio, glib, FileDialog, PositionType, SignalListItemFactory};
use gtk::{prelude::*, subclass::prelude::*, CompositeTemplate};
use gtk::{subclass::prelude::*, CompositeTemplate};
use once_cell::sync::Lazy;
use crate::backend::timeline::Timeline;
@@ -350,7 +351,7 @@ pub mod imp {
.dynamic_cast::<crate::gui::Window>()
.expect("Root of `ChannelMessages` to be a `Window`.");
let dialog = ErrorDialog::new(e, &root);
dialog.present();
dialog.present(&root);
return;
}
}
@@ -362,7 +363,7 @@ pub mod imp {
.dynamic_cast::<crate::gui::Window>()
.expect("Root of `ChannelMessages` to be a `Window`.");
let dialog = ErrorDialog::new(e, &root);
dialog.present();
dialog.present(&root);
}
})
);
+33 -13
View File
@@ -1,16 +1,18 @@
use adw::prelude::MessageDialogExt;
use adw::prelude::AlertDialogExt;
use gdk::glib::object::ObjectExt;
use glib::{prelude::IsA, Object};
use gtk::glib;
use crate::ApplicationError;
use super::Window;
const REPORT: &str = "report";
glib::wrapper! {
pub struct ErrorDialog(ObjectSubclass<imp::ErrorDialog>)
@extends adw::MessageDialog, gtk::Window, gtk::Widget,
@implements gtk::gio::ActionGroup, gtk::gio::ActionMap, gtk::Accessible, gtk::Buildable,
gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
@extends adw::AlertDialog, adw::Dialog, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}
impl ErrorDialog {
@@ -22,29 +24,39 @@ impl ErrorDialog {
.property("body", &error.to_string())
.property("secondary-error", &error.more_information())
.property("should-report", error.should_report())
.property("transient-for", parent)
.property("window", parent)
.build();
s.set_response_enabled(REPORT, error.should_report());
s
}
fn window(&self) -> Window {
self.property("window")
}
}
pub mod imp {
pub(crate) use std::cell::Cell;
use std::cell::RefCell;
use adw::prelude::*;
use adw::subclass::prelude::*;
use gdk::gio;
use gdk::glib::ParamSpecObject;
use glib::{subclass::InitializingObject, ParamSpec, ParamSpecBoolean, ParamSpecString, Value};
use gtk::CompositeTemplate;
use gtk::{glib, UriLauncher};
use gtk::{prelude::*, CompositeTemplate};
use once_cell::sync::Lazy;
use crate::gui::Window;
#[derive(CompositeTemplate, Default)]
#[template(resource = "/ui/error_dialog.ui")]
pub struct ErrorDialog {
secondary_error: RefCell<Option<String>>,
should_report: Cell<bool>,
window: RefCell<Option<Window>>,
}
#[gtk::template_callbacks]
@@ -56,7 +68,7 @@ pub mod imp {
let launcher =
UriLauncher::new("https://gitlab.com/schmiddi-on-mobile/flare/-/issues");
launcher.launch(
self.obj().transient_for().as_ref(),
Some(&self.obj().window()),
None::<&gio::Cancellable>,
|_| {},
);
@@ -68,7 +80,7 @@ pub mod imp {
impl ObjectSubclass for ErrorDialog {
const NAME: &'static str = "FlErrorDialog";
type Type = super::ErrorDialog;
type ParentType = adw::MessageDialog;
type ParentType = adw::AlertDialog;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
@@ -95,6 +107,9 @@ pub mod imp {
ParamSpecBoolean::builder("should-report")
.construct_only()
.build(),
ParamSpecObject::builder::<Window>("window")
.construct_only()
.build(),
]
});
PROPERTIES.as_ref()
@@ -104,6 +119,7 @@ pub mod imp {
match pspec.name() {
"secondary-error" => self.secondary_error.borrow().as_ref().to_value(),
"should-report" => self.should_report.get().to_value(),
"window" => self.window.borrow().as_ref().to_value(),
_ => unimplemented!(),
}
}
@@ -122,15 +138,19 @@ pub mod imp {
);
self.should_report.replace(r);
}
"window" => {
let win = value
.get::<Option<Window>>()
.expect("Property `window` of `ErrorDialog` has to be of type `Window`");
self.window.replace(win);
}
_ => unimplemented!(),
}
}
}
impl MessageDialogImpl for ErrorDialog {}
impl AdwAlertDialogImpl for ErrorDialog {}
impl WidgetImpl for ErrorDialog {}
impl WindowImpl for ErrorDialog {}
impl ApplicationWindowImpl for ErrorDialog {}
impl AdwWindowImpl for ErrorDialog {}
impl AdwApplicationWindowImpl for ErrorDialog {}
impl AdwDialogImpl for ErrorDialog {}
}
+32 -24
View File
@@ -1,6 +1,4 @@
use crate::backend::Manager;
use adw::prelude::GtkWindowExt;
use gdk::prelude::CastNone;
use glib::{prelude::ObjectExt, Object};
use gtk::glib;
@@ -8,9 +6,8 @@ use super::Window;
glib::wrapper! {
pub struct LinkedDevicesWindow(ObjectSubclass<imp::LinkedDevicesWindow>)
@extends adw::Window, gtk::Window, gtk::Widget,
@implements gtk::gio::ActionGroup, gtk::gio::ActionMap, gtk::Accessible, gtk::Buildable,
gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
@extends adw::Dialog, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}
impl LinkedDevicesWindow {
@@ -18,7 +15,7 @@ impl LinkedDevicesWindow {
log::trace!("Initializing link window");
Object::builder::<Self>()
.property("manager", &manager)
.property("transient-for", parent)
.property("window", parent)
.build()
}
@@ -27,16 +24,13 @@ impl LinkedDevicesWindow {
}
fn window(&self) -> Window {
self.transient_for()
.and_dynamic_cast()
.expect("LinkedDevicesWindow to have a Window parent")
self.property("window")
}
}
pub mod imp {
use adw::prelude::EditableExt;
use adw::prelude::GtkWindowExt;
use adw::prelude::{MessageDialogExt, WidgetExt};
use adw::prelude::WidgetExt;
use adw::prelude::*;
use adw::subclass::prelude::*;
use gdk::glib::clone;
use gdk::prelude::ParamSpecBuilderExt;
@@ -52,16 +46,19 @@ pub mod imp {
use crate::gspawn;
use crate::gui::error_dialog::ErrorDialog;
use crate::gui::utility::Utility;
use crate::gui::Window;
#[derive(CompositeTemplate, Default)]
#[template(resource = "/ui/linked_devices_window.ui")]
pub struct LinkedDevicesWindow {
// TODO: Port to AlertDialog
#[template_child]
add_device_dialog: TemplateChild<adw::MessageDialog>,
add_device_dialog: TemplateChild<adw::AlertDialog>,
#[template_child]
entry_device_url: TemplateChild<adw::EntryRow>,
manager: RefCell<Option<Manager>>,
window: RefCell<Option<Window>>,
}
#[gtk::template_callbacks]
@@ -70,9 +67,7 @@ pub mod imp {
fn handle_add_linked_device(&self) {
log::trace!("User asked to add device link. Presenting dialog.");
self.entry_device_url.set_text("");
self.add_device_dialog
.set_transient_for(Some(&self.obj().window()));
self.add_device_dialog.present();
self.add_device_dialog.present(&self.obj().window());
}
#[template_callback]
@@ -88,8 +83,9 @@ pub mod imp {
let manager = self.obj().manager();
gspawn!(clone!(@weak obj => async move {
if let Err(e) = manager.link_secondary(url).await {
let dialog = ErrorDialog::new(e.into(), &obj.window());
dialog.present();
let root = obj.window();
let dialog = ErrorDialog::new(e.into(), &root);
dialog.present(&root);
}
}));
}
@@ -109,7 +105,7 @@ pub mod imp {
impl ObjectSubclass for LinkedDevicesWindow {
const NAME: &'static str = "FlLinkedDevicesWindow";
type Type = super::LinkedDevicesWindow;
type ParentType = adw::Window;
type ParentType = adw::Dialog;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
@@ -130,9 +126,14 @@ pub mod imp {
fn properties() -> &'static [ParamSpec] {
static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
vec![ParamSpecObject::builder::<Manager>("manager")
.construct_only()
.build()]
vec![
ParamSpecObject::builder::<Manager>("manager")
.construct_only()
.build(),
ParamSpecObject::builder::<Window>("window")
.construct_only()
.build(),
]
});
PROPERTIES.as_ref()
}
@@ -140,6 +141,7 @@ pub mod imp {
fn property(&self, _id: usize, pspec: &ParamSpec) -> Value {
match pspec.name() {
"manager" => self.manager.borrow().as_ref().to_value(),
"window" => self.window.borrow().as_ref().to_value(),
_ => unimplemented!(),
}
}
@@ -153,12 +155,18 @@ pub mod imp {
self.manager.replace(man);
}
"window" => {
let win = value.get::<Option<Window>>().expect(
"Property `window` of `LinkedDevicesWindow` has to be of type `Window`",
);
self.window.replace(win);
}
_ => unimplemented!(),
}
}
}
impl WidgetImpl for LinkedDevicesWindow {}
impl WindowImpl for LinkedDevicesWindow {}
impl AdwWindowImpl for LinkedDevicesWindow {}
impl AdwDialogImpl for LinkedDevicesWindow {}
}
+3 -2
View File
@@ -221,13 +221,14 @@ pub mod imp {
use regex::Regex;
use std::cell::{Cell, RefCell};
use adw::prelude::*;
use glib::{
clone,
subclass::{InitializingObject, Signal},
ParamSpec, ParamSpecBoolean, ParamSpecObject, Value,
};
use gtk::{glib, EmojiChooser};
use gtk::{prelude::*, subclass::prelude::*, CompositeTemplate};
use gtk::{subclass::prelude::*, CompositeTemplate};
use once_cell::sync::Lazy;
use crate::{
@@ -392,7 +393,7 @@ pub mod imp {
.dynamic_cast::<crate::gui::Window>()
.expect("Root of `ChannelMessages` to be a `Window`.");
let dialog = ErrorDialog::new(e, &root);
dialog.present();
dialog.present(&root);
}
obj.notify("has-reaction");
}));
+4 -5
View File
@@ -11,7 +11,7 @@ use crate::{gspawn, tspawn};
glib::wrapper! {
pub struct PreferencesWindow(ObjectSubclass<imp::PreferencesWindow>)
@extends adw::PreferencesWindow, adw::Window, gtk::Window, gtk::Widget,
@extends adw::PreferencesDialog, adw::Dialog, gtk::Widget,
@implements gtk::gio::ActionGroup, gtk::gio::ActionMap, gtk::Accessible, gtk::Buildable,
gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
}
@@ -177,7 +177,7 @@ pub mod imp {
impl ObjectSubclass for PreferencesWindow {
const NAME: &'static str = "FlPreferencesWindow";
type Type = super::PreferencesWindow;
type ParentType = adw::PreferencesWindow;
type ParentType = adw::PreferencesDialog;
fn new() -> Self {
Self {
@@ -211,7 +211,6 @@ pub mod imp {
}
}
impl WidgetImpl for PreferencesWindow {}
impl WindowImpl for PreferencesWindow {}
impl PreferencesWindowImpl for PreferencesWindow {}
impl AdwWindowImpl for PreferencesWindow {}
impl PreferencesDialogImpl for PreferencesWindow {}
impl AdwDialogImpl for PreferencesWindow {}
}
+30 -17
View File
@@ -1,6 +1,5 @@
use crate::backend::{Manager, Server, SetupResult};
use adw::prelude::GtkWindowExt;
use gdk::{glib::subclass::types::ObjectSubclassIsExt, prelude::CastNone};
use gdk::glib::subclass::types::ObjectSubclassIsExt;
use glib::{prelude::ObjectExt, Object};
use gtk::glib;
use libsignal_service::configuration::SignalServers;
@@ -9,7 +8,7 @@ use super::Window;
glib::wrapper! {
pub struct SetupWindow(ObjectSubclass<imp::SetupWindow>)
@extends adw::Window, gtk::Window, gtk::Widget,
@extends adw::Dialog, gtk::Widget,
@implements gtk::gio::ActionGroup, gtk::gio::ActionMap, gtk::Accessible, gtk::Buildable,
gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
}
@@ -19,7 +18,7 @@ impl SetupWindow {
log::trace!("Initializing setup window");
Object::builder::<Self>()
.property("manager", &manager)
.property("transient-for", parent)
.property("window", parent)
.build()
}
@@ -32,9 +31,7 @@ impl SetupWindow {
}
pub fn window(&self) -> Window {
self.transient_for()
.and_dynamic_cast()
.expect("SetupWindow to have a Window parent")
self.property("window")
}
}
@@ -52,13 +49,14 @@ fn servers() -> Vec<Server> {
}
pub mod imp {
use adw::prelude::AdwDialogExt;
use adw::prelude::ComboRowExt;
use adw::subclass::dialog::AdwDialogImplExt;
use adw::subclass::prelude::*;
use adw::Toast;
use futures::channel::oneshot::Sender;
use gdk::gdk_pixbuf::Pixbuf;
use gdk::gio::ListStore;
use gdk::glib::Propagation;
use gettextrs::gettext;
use gio::MemoryInputStream;
use glib::{subclass::InitializingObject, Bytes, ParamSpec, ParamSpecObject, Value};
@@ -72,6 +70,7 @@ pub mod imp {
use crate::backend::{Manager, Server, SetupDecision, SetupResult};
use crate::gui::utility::Utility;
use crate::gui::Window;
#[derive(CompositeTemplate, Default)]
#[template(resource = "/ui/setup_window.ui")]
@@ -117,6 +116,8 @@ pub mod imp {
manager: RefCell<Option<Manager>>,
window: RefCell<Option<Window>>,
decision_callback: RefCell<Option<Sender<SetupDecision>>>,
confirm_callback: RefCell<Option<Sender<String>>>,
@@ -237,7 +238,7 @@ pub mod imp {
let obj = self.obj();
match result {
SetupResult::Pending(callback) => {
obj.present();
obj.present(&obj.window());
self.decision_callback.replace(callback.take());
let win = obj.window();
@@ -295,7 +296,7 @@ pub mod imp {
impl ObjectSubclass for SetupWindow {
const NAME: &'static str = "FlSetupWindow";
type Type = super::SetupWindow;
type ParentType = adw::Window;
type ParentType = adw::Dialog;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
@@ -317,9 +318,14 @@ pub mod imp {
fn properties() -> &'static [ParamSpec] {
static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
vec![ParamSpecObject::builder::<Manager>("manager")
.construct_only()
.build()]
vec![
ParamSpecObject::builder::<Manager>("manager")
.construct_only()
.build(),
ParamSpecObject::builder::<Window>("window")
.construct_only()
.build(),
]
});
PROPERTIES.as_ref()
}
@@ -327,6 +333,7 @@ pub mod imp {
fn property(&self, _id: usize, pspec: &ParamSpec) -> Value {
match pspec.name() {
"manager" => self.manager.borrow().as_ref().to_value(),
"window" => self.window.borrow().as_ref().to_value(),
_ => unimplemented!(),
}
}
@@ -340,21 +347,27 @@ pub mod imp {
self.manager.replace(man);
}
"window" => {
let win = value
.get::<Option<Window>>()
.expect("Property `window` of `SetupWindow` has to be of type `Window`");
self.window.replace(win);
}
_ => unimplemented!(),
}
}
}
impl WidgetImpl for SetupWindow {}
impl WindowImpl for SetupWindow {
fn close_request(&self) -> Propagation {
impl AdwDialogImpl for SetupWindow {
fn closed(&self) {
// If it is closed while not being finished, close the parent window
if self.content.visible_page() != Some(self.page_finished.get()) {
self.obj().window().destroy();
}
self.parent_close_request()
self.parent_closed()
}
}
impl AdwWindowImpl for SetupWindow {}
}
+24 -27
View File
@@ -86,9 +86,8 @@ impl Window {
pub mod imp {
use std::{cell::RefCell, env, path::PathBuf};
use adw::prelude::MessageDialogExtManual;
use adw::{prelude::*, subclass::prelude::*, AboutWindow, MessageDialog};
use adw::{EntryRow, ResponseAppearance};
use adw::{prelude::*, subclass::prelude::*, AboutDialog};
use adw::{AlertDialog, EntryRow, ResponseAppearance};
use gdk::gio::Cancellable;
use gdk::glib::{BindingFlags, BoxedAnyObject, Propagation};
use gio::{Settings, SimpleAction, SimpleActionGroup};
@@ -148,19 +147,18 @@ pub mod imp {
log::trace!("Setting up preferences-window action");
let obj = self.obj();
let action_settings = SimpleAction::new("settings", None);
action_settings.connect_activate(|_, _| {
action_settings.connect_activate(clone!(@weak obj => move |_, _| {
let settings = PreferencesWindow::new();
settings.present();
});
settings.present(&obj);
}));
let action_clear_messages = SimpleAction::new("clear-messages", None);
action_clear_messages.connect_activate(clone!(@weak obj => move |_, _| {
log::trace!("User requested to clear messages");
let builder = Builder::from_resource("/ui/dialog_clear_messages.ui");
let dialog: MessageDialog = builder
let dialog: AlertDialog = builder
.object("dialog")
.expect("dialog_clear_messages.ui to have at least one object dialog");
dialog.set_transient_for(Some(&obj));
dialog.connect_response(None, clone!(@weak obj => move |_dialog, response| {
if response == "clear" {
log::info!("Clear messages device");
@@ -173,17 +171,16 @@ pub mod imp {
obj.kill();
}
}));
dialog.present();
dialog.present(&obj);
}));
log::trace!("Setting up unlink action");
let action_unlink = SimpleAction::new("unlink", None);
action_unlink.connect_activate(clone!(@weak obj => move |_, _| {
log::trace!("User requested to unlink the device");
let builder = Builder::from_resource("/ui/dialog_unlink.ui");
let dialog: MessageDialog = builder
let dialog: AlertDialog = builder
.object("dialog")
.expect("dialog_unlink.ui to have at least one object dialog");
dialog.set_transient_for(Some(&obj));
dialog.connect_response(None, clone!(@weak obj => move |_dialog, response| {
if response == "unlink-keep" {
log::info!("Unlinking device");
@@ -214,7 +211,7 @@ pub mod imp {
obj.kill();
}
}));
dialog.present();
dialog.present(&obj);
}));
log::trace!("Setting up submit-captcha action");
@@ -222,7 +219,7 @@ pub mod imp {
action_submit_captcha.connect_activate(clone!(@weak obj => move |_, _| {
log::trace!("User requested to submit a captcha the device");
let builder = Builder::from_resource("/ui/submit_captcha_dialog.ui");
let dialog: MessageDialog = builder
let dialog: AlertDialog = builder
.object("dialog")
.expect("submit_captcha_dialog.ui to have at least one object dialog");
let entry_token: EntryRow = builder
@@ -231,7 +228,6 @@ pub mod imp {
let entry_captcha: EntryRow = builder
.object("entry_captcha")
.expect("submit_captcha_dialog.ui to have at least one object entry_captcha");
dialog.set_transient_for(Some(&obj));
dialog.connect_response(None, clone!(@weak obj, @weak entry_token, @weak entry_captcha => move |_dialog, response| {
if response == "submit" {
log::info!("Unlinking device");
@@ -247,7 +243,7 @@ pub mod imp {
}
}
}));
dialog.present();
dialog.present(&obj);
}));
log::trace!("Setting up sync-contacts action");
let action_sync_contacts = SimpleAction::new("sync-contacts", None);
@@ -274,16 +270,16 @@ pub mod imp {
log::trace!("Setting up about-page action");
let action_about = SimpleAction::new("about", None);
action_about.connect_activate(|_, _| {
action_about.connect_activate(clone!(@weak obj => move |_, _| {
let builder = Builder::from_resource("/ui/about.ui");
let about: AboutWindow = builder
let about: AboutDialog = builder
.object("about")
.expect("about.ui to have at least one object about");
// TODO: Replace this in Blueprint when string[] is supported
about.set_artists(&["David Lapshin <[email protected]>"]);
about.add_link("GitLab", "https://gitlab.com/schmiddi-on-mobile/flare");
about.present();
});
about.present(&obj);
}));
log::trace!("Setting up kill action");
let action_kill = SimpleAction::new("kill", None);
@@ -296,16 +292,17 @@ pub mod imp {
action_channel_information.connect_activate(clone!(@weak obj => move |_, _| {
log::trace!("Requested channel info");
let Some(channel) = obj.imp().channel_messages.active_channel() else {return};
let channel_info = ChannelInfoDialog::new(&channel, &obj.manager(), &obj);
channel_info.present();
// TODO: To AlertDialog
let channel_info = ChannelInfoDialog::new(&channel, &obj.manager());
channel_info.present(&obj);
}));
log::trace!("Setting up channel clear messages action");
let action_channel_clear_messages = SimpleAction::new("channel-clear-messages", None);
action_channel_clear_messages.connect_activate(clone!(@weak obj => move |_, _| {
log::trace!("Requested clearing messages of channels");
let confirmation_dialog = MessageDialog::builder()
.transient_for(&obj)
// TODO: To AlertDialog
let confirmation_dialog = AlertDialog::builder()
.heading(gettextrs::gettext("Remove Messages"))
.body(gettextrs::gettext("This will remove all locally stored messages from this channel"))
.close_response("cancel")
@@ -314,11 +311,11 @@ pub mod imp {
confirmation_dialog.add_response("cancel", &gettextrs::gettext("Cancel"));
confirmation_dialog.add_response("remove", &gettextrs::gettext("Remove Messages"));
confirmation_dialog.set_response_appearance("remove", ResponseAppearance::Destructive);
confirmation_dialog.choose(None::<&Cancellable>, clone!(@weak obj => move |response| {
confirmation_dialog.choose(&obj, None::<&Cancellable>, clone!(@weak obj => move |response| {
if response == "remove" {
if let Err(e) = obj.imp().channel_messages.clear_messages() {
let dialog = ErrorDialog::new(e, &obj);
dialog.present();
dialog.present(&obj);
}
}
}));
@@ -399,7 +396,7 @@ pub mod imp {
action_linked_devices.connect_activate(clone!(@weak obj => move |_, _| {
log::trace!("User requested to view linked devices");
let win = LinkedDevicesWindow::new(obj.manager(), &obj);
win.present();
win.present(&obj);
}));
self.obj()
@@ -520,7 +517,7 @@ pub mod imp {
if let Err(e) = manager.init(&path).await {
let dialog = ErrorDialog::new(e, &obj);
dialog.present();
dialog.present(&obj);
}
}));
}