From 9da2b030d7e54bfcc7bea0d37e3688c3f0266fa5 Mon Sep 17 00:00:00 2001 From: Lucy Date: Wed, 2 Feb 2022 11:05:36 -0500 Subject: [PATCH] Port `SettingsEntry` from cosmic-settings --- applets/cosmic-applet-network/src/main.rs | 1 + .../cosmic-applet-network/src/ui/toggles.rs | 25 +- applets/cosmic-applet-network/src/widgets.rs | 5 + .../src/widgets/settings_entry.rs | 214 ++++++++++++++++++ 4 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 applets/cosmic-applet-network/src/widgets.rs create mode 100644 applets/cosmic-applet-network/src/widgets/settings_entry.rs diff --git a/applets/cosmic-applet-network/src/main.rs b/applets/cosmic-applet-network/src/main.rs index bb608f9e..17c44462 100644 --- a/applets/cosmic-applet-network/src/main.rs +++ b/applets/cosmic-applet-network/src/main.rs @@ -5,6 +5,7 @@ extern crate relm4_macros; pub mod task; pub mod ui; +pub mod widgets; use gtk4::{gio::ApplicationFlags, prelude::*, Orientation}; use once_cell::sync::Lazy; diff --git a/applets/cosmic-applet-network/src/ui/toggles.rs b/applets/cosmic-applet-network/src/ui/toggles.rs index 2f84118d..d68bf0cf 100644 --- a/applets/cosmic-applet-network/src/ui/toggles.rs +++ b/applets/cosmic-applet-network/src/ui/toggles.rs @@ -1,27 +1,22 @@ -use gtk4::{prelude::*, Align, Label, Orientation, Separator, Switch}; +use crate::widgets::SettingsEntry; +use gtk4::{prelude::*, Orientation, Separator, Switch}; pub fn add_toggles(target: >k4::Box) { view! { - airplane_mode_box = gtk4::Box { - append: airplane_mode_label = &Label { - set_markup: "Airplane Mode", - set_halign: Align::Start - }, - append: airplane_mode_switch = &Switch {} + airplane_mode = SettingsEntry { + set_title_markup: "Airplane Mode", + set_child: airplane_mode_switch = &Switch {} } } view! { - wifi_box = gtk4::Box { - append: wifi_label = &Label { - set_markup: "WiFi", - set_halign: Align::Start - }, - append: wifi_switch = &Switch {} + wifi = SettingsEntry { + set_title_markup: "WiFi", + set_child: wifi_switch = &Switch {} } } - target.append(&airplane_mode_box); + target.append(&airplane_mode); target.append(&Separator::new(Orientation::Horizontal)); - target.append(&wifi_box); + target.append(&wifi); target.append(&Separator::new(Orientation::Horizontal)); } diff --git a/applets/cosmic-applet-network/src/widgets.rs b/applets/cosmic-applet-network/src/widgets.rs new file mode 100644 index 00000000..a92fff62 --- /dev/null +++ b/applets/cosmic-applet-network/src/widgets.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +mod settings_entry; + +pub use settings_entry::SettingsEntry; diff --git a/applets/cosmic-applet-network/src/widgets/settings_entry.rs b/applets/cosmic-applet-network/src/widgets/settings_entry.rs new file mode 100644 index 00000000..f1793637 --- /dev/null +++ b/applets/cosmic-applet-network/src/widgets/settings_entry.rs @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +use gtk4::{ + glib::{self, Object}, + prelude::*, + subclass::prelude::*, + Label, +}; +use std::cell::RefCell; + +glib::wrapper! { + pub struct SettingsEntry(ObjectSubclass) + @extends gtk4::Widget, + @implements gtk4::Accessible; +} + +impl SettingsEntry { + pub fn new() -> Self { + Self::default() + } + + pub fn set_child<'a, Widget, IntoWidget>(&self, child: IntoWidget) + where + Widget: IsA, + IntoWidget: Into>, + { + let imp = self.inner(); + let child = child.into().map(AsRef::as_ref); + let child_box_ref = imp.child_box.borrow(); + let child_box: >k4::Box = child_box_ref.as_ref().expect("child_box not created??"); + if let Some(new_child) = child { + new_child.set_halign(gtk4::Align::End); + child_box.append(new_child); + } + if let Some(old_child) = imp.child.replace(child.cloned()) { + child_box.remove(&old_child); + } + } + + pub fn set_child_label>(&self, label: A) { + let label = label.as_ref(); + let child = Label::builder() + .label(label) + .css_classes(vec!["settings-entry-text".into()]) + .build(); + self.set_child(&child); + } + + pub fn align_child(&self, alignment: gtk4::Align) { + let imp = self.inner(); + let child_box = imp.child_box.borrow(); + let child_box = child_box.as_ref().expect("child_box not created??"); + let child = imp.child.borrow(); + let child = child.as_ref().expect("child not set"); + let title_desc_box = imp.title_desc_box.borrow(); + let title_desc_box = title_desc_box + .as_ref() + .expect("title_desc_box not created?"); + match alignment { + gtk4::Align::Start => { + child_box.reorder_child_after(title_desc_box, Some(child)); + } + gtk4::Align::End => { + child_box.reorder_child_after(child, Some(title_desc_box)); + } + _ => unimplemented!(), + } + } + + pub fn set_title(&self, title: &str) { + let inner = self.inner(); + let title_ref = inner.title.borrow_mut(); + match &*title_ref { + Some(label) => label.set_label(title), + None => { + let title = gtk4::Label::builder() + .label(title) + .css_classes(vec!["settings-entry-title".into()]) + .halign(gtk4::Align::Start) + .build(); + let title_desc_box = inner.title_desc_box.borrow(); + let title_desc_box = title_desc_box + .as_ref() + .expect("title_desc_box not created?"); + if inner.desc.borrow().is_some() { + title_desc_box.prepend(&title); + } else { + title_desc_box.append(&title); + } + } + } + } + + pub fn set_title_markup(&self, title: &str) { + let inner = self.inner(); + let title_ref = inner.title.borrow_mut(); + match &*title_ref { + Some(label) => label.set_markup(title), + None => { + let title = gtk4::Label::builder() + .label(title) + .use_markup(true) + .css_classes(vec!["settings-entry-title".into()]) + .halign(gtk4::Align::Start) + .build(); + let title_desc_box = inner.title_desc_box.borrow(); + let title_desc_box = title_desc_box + .as_ref() + .expect("title_desc_box not created?"); + if inner.desc.borrow().is_some() { + title_desc_box.prepend(&title); + } else { + title_desc_box.append(&title); + } + } + } + } + + pub fn set_description(&self, description: &str) { + let inner = self.inner(); + let desc_ref = inner.desc.borrow_mut(); + match &*desc_ref { + Some(label) => label.set_label(description), + None => { + let desc = gtk4::Label::builder() + .label(description) + .css_classes(vec!["settings-entry-desc".into()]) + .halign(gtk4::Align::Start) + .build(); + let title_desc_box = inner.title_desc_box.borrow(); + let title_desc_box = title_desc_box + .as_ref() + .expect("title_desc_box not created?"); + title_desc_box.append(&desc); + } + } + } + + fn inner(&self) -> &SettingsEntryImp { + SettingsEntryImp::from_instance(self) + } +} + +impl Default for SettingsEntry { + fn default() -> Self { + Object::new(&[]).expect("Failed to create `SettingsEntry`.") + } +} + +#[derive(Debug, Default)] +pub struct SettingsEntryImp { + title: RefCell>, + desc: RefCell>, + title_desc_box: RefCell>, + child_box: RefCell>, + child: RefCell>, +} + +#[glib::object_subclass] +impl ObjectSubclass for SettingsEntryImp { + const NAME: &'static str = "SettingsEntry"; + type Type = SettingsEntry; + type ParentType = gtk4::Widget; + type Interfaces = (); + type Instance = glib::subclass::basic::InstanceStruct; + type Class = glib::subclass::basic::ClassStruct; + + fn class_init(klass: &mut Self::Class) { + // The layout manager determines how child widgets are laid out. + klass.set_layout_manager_type::(); + } +} + +impl ObjectImpl for SettingsEntryImp { + fn constructed(&self, obj: &Self::Type) { + self.parent_constructed(obj); + let child = gtk4::Box::builder() + .css_classes(vec!["settings-entry".into()]) + .orientation(gtk4::Orientation::Horizontal) + .hexpand(true) + .margin_start(24) + .margin_end(24) + .margin_top(8) + .margin_bottom(8) + .spacing(16) + .build(); + + let title_and_desc = gtk4::Box::builder() + .css_classes(vec!["settings-entry-info".into()]) + .orientation(gtk4::Orientation::Vertical) + .spacing(4) + .hexpand(true) + .valign(gtk4::Align::Center) + .build(); + child.append(&title_and_desc); + *self.title_desc_box.borrow_mut() = Some(title_and_desc); + if let Some(entry_child) = self.child.borrow().as_ref() { + child.append(entry_child); + } + child.set_parent(obj); + *self.child_box.borrow_mut() = Some(child); + } + + fn dispose(&self, _obj: &Self::Type) { + if let Some(child) = self.child.borrow_mut().take() { + child.unparent(); + } + if let Some(child_box) = self.child_box.borrow_mut().take() { + child_box.unparent(); + } + } +} + +impl WidgetImpl for SettingsEntryImp {}