Browse Source

Move sidebar_data::Selection to utils as FixedSelection

To be able to reuse it.
fractal-12
Kévin Commaille 8 months ago
parent
commit
4584c0b5ec
No known key found for this signature in database
GPG Key ID: F26F4BE20A08255B
  1. 4
      src/session/model/mod.rs
  2. 6
      src/session/model/sidebar_data/list_model.rs
  3. 2
      src/session/model/sidebar_data/mod.rs
  4. 8
      src/session/view/sidebar/mod.rs
  5. 200
      src/utils/fixed_selection.rs
  6. 2
      src/utils/mod.rs

4
src/session/model/mod.rs

@ -25,8 +25,8 @@ pub(crate) use self::{
session::*,
session_settings::*,
sidebar_data::{
Selection, SidebarIconItem, SidebarIconItemType, SidebarItemList, SidebarListModel,
SidebarSection, SidebarSectionName,
SidebarIconItem, SidebarIconItemType, SidebarItemList, SidebarListModel, SidebarSection,
SidebarSectionName,
},
user::{User, UserExt},
user_sessions_list::{UserSession, UserSessionsList},

6
src/session/model/sidebar_data/list_model.rs

@ -1,9 +1,9 @@
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
use super::{Selection, SidebarItemList};
use super::SidebarItemList;
use crate::{
session::model::{IdentityVerification, Room},
utils::{BoundObjectWeakRef, expression},
utils::{BoundObjectWeakRef, FixedSelection, expression},
};
mod imp {
@ -25,7 +25,7 @@ mod imp {
pub is_filtered: Cell<bool>,
/// The selection model.
#[property(get)]
pub selection_model: Selection,
pub selection_model: FixedSelection,
/// The selected item, if it has signal handlers.
pub selected_item: BoundObjectWeakRef<glib::Object>,
item_type_filter: gtk::CustomFilter,

2
src/session/model/sidebar_data/mod.rs

@ -3,7 +3,6 @@ mod item;
mod item_list;
mod list_model;
mod section;
mod selection;
pub use self::{
icon_item::{SidebarIconItem, SidebarIconItemType},
@ -11,5 +10,4 @@ pub use self::{
item_list::SidebarItemList,
list_model::SidebarListModel,
section::{SidebarSection, SidebarSectionName},
selection::Selection,
};

8
src/session/view/sidebar/mod.rs

@ -21,10 +21,10 @@ use crate::{
account_switcher::AccountSwitcherButton,
components::OfflineBanner,
session::model::{
CryptoIdentityState, RecoveryState, RoomCategory, Selection, Session,
SessionVerificationState, SidebarListModel, SidebarSection, TargetRoomCategory, User,
CryptoIdentityState, RecoveryState, RoomCategory, Session, SessionVerificationState,
SidebarListModel, SidebarSection, TargetRoomCategory, User,
},
utils::expression,
utils::{FixedSelection, expression},
};
mod imp {
@ -126,7 +126,7 @@ mod imp {
self.listview.set_factory(Some(&factory));
self.listview.connect_activate(move |listview, pos| {
let Some(model) = listview.model().and_downcast::<Selection>() else {
let Some(model) = listview.model().and_downcast::<FixedSelection>() else {
return;
};
let Some(item) = model.item(pos) else {

200
src/session/model/sidebar_data/selection.rs → src/utils/fixed_selection.rs

@ -8,20 +8,20 @@ mod imp {
use super::*;
#[derive(Debug, glib::Properties)]
#[properties(wrapper_type = super::Selection)]
pub struct Selection {
#[properties(wrapper_type = super::FixedSelection)]
pub struct FixedSelection {
/// The underlying model.
#[property(get, set = Self::set_model, explicit_notify, nullable)]
pub model: BoundObject<gio::ListModel>,
model: BoundObject<gio::ListModel>,
/// The position of the selected item.
#[property(get, set = Self::set_selected, explicit_notify, default = gtk::INVALID_LIST_POSITION)]
pub selected: Cell<u32>,
selected: Cell<u32>,
/// The selected item.
#[property(get, set = Self::set_selected_item, explicit_notify, nullable)]
pub selected_item: RefCell<Option<glib::Object>>,
selected_item: RefCell<Option<glib::Object>>,
}
impl Default for Selection {
impl Default for FixedSelection {
fn default() -> Self {
Self {
model: Default::default(),
@ -32,16 +32,16 @@ mod imp {
}
#[glib::object_subclass]
impl ObjectSubclass for Selection {
const NAME: &'static str = "SidebarSelection";
type Type = super::Selection;
impl ObjectSubclass for FixedSelection {
const NAME: &'static str = "FixedSelection";
type Type = super::FixedSelection;
type Interfaces = (gio::ListModel, gtk::SelectionModel);
}
#[glib::derived_properties]
impl ObjectImpl for Selection {}
impl ObjectImpl for FixedSelection {}
impl ListModelImpl for Selection {
impl ListModelImpl for FixedSelection {
fn item_type(&self) -> glib::Type {
glib::Object::static_type()
}
@ -55,7 +55,7 @@ mod imp {
}
}
impl SelectionModelImpl for Selection {
impl SelectionModelImpl for FixedSelection {
fn selection_in_range(&self, _position: u32, _n_items: u32) -> gtk::Bitset {
let bitset = gtk::Bitset::new_empty();
let selected = self.selected.get();
@ -72,44 +72,52 @@ mod imp {
}
}
impl Selection {
impl FixedSelection {
/// Set the underlying model.
fn set_model(&self, model: Option<gio::ListModel>) {
let obj = self.obj();
let _guard = obj.freeze_notify();
let model = model.map(|m| m.clone().upcast());
let prev_model = self.model.obj();
let old_model = self.model.obj();
if old_model == model {
if prev_model == model {
return;
}
let n_items_before = old_model.map_or(0, |model| model.n_items());
let prev_n_items = prev_model
.as_ref()
.map(ListModelExt::n_items)
.unwrap_or_default();
let n_items = model
.as_ref()
.map(ListModelExt::n_items)
.unwrap_or_default();
self.model.disconnect_signals();
let obj = self.obj();
let _guard = obj.freeze_notify();
if let Some(model) = model {
let items_changed_handler = model.connect_items_changed(clone!(
#[weak]
obj,
#[weak(rename_to = imp)]
self,
move |m, p, r, a| {
obj.items_changed_cb(m, p, r, a);
imp.items_changed_cb(m, p, r, a);
}
));
self.model.set(model.clone(), vec![items_changed_handler]);
obj.items_changed_cb(&model, 0, n_items_before, model.n_items());
} else {
if self.selected.get() != gtk::INVALID_LIST_POSITION {
self.selected.replace(gtk::INVALID_LIST_POSITION);
obj.notify_selected();
}
if self.selected_item.borrow().is_some() {
self.selected_item.replace(None);
obj.notify_selected_item();
}
self.model.set(model, vec![items_changed_handler]);
}
if self.selected.get() != gtk::INVALID_LIST_POSITION {
self.selected.replace(gtk::INVALID_LIST_POSITION);
obj.notify_selected();
}
if self.selected_item.borrow().is_some() {
self.selected_item.replace(None);
obj.notify_selected_item();
}
obj.items_changed(0, n_items_before, 0);
if prev_n_items > 0 || n_items > 0 {
obj.items_changed(0, prev_n_items, n_items);
}
obj.notify_model();
@ -117,8 +125,8 @@ mod imp {
/// Set the selected item by its position.
fn set_selected(&self, position: u32) {
let old_selected = self.selected.get();
if old_selected == position {
let prev_selected = self.selected.get();
if prev_selected == position {
return;
}
@ -130,7 +138,7 @@ mod imp {
position
};
if old_selected == selected {
if prev_selected == selected {
return;
}
let obj = self.obj();
@ -138,14 +146,14 @@ mod imp {
self.selected.replace(selected);
self.selected_item.replace(selected_item);
if old_selected == gtk::INVALID_LIST_POSITION {
if prev_selected == gtk::INVALID_LIST_POSITION {
obj.selection_changed(selected, 1);
} else if selected == gtk::INVALID_LIST_POSITION {
obj.selection_changed(old_selected, 1);
} else if selected < old_selected {
obj.selection_changed(selected, old_selected - selected + 1);
obj.selection_changed(prev_selected, 1);
} else if selected < prev_selected {
obj.selection_changed(selected, prev_selected - selected + 1);
} else {
obj.selection_changed(old_selected, selected - old_selected + 1);
obj.selection_changed(prev_selected, selected - prev_selected + 1);
}
obj.notify_selected();
@ -159,7 +167,7 @@ mod imp {
}
let obj = self.obj();
let old_selected = self.selected.get();
let prev_selected = self.selected.get();
let mut selected = gtk::INVALID_LIST_POSITION;
if item.is_some() {
@ -176,77 +184,89 @@ mod imp {
self.selected_item.replace(item);
if old_selected != selected {
if prev_selected != selected {
self.selected.replace(selected);
if old_selected == gtk::INVALID_LIST_POSITION {
if prev_selected == gtk::INVALID_LIST_POSITION {
obj.selection_changed(selected, 1);
} else if selected == gtk::INVALID_LIST_POSITION {
obj.selection_changed(old_selected, 1);
} else if selected < old_selected {
obj.selection_changed(selected, old_selected - selected + 1);
obj.selection_changed(prev_selected, 1);
} else if selected < prev_selected {
obj.selection_changed(selected, prev_selected - selected + 1);
} else {
obj.selection_changed(old_selected, selected - old_selected + 1);
obj.selection_changed(prev_selected, selected - prev_selected + 1);
}
obj.notify_selected();
}
obj.notify_selected_item();
}
}
}
glib::wrapper! {
/// A `GtkSelectionModel` that keeps track of the selected item even if its position changes or it is removed from the list.
pub struct Selection(ObjectSubclass<imp::Selection>)
@implements gio::ListModel, gtk::SelectionModel;
}
/// Handle when items changed in the underlying model.
fn items_changed_cb(
&self,
model: &gio::ListModel,
position: u32,
removed: u32,
added: u32,
) {
let obj = self.obj();
let _guard = obj.freeze_notify();
impl Selection {
pub fn new<P: IsA<gio::ListModel>>(model: Option<&P>) -> Selection {
let model = model.map(|m| m.clone().upcast());
glib::Object::builder().property("model", &model).build()
}
let selected = self.selected.get();
let selected_item = self.selected_item.borrow().clone();
if selected_item.is_none() || selected < position {
// unchanged
} else if selected != gtk::INVALID_LIST_POSITION && selected >= position + removed {
self.selected.set(selected + added - removed);
obj.notify_selected();
} else {
let mut found = false;
for i in position..(position + added) {
let item = model.item(i);
fn items_changed_cb(&self, model: &gio::ListModel, position: u32, removed: u32, added: u32) {
let imp = self.imp();
let _guard = self.freeze_notify();
let selected = self.selected();
let selected_item = self.selected_item();
if selected_item.is_none() || selected < position {
// unchanged
} else if selected != gtk::INVALID_LIST_POSITION && selected >= position + removed {
imp.selected.replace(selected + added - removed);
self.notify_selected();
} else {
for i in 0..=added {
if i == added {
// the item really was deleted
imp.selected.replace(gtk::INVALID_LIST_POSITION);
self.notify_selected();
} else {
let item = model.item(position + i);
if item == selected_item {
// the item moved
if selected != position + i {
imp.selected.replace(position + i);
self.notify_selected();
if selected != i {
// The position of the item changed.
self.selected.set(i);
obj.notify_selected();
}
found = true;
break;
}
}
if !found {
// The item is no longer in the model.
self.selected.set(gtk::INVALID_LIST_POSITION);
obj.notify_selected();
}
}
obj.items_changed(position, removed, added);
}
}
}
glib::wrapper! {
/// A `GtkSelectionModel` that keeps track of the selected item even if its
/// position changes or it is removed from the list.
pub struct FixedSelection(ObjectSubclass<imp::FixedSelection>)
@implements gio::ListModel, gtk::SelectionModel;
}
self.items_changed(position, removed, added);
impl FixedSelection {
/// Construct a new `FixedSelection` with the given model.
pub fn new(model: Option<&impl IsA<gio::ListModel>>) -> Self {
glib::Object::builder().property("model", model).build()
}
}
impl Default for Selection {
impl Default for FixedSelection {
fn default() -> Self {
Self::new(gio::ListModel::NONE)
Self::new(None::<&gio::ListModel>)
}
}

2
src/utils/mod.rs

@ -18,6 +18,7 @@ use tokio::task::{AbortHandle, JoinHandle};
pub(crate) mod expression;
mod expression_list_model;
mod fixed_selection;
mod grouping_list_model;
pub(crate) mod key_bindings;
mod location;
@ -34,6 +35,7 @@ pub(crate) mod toast;
pub(crate) use self::{
expression_list_model::ExpressionListModel,
fixed_selection::FixedSelection,
grouping_list_model::*,
location::{Location, LocationError, LocationExt},
placeholder_object::PlaceholderObject,

Loading…
Cancel
Save