add a UrlRow widget & use it

just a wrapper around ActionRow that opens the uri when activated
This commit is contained in:
Bilal Elmoussaoui 2020-12-10 21:27:59 +01:00
parent e927a7bb6b
commit 6be97a815f
7 changed files with 150 additions and 57 deletions

View file

@ -138,32 +138,6 @@
<object class="GtkListBox" id="more_list"> <object class="GtkListBox" id="more_list">
<property name="visible">False</property> <property name="visible">False</property>
<property name="selection_mode">none</property> <property name="selection_mode">none</property>
<child>
<object class="HdyActionRow" id="provider_website_row">
<property name="title" translatable="yes">Website</property>
<child>
<object class="GtkImage">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="icon-name">link-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="HdyActionRow" id="provider_help_row">
<property name="title" translatable="yes">How to setup</property>
<child>
<object class="GtkImage">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="icon-name">help-page-symbolic</property>
</object>
</child>
</object>
</child>
<child> <child>
<object class="HdyActionRow" id="algorithm_row"> <object class="HdyActionRow" id="algorithm_row">
<property name="title" translatable="yes">Algorithm</property> <property name="title" translatable="yes">Algorithm</property>

View file

@ -33,7 +33,7 @@
<property name="margin-start">8</property> <property name="margin-start">8</property>
<property name="margin-end">8</property> <property name="margin-end">8</property>
<child> <child>
<object class="GtkListBox"> <object class="GtkListBox" id="list">
<property name="selection_mode">none</property> <property name="selection_mode">none</property>
<child> <child>
<object class="HdyActionRow"> <object class="HdyActionRow">
@ -55,19 +55,6 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="HdyActionRow" id="provider_website_row">
<property name="title" translatable="yes">Website</property>
<child>
<object class="GtkImage">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="icon-name">link-symbolic</property>
</object>
</child>
</object>
</child>
<style> <style>
<class name="content"/> <class name="content"/>
</style> </style>

View file

