replace hashmap index with custom gobject properties impl

This commit is contained in:
yggverse 2024-11-18 22:16:20 +02:00
parent 5d647518c0
commit 46a25306e1
4 changed files with 114 additions and 26 deletions

View File

@ -23,7 +23,7 @@ impl Gemini {
let widget = Rc::new(Widget::new());
// Add new identity option
widget.form.list.append(None, "Create new..");
widget.form.list.append(None, "Create new..", true);
// Collect additional options from database
match profile.identity.gemini.database.records() {
@ -49,6 +49,7 @@ impl Gemini {
Some(name) => format!("{name} ({expires})"),
None => format!("{expires}"),
},
true,
);
}
}

View File

@ -1,10 +1,12 @@
use gtk::{gio::ListStore, DropDown, Label};
use std::{cell::RefCell, collections::HashMap, rc::Rc};
mod item;
use item::Item;
use gio::prelude::{Cast, CastNone};
use gtk::{gio::ListStore, prelude::ListItemExt, DropDown, Label, ListItem, SignalListItemFactory};
pub struct List {
pub gobject: DropDown,
model: ListStore,
index: Rc<RefCell<HashMap<Label, Option<i64>>>>,
}
impl List {
@ -12,31 +14,40 @@ impl List {
/// Create new `Self`
pub fn new() -> Self {
let index = Rc::new(RefCell::new(HashMap::new()));
let model = ListStore::new::<Label>();
let gobject = DropDown::builder().model(&model).build();
// Init model with custom `GObject` properties
let model = ListStore::new::<Item>();
Self {
model,
index,
gobject,
}
// Setup item factory to append items after `DropDown` init
let factory = SignalListItemFactory::new();
// @TODO factory.connect_setup(move |_, gobject| {});
factory.connect_bind(move |_, gobject| {
// Cast components
let list_item = gobject.downcast_ref::<ListItem>().unwrap();
let item = list_item.item().and_downcast::<Item>().unwrap();
// Update menu item
list_item.set_child(Some(&Label::new(Some(&item.label()))));
// @TODO
println!("{:?}", item.profile_identity_gemini_id_option());
println!("{:?}", item.label());
println!("{:?}", item.is_enabled());
});
// Init list `GObject`
let gobject = DropDown::builder().model(&model).factory(&factory).build();
// Return activated `Self`
Self { model, gobject }
}
// Actions
/// Append new item with `profile_identity_gemini_id` as `key` and label as `value`
pub fn append(&self, profile_identity_gemini_id: Option<i64>, label: &str) {
// Create new label for item
let item = Label::new(Some(label));
// Append formatted record
self.model.append(&item);
// Register ID in hash map index
self.index
.borrow_mut()
.insert(item, profile_identity_gemini_id);
pub fn append(&self, profile_identity_gemini_id: Option<i64>, label: &str, is_enabled: bool) {
self.model
.append(&Item::new(profile_identity_gemini_id, label, is_enabled));
}
// Events
@ -44,9 +55,13 @@ impl List {
/// Run callback function on `connect_selected_notify` event
/// * return formatted `profile_identity_gemini_id` match selected
pub fn connect_selected_notify(&self, callback: impl Fn(Option<i64>) + 'static) {
self.gobject.connect_selected_notify({
let index = self.index.clone();
move |list| callback(*index.borrow().get(&list.selected_item().unwrap()).unwrap())
self.gobject.connect_selected_notify(move |list| {
callback(
list.selected_item()
.and_downcast::<Item>()
.unwrap()
.profile_identity_gemini_id_option(),
)
});
}
}

View File

@ -0,0 +1,38 @@
mod imp;
use gtk::glib::{self, Object};
glib::wrapper! {
pub struct Item(ObjectSubclass<imp::Item>);
}
// C-based conversion for `None` value
const PROFILE_IDENTITY_GEMINI_ID_NONE: i64 = -1;
impl Item {
// Constructors
/// Create new `GObject` with formatted properties
pub fn new(profile_identity_gemini_id: Option<i64>, label: &str, is_enabled: bool) -> Self {
Object::builder()
.property(
"profile_identity_gemini_id",
match profile_identity_gemini_id {
Some(value) => value,
None => PROFILE_IDENTITY_GEMINI_ID_NONE,
},
)
.property("label", label)
.property("is_enabled", is_enabled)
.build()
}
// Getters
/// Additional `profile_identity_gemini_id` wrapper with `Option` value support
pub fn profile_identity_gemini_id_option(&self) -> Option<i64> {
match self.profile_identity_gemini_id() {
PROFILE_IDENTITY_GEMINI_ID_NONE => None,
value => Some(value),
}
}
}

View File

@ -0,0 +1,34 @@
//! Custom `GObject` implementation for dropdown
//! [ListStore](https://docs.gtk.org/gio/class.ListStore.html) menu item
use gtk::{
gio::subclass::prelude::{DerivedObjectProperties, ObjectImpl, ObjectImplExt, ObjectSubclass},
glib::{self, Object, Properties},
prelude::ObjectExt,
};
use std::cell::{Cell, RefCell};
#[derive(Properties, Default)]
#[properties(wrapper_type = super::Item)]
pub struct Item {
#[property(get, set)]
profile_identity_gemini_id: Cell<i64>,
#[property(get, set)]
label: RefCell<String>,
#[property(get, set)]
is_enabled: Cell<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for Item {
const NAME: &str = "Item"; // @TODO make globally unique
type Type = super::Item;
type ParentType = Object;
}
#[glib::derived_properties]
impl ObjectImpl for Item {
fn constructed(&self) {
self.parent_constructed();
}
}