@ -73,6 +73,7 @@ sources = files(
'widgets/providers/page.rs', 'widgets/providers/page.rs',
'widgets/providers/row.rs', 'widgets/providers/row.rs',
'widgets/mod.rs', 'widgets/mod.rs',
'widgets/url_row.rs',
'widgets/window.rs', 'widgets/window.rs',
'application.rs', 'application.rs',
'config.rs', 'config.rs',

View file

@ -1,6 +1,6 @@
use crate::helpers::qrcode; use crate::helpers::qrcode;
use crate::models::{Account, OTPMethod, OTPUri, Provider, ProvidersModel}; use crate::models::{Account, OTPMethod, OTPUri, Provider, ProvidersModel};
use crate::widgets::{ProviderImage, ProviderImageSize}; use crate::widgets::{ProviderImage, ProviderImageSize, UrlRow};
use anyhow::Result; use anyhow::Result;
use gio::prelude::*; use gio::prelude::*;
use gio::{subclass::ObjectSubclass, ActionMapExt}; use gio::{subclass::ObjectSubclass, ActionMapExt};
@ -9,7 +9,6 @@ use glib::subclass::prelude::*;
use glib::{clone, glib_object_subclass, glib_wrapper}; use glib::{clone, glib_object_subclass, glib_wrapper};
use gtk::{prelude::*, CompositeTemplate}; use gtk::{prelude::*, CompositeTemplate};
use gtk_macros::{action, get_action}; use gtk_macros::{action, get_action};
use libhandy::ActionRowExt;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
mod imp { mod imp {
@ -24,6 +23,8 @@ mod imp {
pub selected_provider: RefCell<Option<Provider>>, pub selected_provider: RefCell<Option<Provider>>,
pub actions: gio::SimpleActionGroup, pub actions: gio::SimpleActionGroup,
pub image: ProviderImage, pub image: ProviderImage,
pub provider_website_row: UrlRow,
pub provider_help_row: UrlRow,
#[template_child] #[template_child]
pub main_container: TemplateChild<gtk::Box>, pub main_container: TemplateChild<gtk::Box>,
#[template_child] #[template_child]
@ -41,10 +42,6 @@ mod imp {
#[template_child] #[template_child]
pub method_label: TemplateChild<gtk::Label>, pub method_label: TemplateChild<gtk::Label>,
#[template_child] #[template_child]
pub provider_website_row: TemplateChild<libhandy::ActionRow>,
#[template_child]
pub provider_help_row: TemplateChild<libhandy::ActionRow>,
#[template_child]
pub algorithm_label: TemplateChild<gtk::Label>, pub algorithm_label: TemplateChild<gtk::Label>,
#[template_child] #[template_child]
pub counter_row: TemplateChild<libhandy::ActionRow>, pub counter_row: TemplateChild<libhandy::ActionRow>,
@ -65,10 +62,14 @@ mod imp {
fn new() -> Self { fn new() -> Self {
let actions = gio::SimpleActionGroup::new(); let actions = gio::SimpleActionGroup::new();
let provider_website_row = UrlRow::new("Website", "link-symbolic");
let provider_help_row = UrlRow::new("How to setup", "help-page-symbolic");
Self { Self {
actions, actions,
image: ProviderImage::new(ProviderImageSize::Large), image: ProviderImage::new(ProviderImageSize::Large),
provider_website_row,
provider_help_row,
model: OnceCell::new(), model: OnceCell::new(),
selected_provider: RefCell::new(None), selected_provider: RefCell::new(None),
main_container: TemplateChild::default(), main_container: TemplateChild::default(),
@ -79,8 +80,6 @@ mod imp {
digits_label: TemplateChild::default(), digits_label: TemplateChild::default(),
provider_entry: TemplateChild::default(), provider_entry: TemplateChild::default(),
method_label: TemplateChild::default(), method_label: TemplateChild::default(),
provider_website_row: TemplateChild::default(),
provider_help_row: TemplateChild::default(),
provider_completion: TemplateChild::default(), provider_completion: TemplateChild::default(),
algorithm_label: TemplateChild::default(), algorithm_label: TemplateChild::default(),
counter_row: TemplateChild::default(), counter_row: TemplateChild::default(),
@ -246,10 +245,10 @@ impl AccountAddDialog {
}; };
if let Some(ref website) = provider.website() { if let Some(ref website) = provider.website() {
self_.provider_website_row.get().set_subtitle(Some(website)); self_.provider_website_row.set_uri(website);
} }
if let Some(ref help_url) = provider.help_url() { if let Some(ref help_url) = provider.help_url() {
self_.provider_help_row.get().set_subtitle(Some(help_url)); self_.provider_help_row.set_uri(help_url);
} }
self_.selected_provider.borrow_mut().replace(provider); self_.selected_provider.borrow_mut().replace(provider);
} }
@ -291,6 +290,9 @@ impl AccountAddDialog {
.get() .get()
.set_model(Some(&self_.model.get().unwrap().completion_model())); .set_model(Some(&self_.model.get().unwrap().completion_model()));
self_.more_list.get().prepend(&self_.provider_help_row);
self_.more_list.get().prepend(&self_.provider_website_row);
self_.main_container.get().prepend(&self_.image); self_.main_container.get().prepend(&self_.image);
self_.provider_completion.get().connect_match_selected( self_.provider_completion.get().connect_match_selected(

View file

@ -1,9 +1,9 @@
use crate::models::Account; use crate::models::Account;
use crate::widgets::UrlRow;
use gio::{subclass::ObjectSubclass, FileExt}; use gio::{subclass::ObjectSubclass, FileExt};
use glib::subclass::prelude::*; use glib::subclass::prelude::*;
use glib::{glib_object_subclass, glib_wrapper}; use glib::{glib_object_subclass, glib_wrapper};
use gtk::{prelude::*, CompositeTemplate}; use gtk::{prelude::*, CompositeTemplate};
use libhandy::prelude::*;
mod imp { mod imp {
use super::*; use super::*;
@ -12,14 +12,15 @@ mod imp {
#[derive(Debug, CompositeTemplate)] #[derive(Debug, CompositeTemplate)]
pub struct QRCodePage { pub struct QRCodePage {
pub website_row: UrlRow,
#[template_child] #[template_child]
pub image: TemplateChild<gtk::Image>, pub image: TemplateChild<gtk::Image>,
#[template_child] #[template_child]
pub provider_label: TemplateChild<gtk::Label>, pub provider_label: TemplateChild<gtk::Label>,
#[template_child] #[template_child]
pub account_label: TemplateChild<gtk::Label>, pub account_label: TemplateChild<gtk::Label>,
#[template_child(id = "provider_website_row")] #[template_child(id = "list")]
pub website_row: TemplateChild<libhandy::ActionRow>, pub listbox: TemplateChild<gtk::ListBox>,
} }
impl ObjectSubclass for QRCodePage { impl ObjectSubclass for QRCodePage {
@ -36,7 +37,8 @@ mod imp {
image: TemplateChild::default(), image: TemplateChild::default(),
account_label: TemplateChild::default(), account_label: TemplateChild::default(),
provider_label: TemplateChild::default(), provider_label: TemplateChild::default(),
website_row: TemplateChild::default(), website_row: UrlRow::new("Website", "link-symbolic"),
listbox: TemplateChild::default(),
} }
} }
@ -51,6 +53,7 @@ mod imp {
impl ObjectImpl for QRCodePage { impl ObjectImpl for QRCodePage {
fn constructed(&self, obj: &Self::Type) { fn constructed(&self, obj: &Self::Type) {
obj.init_template(); obj.init_template();
obj.setup_widgets();
self.parent_constructed(obj); self.parent_constructed(obj);
} }
} }
@ -87,10 +90,16 @@ impl QRCodePage {
.set_text(&account.provider().name()); .set_text(&account.provider().name());
if let Some(ref website) = account.provider().website() { if let Some(ref website) = account.provider().website() {
self_.website_row.get().set_subtitle(Some(website)); self_.website_row.set_uri(website);
self_.website_row.get().show(); self_.website_row.show();
} else { } else {
self_.website_row.get().hide(); self_.website_row.hide();
} }
} }
fn setup_widgets(&self) {
let self_ = imp::QRCodePage::from_instance(self);
self_.listbox.get().append(&self_.website_row);
}
} }

View file

@ -1,9 +1,11 @@
mod accounts; mod accounts;
mod preferences; mod preferences;
mod providers; mod providers;
mod url_row;
mod window; mod window;
pub use self::accounts::AccountAddDialog; pub use self::accounts::AccountAddDialog;
pub use self::preferences::PreferencesWindow; pub use self::preferences::PreferencesWindow;
pub use self::providers::{ProviderImage, ProviderImageSize, ProvidersDialog, ProvidersList}; pub use self::providers::{ProviderImage, ProviderImageSize, ProvidersDialog, ProvidersList};
pub use self::url_row::UrlRow;
pub use self::window::{View, Window}; pub use self::window::{View, Window};

118
src/widgets/url_row.rs Normal file
View file

@ -0,0 +1,118 @@
use gio::subclass::ObjectSubclass;
use glib::{clone, glib_wrapper, Cast, ObjectExt, StaticType, ToValue};
use gtk::WidgetExt;
use libhandy::ActionRowExt;
mod imp {
use super::*;
use glib::{glib_object_subclass, subclass};
use gtk::subclass::prelude::*;
use libhandy::subclass::action_row::ActionRowImpl;
use std::cell::RefCell;
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("uri", |name| {
glib::ParamSpec::string(
name,
"uri",
"The Row URI",
None,
glib::ParamFlags::READWRITE,
)
})];
pub struct UrlRow {
pub uri: RefCell<Option<String>>,
}
impl ObjectSubclass for UrlRow {
const NAME: &'static str = "UrlRow";
type Type = super::UrlRow;
type ParentType = libhandy::ActionRow;
type Instance = subclass::simple::InstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn new() -> Self {
Self {
uri: RefCell::new(None),
}
}
fn class_init(klass: &mut Self::Class) {
klass.install_properties(&PROPERTIES);
}
}
impl ObjectImpl for UrlRow {
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id];
match *prop {
subclass::Property("uri", ..) => {
let uri = value.get().unwrap();
self.uri.replace(uri);
}
_ => unimplemented!(),
}
}
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
let prop = &PROPERTIES[id];
match *prop {
subclass::Property("uri", ..) => self.uri.borrow().to_value(),
_ => unimplemented!(),
}
}
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
}
}
impl WidgetImpl for UrlRow {}
impl ListBoxRowImpl for UrlRow {}
impl ActionRowImpl for UrlRow {}
}
glib_wrapper! {
pub struct UrlRow(ObjectSubclass<imp::UrlRow>) @extends gtk::Widget, gtk::ListBoxRow, libhandy::ActionRow;
}
impl UrlRow {
#[allow(clippy::new_without_default)]
pub fn new(title: &str, icon_name: &str) -> Self {
let row = glib::Object::new(Self::static_type(), &[])
.expect("Failed to create UrlRow")
.downcast::<UrlRow>()
.expect("Created object is of wrong type");
row.setup_widgets(title, icon_name);
row
}
fn setup_widgets(&self, title: &str, icon_name: &str) {
self.set_property("title", &title).unwrap();
let gesture = gtk::GestureClick::new();
gesture.connect_pressed(clone!(@weak self as row => move |_,_,_,_| {
row.open_uri();
}));
self.add_controller(&gesture);
let image = gtk::Image::from_icon_name(Some(icon_name));
self.add_suffix(&image);
}
fn open_uri(&self) {
let self_ = imp::UrlRow::from_instance(self);
if let Some(ref uri) = *self_.uri.borrow() {
gtk::show_uri(gtk::NONE_WINDOW, uri, 0);
}
}
pub fn set_uri(&self, uri: &str) {
self.set_subtitle(Some(uri));
let self_ = imp::UrlRow::from_instance(self);
self_.uri.borrow_mut().replace(uri.to_string());
}
}