explore: Port to glib::Properties macro
This commit is contained in:
parent
bffd9463f7
commit
f366b02137
|
@ -19,22 +19,20 @@ use crate::{components::Spinner, session::model::Session};
|
|||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::{object::WeakRef, subclass::InitializingObject};
|
||||
use once_cell::sync::Lazy;
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(resource = "/org/gnome/Fractal/ui/session/view/content/explore/mod.ui")]
|
||||
#[properties(wrapper_type = super::Explore)]
|
||||
pub struct Explore {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set = Self::set_session, explicit_notify)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
#[template_child]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
pub spinner: TemplateChild<Spinner>,
|
||||
#[template_child]
|
||||
pub empty_label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub search_entry: TemplateChild<gtk::SearchEntry>,
|
||||
#[template_child]
|
||||
pub servers_button: TemplateChild<gtk::MenuButton>,
|
||||
|
@ -57,7 +55,10 @@ mod imp {
|
|||
PublicRoom::static_type();
|
||||
PublicRoomList::static_type();
|
||||
PublicRoomRow::static_type();
|
||||
Spinner::static_type();
|
||||
|
||||
Self::bind_template(klass);
|
||||
|
||||
klass.set_accessible_role(gtk::AccessibleRole::Group);
|
||||
}
|
||||
|
||||
|
@ -66,35 +67,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for Explore {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecObject::builder::<Session>("session")
|
||||
.explicit_notify()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
@ -116,7 +90,7 @@ mod imp {
|
|||
self.servers_popover.connect_selected_server_changed(
|
||||
clone!(@weak obj => move |_, server| {
|
||||
if let Some(server) = server {
|
||||
obj.imp().servers_button.set_label(server.name());
|
||||
obj.imp().servers_button.set_label(&server.name());
|
||||
obj.trigger_search();
|
||||
}
|
||||
}),
|
||||
|
@ -126,9 +100,46 @@ mod imp {
|
|||
|
||||
impl WidgetImpl for Explore {}
|
||||
impl BinImpl for Explore {}
|
||||
|
||||
impl Explore {
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Option<Session>) {
|
||||
if session == self.session.upgrade() {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
if let Some(session) = &session {
|
||||
let public_room_list = PublicRoomList::new(session);
|
||||
self.listview
|
||||
.set_model(Some(>k::NoSelection::new(Some(public_room_list.clone()))));
|
||||
|
||||
public_room_list.connect_notify_local(
|
||||
Some("loading"),
|
||||
clone!(@weak obj => move |_, _| {
|
||||
obj.update_visible_child();
|
||||
}),
|
||||
);
|
||||
|
||||
public_room_list.connect_notify_local(
|
||||
Some("empty"),
|
||||
clone!(@weak obj => move |_, _| {
|
||||
obj.update_visible_child();
|
||||
}),
|
||||
);
|
||||
|
||||
self.public_room_list.replace(Some(public_room_list));
|
||||
obj.update_visible_child();
|
||||
}
|
||||
|
||||
self.session.set(session.as_ref());
|
||||
obj.notify_session();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A view to explore rooms in the public directory of homeservers.
|
||||
pub struct Explore(ObjectSubclass<imp::Explore>)
|
||||
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
|
||||
}
|
||||
|
@ -138,18 +149,13 @@ impl Explore {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
pub fn init(&self) {
|
||||
let imp = self.imp();
|
||||
|
||||
imp.servers_popover.init();
|
||||
|
||||
if let Some(server) = imp.servers_popover.selected_server() {
|
||||
imp.servers_button.set_label(server.name());
|
||||
imp.servers_button.set_label(&server.name());
|
||||
}
|
||||
|
||||
if let Some(public_room_list) = &*imp.public_room_list.borrow() {
|
||||
|
@ -159,49 +165,16 @@ impl Explore {
|
|||
self.imp().search_entry.grab_focus();
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if session == self.session() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(ref session) = session {
|
||||
let public_room_list = PublicRoomList::new(session);
|
||||
imp.listview
|
||||
.set_model(Some(>k::NoSelection::new(Some(public_room_list.clone()))));
|
||||
|
||||
public_room_list.connect_notify_local(
|
||||
Some("loading"),
|
||||
clone!(@weak self as obj => move |_, _| {
|
||||
obj.set_visible_child();
|
||||
}),
|
||||
);
|
||||
|
||||
public_room_list.connect_notify_local(
|
||||
Some("empty"),
|
||||
clone!(@weak self as obj => move |_, _| {
|
||||
obj.set_visible_child();
|
||||
}),
|
||||
);
|
||||
|
||||
imp.public_room_list.replace(Some(public_room_list));
|
||||
}
|
||||
|
||||
imp.session.set(session.as_ref());
|
||||
self.notify("session");
|
||||
}
|
||||
|
||||
fn set_visible_child(&self) {
|
||||
/// Update the visible child according to the current state.
|
||||
fn update_visible_child(&self) {
|
||||
let imp = self.imp();
|
||||
if let Some(public_room_list) = &*imp.public_room_list.borrow() {
|
||||
if public_room_list.loading() {
|
||||
imp.stack.set_visible_child(&*imp.spinner);
|
||||
imp.stack.set_visible_child_name("loading");
|
||||
} else if public_room_list.empty() {
|
||||
imp.stack.set_visible_child(&*imp.empty_label);
|
||||
imp.stack.set_visible_child_name("empty");
|
||||
} else {
|
||||
imp.stack.set_visible_child(&*imp.scrolled_window);
|
||||
imp.stack.set_visible_child_name("results");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,43 +41,56 @@
|
|||
<class name="explore"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="Spinner" id="spinner">
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="vexpand">True</property>
|
||||
<style>
|
||||
<class name="session-loading-spinner"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="empty_label">
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="label" translatable="yes">No rooms matching the search were found</property>
|
||||
<style>
|
||||
<class name="bold"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<property name="vexpand">True</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">loading</property>
|
||||
<property name="child">
|
||||
<object class="AdwClampScrollable">
|
||||
<object class="Spinner" id="spinner">
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="maximum-size">750</property>
|
||||
<property name="tightening-threshold">550</property>
|
||||
<style>
|
||||
<class name="session-loading-spinner"/>
|
||||
</style>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">empty</property>
|
||||
<property name="child">
|
||||
<object class="GtkLabel" id="empty_label">
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="label" translatable="yes">No rooms matching the search were found</property>
|
||||
<style>
|
||||
<class name="bold"/>
|
||||
</style>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">results</property>
|
||||
<property name="child">
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<property name="vexpand">True</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="child">
|
||||
<object class="GtkListView" id="listview">
|
||||
<property name="margin-end">24</property>
|
||||
<property name="margin-start">24</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<object class="AdwClampScrollable">
|
||||
<property name="vexpand">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="maximum-size">750</property>
|
||||
<property name="tightening-threshold">550</property>
|
||||
<property name="child">
|
||||
<object class="GtkListView" id="listview">
|
||||
<property name="margin-end">24</property>
|
||||
<property name="margin-start">24</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
|
@ -92,12 +105,14 @@
|
|||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<accessibility>
|
||||
<property name="label" translatable="yes">Room List</property>
|
||||
</accessibility>
|
||||
</object>
|
||||
</property>
|
||||
<accessibility>
|
||||
<property name="label" translatable="yes">Room List</property>
|
||||
</accessibility>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
|
|
|
@ -4,22 +4,34 @@ use matrix_sdk::ruma::directory::PublicRoomsChunk;
|
|||
use crate::session::model::{AvatarData, AvatarImage, AvatarUriSource, Room, RoomList};
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
|
||||
use glib::signal::SignalHandlerId;
|
||||
use once_cell::{sync::Lazy, unsync::OnceCell};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::PublicRoom)]
|
||||
pub struct PublicRoom {
|
||||
/// The list of rooms in this session.
|
||||
#[property(get, construct_only)]
|
||||
pub room_list: OnceCell<RoomList>,
|
||||
/// The server that returned the room.
|
||||
#[property(get, construct_only)]
|
||||
pub server: OnceCell<String>,
|
||||
pub matrix_public_room: OnceCell<PublicRoomsChunk>,
|
||||
/// The [`AvatarData`] of this room.
|
||||
#[property(get)]
|
||||
pub avatar_data: OnceCell<AvatarData>,
|
||||
pub room: OnceCell<Room>,
|
||||
pub is_pending: Cell<bool>,
|
||||
/// The `Room` object for this room, if the user is already a member of
|
||||
/// this room.
|
||||
#[property(get)]
|
||||
pub room: RefCell<Option<Room>>,
|
||||
/// Whether the room is pending.
|
||||
///
|
||||
/// A room is pending when the user clicked to join it.
|
||||
#[property(get)]
|
||||
pub pending: Cell<bool>,
|
||||
pub room_handler: RefCell<Option<SignalHandlerId>>,
|
||||
}
|
||||
|
||||
|
@ -29,52 +41,8 @@ mod imp {
|
|||
type Type = super::PublicRoom;
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for PublicRoom {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<RoomList>("room-list")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("server")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecObject::builder::<Room>("room")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("pending")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecObject::builder::<AvatarData>("avatar-data")
|
||||
.read_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"room-list" => self.room_list.set(value.get().unwrap()).unwrap(),
|
||||
"server" => self.server.set(value.get().unwrap()).unwrap(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"room-list" => obj.room_list().to_value(),
|
||||
"server" => obj.server().to_value(),
|
||||
"avatar-data" => obj.avatar_data().to_value(),
|
||||
"room" => obj.room().to_value(),
|
||||
"pending" => obj.is_pending().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
@ -107,6 +75,7 @@ mod imp {
|
|||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A room in a homeserver's public directory.
|
||||
pub struct PublicRoom(ObjectSubclass<imp::PublicRoom>);
|
||||
}
|
||||
|
||||
|
@ -118,48 +87,20 @@ impl PublicRoom {
|
|||
.build()
|
||||
}
|
||||
|
||||
/// The list of rooms in this session.
|
||||
pub fn room_list(&self) -> &RoomList {
|
||||
self.imp().room_list.get().unwrap()
|
||||
}
|
||||
|
||||
/// The server that returned the room.
|
||||
pub fn server(&self) -> &str {
|
||||
self.imp().server.get().unwrap()
|
||||
}
|
||||
|
||||
/// The [`AvatarData`] of this room.
|
||||
pub fn avatar_data(&self) -> &AvatarData {
|
||||
self.imp().avatar_data.get().unwrap()
|
||||
}
|
||||
|
||||
/// The `Room` object for this room, if the user is already a member of this
|
||||
/// room.
|
||||
pub fn room(&self) -> Option<&Room> {
|
||||
self.imp().room.get()
|
||||
}
|
||||
|
||||
/// Set the `Room` object for this room.
|
||||
fn set_room(&self, room: Room) {
|
||||
self.imp().room.set(room).unwrap();
|
||||
self.notify("room");
|
||||
self.imp().room.replace(Some(room));
|
||||
self.notify_room();
|
||||
}
|
||||
|
||||
/// Set whether this room is pending.
|
||||
fn set_pending(&self, is_pending: bool) {
|
||||
if self.is_pending() == is_pending {
|
||||
fn set_pending(&self, pending: bool) {
|
||||
if self.pending() == pending {
|
||||
return;
|
||||
}
|
||||
|
||||
self.imp().is_pending.set(is_pending);
|
||||
self.notify("pending");
|
||||
}
|
||||
|
||||
/// Whether the room is pending.
|
||||
///
|
||||
/// A room is pending when the user clicked to join it.
|
||||
pub fn is_pending(&self) -> bool {
|
||||
self.imp().is_pending.get()
|
||||
self.imp().pending.set(pending);
|
||||
self.notify_pending();
|
||||
}
|
||||
|
||||
pub fn set_matrix_public_room(&self, room: PublicRoomsChunk) {
|
||||
|
|
|
@ -14,14 +14,15 @@ use super::{PublicRoom, Server};
|
|||
use crate::{session::model::Session, spawn, spawn_tokio};
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use glib::object::WeakRef;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::PublicRoomList)]
|
||||
pub struct PublicRoomList {
|
||||
pub list: RefCell<Vec<PublicRoom>>,
|
||||
pub search_term: RefCell<Option<String>>,
|
||||
|
@ -30,7 +31,18 @@ mod imp {
|
|||
pub next_batch: RefCell<Option<String>>,
|
||||
pub request_sent: Cell<bool>,
|
||||
pub total_room_count_estimate: Cell<Option<u64>>,
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, construct_only)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
/// Whether the list is loading.
|
||||
#[property(get = Self::loading)]
|
||||
pub loading: PhantomData<bool>,
|
||||
/// Whether the list is empty.
|
||||
#[property(get = Self::empty)]
|
||||
pub empty: PhantomData<bool>,
|
||||
/// Whether all results for the current search were loaded.
|
||||
#[property(get = Self::complete)]
|
||||
pub complete: PhantomData<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -40,53 +52,18 @@ mod imp {
|
|||
type Interfaces = (gio::ListModel,);
|
||||
}
|
||||
|
||||
impl ObjectImpl for PublicRoomList {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<Session>("session")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("loading")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("empty").read_only().build(),
|
||||
glib::ParamSpecBoolean::builder("complete")
|
||||
.read_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.session().to_value(),
|
||||
"loading" => obj.loading().to_value(),
|
||||
"empty" => obj.empty().to_value(),
|
||||
"complete" => obj.complete().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for PublicRoomList {}
|
||||
|
||||
impl ListModelImpl for PublicRoomList {
|
||||
fn item_type(&self) -> glib::Type {
|
||||
PublicRoom::static_type()
|
||||
}
|
||||
|
||||
fn n_items(&self) -> u32 {
|
||||
self.list.borrow().len() as u32
|
||||
}
|
||||
|
||||
fn item(&self, position: u32) -> Option<glib::Object> {
|
||||
self.list
|
||||
.borrow()
|
||||
|
@ -95,9 +72,27 @@ mod imp {
|
|||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicRoomList {
|
||||
/// Whether the list is loading.
|
||||
fn loading(&self) -> bool {
|
||||
self.request_sent.get() && self.list.borrow().is_empty()
|
||||
}
|
||||
|
||||
/// Whether the list is empty.
|
||||
fn empty(&self) -> bool {
|
||||
!self.request_sent.get() && self.list.borrow().is_empty()
|
||||
}
|
||||
|
||||
/// Whether all results for the current search were loaded.
|
||||
fn complete(&self) -> bool {
|
||||
self.next_batch.borrow().is_none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A list of rooms in a homeserver's public directory.
|
||||
pub struct PublicRoomList(ObjectSubclass<imp::PublicRoomList>)
|
||||
@implements gio::ListModel;
|
||||
}
|
||||
|
@ -107,46 +102,18 @@ impl PublicRoomList {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Option<Session>) {
|
||||
if session == self.session() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.imp().session.set(session.as_ref());
|
||||
self.notify("session");
|
||||
}
|
||||
|
||||
/// Whether the list is loading.
|
||||
pub fn loading(&self) -> bool {
|
||||
self.request_sent() && self.imp().list.borrow().is_empty()
|
||||
}
|
||||
|
||||
/// Whether the list is empty.
|
||||
pub fn empty(&self) -> bool {
|
||||
!self.request_sent() && self.imp().list.borrow().is_empty()
|
||||
}
|
||||
|
||||
/// Whether all results for the current search were loaded.
|
||||
pub fn complete(&self) -> bool {
|
||||
self.imp().next_batch.borrow().is_none()
|
||||
}
|
||||
|
||||
/// Whether a request is in progress.
|
||||
fn request_sent(&self) -> bool {
|
||||
self.imp().request_sent.get()
|
||||
}
|
||||
|
||||
/// Set whether a request is in progress.
|
||||
fn set_request_sent(&self, request_sent: bool) {
|
||||
self.imp().request_sent.set(request_sent);
|
||||
|
||||
self.notify("loading");
|
||||
self.notify("empty");
|
||||
self.notify("complete");
|
||||
self.notify_loading();
|
||||
self.notify_empty();
|
||||
self.notify_complete();
|
||||
}
|
||||
|
||||
pub fn init(&self) {
|
||||
|
@ -156,21 +123,22 @@ impl PublicRoomList {
|
|||
}
|
||||
}
|
||||
|
||||
/// Search the given term on the given server.
|
||||
pub fn search(&self, search_term: Option<String>, server: Server) {
|
||||
let imp = self.imp();
|
||||
let network = Some(server.network());
|
||||
let server = server.server();
|
||||
|
||||
if imp.search_term.borrow().as_ref() == search_term.as_ref()
|
||||
&& imp.server.borrow().as_deref() == server
|
||||
&& imp.network.borrow().as_deref() == network
|
||||
if *imp.search_term.borrow() == search_term
|
||||
&& *imp.server.borrow() == server
|
||||
&& *imp.network.borrow() == network
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
imp.search_term.replace(search_term);
|
||||
imp.server.replace(server.map(ToOwned::to_owned));
|
||||
imp.network.replace(network.map(ToOwned::to_owned));
|
||||
imp.server.replace(server);
|
||||
imp.network.replace(network);
|
||||
self.load_public_rooms(true);
|
||||
}
|
||||
|
||||
|
@ -223,6 +191,7 @@ impl PublicRoomList {
|
|||
self.set_request_sent(false);
|
||||
}
|
||||
|
||||
/// Whether this is the response for the latest request that was sent.
|
||||
fn is_valid_response(
|
||||
&self,
|
||||
search_term: Option<String>,
|
||||
|
|
|
@ -7,21 +7,25 @@ use super::PublicRoom;
|
|||
use crate::{
|
||||
components::{Avatar, Spinner, SpinnerButton},
|
||||
prelude::*,
|
||||
spawn, toast, Window,
|
||||
spawn, toast,
|
||||
utils::BoundObject,
|
||||
Window,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::{signal::SignalHandlerId, subclass::InitializingObject};
|
||||
use once_cell::sync::Lazy;
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(resource = "/org/gnome/Fractal/ui/session/view/content/explore/public_room_row.ui")]
|
||||
#[properties(wrapper_type = super::PublicRoomRow)]
|
||||
pub struct PublicRoomRow {
|
||||
pub public_room: RefCell<Option<PublicRoom>>,
|
||||
/// The public room displayed by this row.
|
||||
#[property(get, set= Self::set_public_room, explicit_notify)]
|
||||
pub public_room: BoundObject<PublicRoom>,
|
||||
#[template_child]
|
||||
pub avatar: TemplateChild<Avatar>,
|
||||
#[template_child]
|
||||
|
@ -35,8 +39,6 @@ mod imp {
|
|||
#[template_child]
|
||||
pub button: TemplateChild<SpinnerButton>,
|
||||
pub original_child: RefCell<Option<gtk::Widget>>,
|
||||
pub pending_handler: RefCell<Option<SignalHandlerId>>,
|
||||
pub room_handler: RefCell<Option<SignalHandlerId>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -54,30 +56,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for PublicRoomRow {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecObject::builder::<PublicRoom>("public-room")
|
||||
.explicit_notify()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"public-room" => self.obj().set_public_room(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"public-room" => self.obj().public_room().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
self.button
|
||||
|
@ -85,24 +65,81 @@ mod imp {
|
|||
imp.obj().join_or_view();
|
||||
}));
|
||||
}
|
||||
|
||||
fn dispose(&self) {
|
||||
if let Some(ref old_public_room) = self.obj().public_room() {
|
||||
if let Some(handler) = self.pending_handler.take() {
|
||||
old_public_room.disconnect(handler);
|
||||
}
|
||||
if let Some(handler_id) = self.room_handler.take() {
|
||||
old_public_room.disconnect(handler_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for PublicRoomRow {}
|
||||
impl BinImpl for PublicRoomRow {}
|
||||
|
||||
impl PublicRoomRow {
|
||||
/// Set the public room displayed by this row.
|
||||
fn set_public_room(&self, public_room: Option<PublicRoom>) {
|
||||
if self.public_room.obj() == public_room {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
self.public_room.disconnect_signals();
|
||||
|
||||
if let Some(public_room) = public_room {
|
||||
if let Some(child) = self.original_child.take() {
|
||||
obj.set_child(Some(&child));
|
||||
}
|
||||
if let Some(matrix_public_room) = public_room.matrix_public_room() {
|
||||
self.avatar
|
||||
.set_data(Some(public_room.avatar_data().clone()));
|
||||
|
||||
let display_name = matrix_public_room
|
||||
.name
|
||||
.as_deref()
|
||||
// FIXME: display some other identification for this room
|
||||
.unwrap_or("Room without name");
|
||||
self.display_name.set_text(display_name);
|
||||
|
||||
if let Some(topic) = &matrix_public_room.topic {
|
||||
self.description.set_text(topic);
|
||||
}
|
||||
self.description
|
||||
.set_visible(matrix_public_room.topic.is_some());
|
||||
|
||||
if let Some(alias) = &matrix_public_room.canonical_alias {
|
||||
self.alias.set_text(alias.as_str());
|
||||
}
|
||||
self.alias
|
||||
.set_visible(matrix_public_room.canonical_alias.is_some());
|
||||
|
||||
self.members_count
|
||||
.set_text(&matrix_public_room.num_joined_members.to_string());
|
||||
|
||||
let pending_handler = public_room.connect_pending_notify(
|
||||
clone!(@weak obj => move |public_room| {
|
||||
obj.update_button(public_room);
|
||||
}),
|
||||
);
|
||||
|
||||
let room_handler =
|
||||
public_room.connect_room_notify(clone!(@weak obj => move |public_room| {
|
||||
obj.update_button(public_room);
|
||||
}));
|
||||
|
||||
obj.update_button(&public_room);
|
||||
self.public_room
|
||||
.set(public_room, vec![pending_handler, room_handler]);
|
||||
} else if self.original_child.borrow().is_none() {
|
||||
let spinner = Spinner::default();
|
||||
spinner.set_margin_top(12);
|
||||
spinner.set_margin_bottom(12);
|
||||
self.original_child.replace(obj.child());
|
||||
obj.set_child(Some(&spinner));
|
||||
}
|
||||
}
|
||||
|
||||
obj.notify_public_room();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A row representing a room for a homeserver's public directory.
|
||||
pub struct PublicRoomRow(ObjectSubclass<imp::PublicRoomRow>)
|
||||
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
|
||||
}
|
||||
|
@ -112,97 +149,6 @@ impl PublicRoomRow {
|
|||
glib::Object::new()
|
||||
}
|
||||
|
||||
/// The public room displayed by this row.
|
||||
pub fn public_room(&self) -> Option<PublicRoom> {
|
||||
self.imp().public_room.borrow().clone()
|
||||
}
|
||||
|
||||
/// Set the public room displayed by this row.
|
||||
pub fn set_public_room(&self, public_room: Option<PublicRoom>) {
|
||||
let imp = self.imp();
|
||||
let old_public_room = self.public_room();
|
||||
|
||||
if old_public_room == public_room {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(ref old_public_room) = old_public_room {
|
||||
if let Some(handler) = imp.room_handler.take() {
|
||||
old_public_room.disconnect(handler);
|
||||
}
|
||||
if let Some(handler) = imp.pending_handler.take() {
|
||||
old_public_room.disconnect(handler);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref public_room) = public_room {
|
||||
if let Some(child) = imp.original_child.take() {
|
||||
self.set_child(Some(&child));
|
||||
}
|
||||
if let Some(matrix_public_room) = public_room.matrix_public_room() {
|
||||
imp.avatar.set_data(Some(public_room.avatar_data().clone()));
|
||||
|
||||
let display_name = matrix_public_room
|
||||
.name
|
||||
.as_deref()
|
||||
.map(AsRef::as_ref)
|
||||
// FIXME: display some other identification for this room
|
||||
.unwrap_or("Room without name");
|
||||
imp.display_name.set_text(display_name);
|
||||
|
||||
let has_topic = if let Some(ref topic) = matrix_public_room.topic {
|
||||
imp.description.set_text(topic);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
imp.description.set_visible(has_topic);
|
||||
|
||||
let has_alias = if let Some(ref alias) = matrix_public_room.canonical_alias {
|
||||
imp.alias.set_text(alias.as_str());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
imp.alias.set_visible(has_alias);
|
||||
imp.members_count
|
||||
.set_text(&matrix_public_room.num_joined_members.to_string());
|
||||
|
||||
let pending_handler = public_room.connect_notify_local(
|
||||
Some("pending"),
|
||||
clone!(@weak self as obj => move |public_room, _| {
|
||||
obj.update_button(public_room);
|
||||
}),
|
||||
);
|
||||
|
||||
imp.pending_handler.replace(Some(pending_handler));
|
||||
|
||||
let room_handler = public_room.connect_notify_local(
|
||||
Some("room"),
|
||||
clone!(@weak self as obj => move |public_room, _| {
|
||||
obj.update_button(public_room);
|
||||
}),
|
||||
);
|
||||
|
||||
imp.room_handler.replace(Some(room_handler));
|
||||
|
||||
self.update_button(public_room);
|
||||
} else if imp.original_child.borrow().is_none() {
|
||||
let spinner = Spinner::default();
|
||||
spinner.set_margin_top(12);
|
||||
spinner.set_margin_bottom(12);
|
||||
imp.original_child.replace(self.child());
|
||||
self.set_child(Some(&spinner));
|
||||
}
|
||||
}
|
||||
imp.avatar
|
||||
.set_data(public_room.clone().map(|room| room.avatar_data().clone()));
|
||||
imp.public_room.replace(public_room);
|
||||
self.notify("public-room");
|
||||
}
|
||||
|
||||
fn update_button(&self, public_room: &PublicRoom) {
|
||||
let button = &self.imp().button;
|
||||
if public_room.room().is_some() {
|
||||
|
@ -212,7 +158,7 @@ impl PublicRoomRow {
|
|||
button.set_label(gettext("Join"));
|
||||
}
|
||||
|
||||
button.set_loading(public_room.is_pending());
|
||||
button.set_loading(public_room.pending());
|
||||
}
|
||||
|
||||
/// Join or view the public room.
|
||||
|
|
|
@ -2,22 +2,24 @@ use gtk::{glib, prelude::*, subclass::prelude::*};
|
|||
use ruma::thirdparty::ProtocolInstance;
|
||||
|
||||
mod imp {
|
||||
use once_cell::{sync::Lazy, unsync::OnceCell};
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::Server)]
|
||||
pub struct Server {
|
||||
/// The name of the server that is displayed in the list.
|
||||
#[property(get, construct_only)]
|
||||
pub name: OnceCell<String>,
|
||||
|
||||
/// The ID of the network that is used during search.
|
||||
#[property(get, construct_only)]
|
||||
pub network: OnceCell<String>,
|
||||
|
||||
/// The server name that is used during search.
|
||||
pub server: OnceCell<String>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub server: RefCell<Option<String>>,
|
||||
/// Whether this server can be deleted from the list.
|
||||
#[property(get, construct_only)]
|
||||
pub deletable: OnceCell<bool>,
|
||||
}
|
||||
|
||||
|
@ -27,54 +29,8 @@ mod imp {
|
|||
type Type = super::Server;
|
||||
}
|
||||
|
||||
impl ObjectImpl for Server {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecString::builder("name")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("network")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("server")
|
||||
.construct_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("deletable")
|
||||
.construct_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"name" => self.name.set(value.get().unwrap()).unwrap(),
|
||||
"network" => self.network.set(value.get().unwrap()).unwrap(),
|
||||
"server" => {
|
||||
if let Some(server) = value.get().unwrap() {
|
||||
self.server.set(server).unwrap();
|
||||
}
|
||||
}
|
||||
"deletable" => self.deletable.set(value.get().unwrap()).unwrap(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"name" => obj.name().to_value(),
|
||||
"network" => obj.network().to_value(),
|
||||
"server" => obj.server().to_value(),
|
||||
"deletable" => obj.deletable().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for Server {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -107,24 +63,4 @@ impl Server {
|
|||
.property("deletable", true)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// The name of the server.
|
||||
pub fn name(&self) -> &str {
|
||||
self.imp().name.get().unwrap()
|
||||
}
|
||||
|
||||
/// The ID of the network that is used during search.
|
||||
pub fn network(&self) -> &str {
|
||||
self.imp().network.get().unwrap()
|
||||
}
|
||||
|
||||
/// The server name that is used during search.
|
||||
pub fn server(&self) -> Option<&str> {
|
||||
self.imp().server.get().map(String::as_ref)
|
||||
}
|
||||
|
||||
/// Whether this server can be deleted from the list.
|
||||
pub fn deletable(&self) -> bool {
|
||||
*self.imp().deletable.get().unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,14 +8,14 @@ use crate::{prelude::*, session::model::Session, spawn, spawn_tokio};
|
|||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::object::WeakRef;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::ServerList)]
|
||||
pub struct ServerList {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set = Self::set_session, construct_only)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
pub list: RefCell<Vec<Server>>,
|
||||
}
|
||||
|
||||
|
@ -26,31 +26,8 @@ mod imp {
|
|||
type Interfaces = (gio::ListModel,);
|
||||
}
|
||||
|
||||
impl ObjectImpl for ServerList {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecObject::builder::<Session>("session")
|
||||
.construct_only()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for ServerList {}
|
||||
|
||||
impl ListModelImpl for ServerList {
|
||||
fn item_type(&self) -> glib::Type {
|
||||
|
@ -69,6 +46,25 @@ mod imp {
|
|||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerList {
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Session) {
|
||||
let obj = self.obj();
|
||||
|
||||
self.session.set(Some(&session));
|
||||
|
||||
let user_id = session.user_id();
|
||||
self.list.replace(vec![Server::with_default_server(
|
||||
user_id.server_name().as_str(),
|
||||
)]);
|
||||
obj.items_changed(0, 0, 1);
|
||||
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.load_servers().await;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
|
@ -82,28 +78,7 @@ impl ServerList {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Session) {
|
||||
let imp = self.imp();
|
||||
|
||||
imp.session.set(Some(&session));
|
||||
|
||||
let user_id = session.user_id();
|
||||
imp.list.replace(vec![Server::with_default_server(
|
||||
user_id.server_name().as_str(),
|
||||
)]);
|
||||
self.items_changed(0, 0, 1);
|
||||
|
||||
spawn!(clone!(@weak self as obj => async move {
|
||||
obj.load_servers().await;
|
||||
}));
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Load all the servers.
|
||||
async fn load_servers(&self) {
|
||||
self.load_protocols().await;
|
||||
|
||||
|
@ -118,6 +93,7 @@ impl ServerList {
|
|||
self.items_changed(1, 0, (added - 1) as u32);
|
||||
}
|
||||
|
||||
/// Load the protocols of the session's homeserver.
|
||||
async fn load_protocols(&self) {
|
||||
let client = self.session().unwrap().client();
|
||||
|
||||
|
@ -132,6 +108,7 @@ impl ServerList {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add the given protocol to this list.
|
||||
fn add_protocols(&self, protocols: get_protocols::v3::Response) {
|
||||
let protocols_servers =
|
||||
protocols
|
||||
|
@ -146,13 +123,15 @@ impl ServerList {
|
|||
self.imp().list.borrow_mut().extend(protocols_servers)
|
||||
}
|
||||
|
||||
/// Whether this list contains the given Matrix server.
|
||||
pub fn contains_matrix_server(&self, server: &str) -> bool {
|
||||
let list = &self.imp().list.borrow();
|
||||
// The user's matrix server is a special case that doesn't have a "server", so
|
||||
// use its name.
|
||||
list[0].name() == server || list.iter().any(|s| s.server() == Some(server))
|
||||
list[0].name() == server || list.iter().any(|s| s.server().as_deref() == Some(server))
|
||||
}
|
||||
|
||||
/// Add a custom Matrix server.
|
||||
pub fn add_custom_matrix_server(&self, server_name: String) {
|
||||
let server = Server::with_custom_matrix_server(&server_name);
|
||||
let pos = {
|
||||
|
@ -172,12 +151,13 @@ impl ServerList {
|
|||
self.items_changed(pos as u32, 0, 1);
|
||||
}
|
||||
|
||||
/// Remove a custom Matrix server.
|
||||
pub fn remove_custom_matrix_server(&self, server_name: &str) {
|
||||
let pos = {
|
||||
let mut list = self.imp().list.borrow_mut();
|
||||
let pos = list
|
||||
.iter()
|
||||
.position(|s| s.deletable() && s.server() == Some(server_name));
|
||||
.position(|s| s.deletable() && s.server().as_deref() == Some(server_name));
|
||||
|
||||
if let Some(pos) = pos {
|
||||
list.remove(pos);
|
||||
|
|
|
@ -3,16 +3,19 @@ use gtk::{glib, prelude::*, subclass::prelude::*, CompositeTemplate};
|
|||
use super::server::Server;
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::subclass::InitializingObject;
|
||||
use once_cell::{sync::Lazy, unsync::OnceCell};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(resource = "/org/gnome/Fractal/ui/session/view/content/explore/server_row.ui")]
|
||||
#[properties(wrapper_type = super::ExploreServerRow)]
|
||||
pub struct ExploreServerRow {
|
||||
/// The server displayed by this row.
|
||||
pub server: OnceCell<Server>,
|
||||
#[property(get, construct_only)]
|
||||
pub server: RefCell<Option<Server>>,
|
||||
#[template_child]
|
||||
pub remove_button: TemplateChild<gtk::Button>,
|
||||
}
|
||||
|
@ -32,31 +35,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for ExploreServerRow {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecObject::builder::<Server>("server")
|
||||
.construct_only()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"server" => self.server.set(value.get().unwrap()).unwrap(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"server" => self.obj().server().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
|
@ -73,6 +53,7 @@ mod imp {
|
|||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A row representing a server to explore.
|
||||
pub struct ExploreServerRow(ObjectSubclass<imp::ExploreServerRow>)
|
||||
@extends gtk::Widget, gtk::ListBoxRow, @implements gtk::Accessible;
|
||||
}
|
||||
|
@ -81,9 +62,4 @@ impl ExploreServerRow {
|
|||
pub fn new(server: &Server) -> Self {
|
||||
glib::Object::builder().property("server", server).build()
|
||||
}
|
||||
|
||||
/// The server displayed by this row.
|
||||
pub fn server(&self) -> Option<&Server> {
|
||||
self.imp().server.get()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,15 +13,19 @@ use crate::session::model::Session;
|
|||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use glib::{object::WeakRef, subclass::InitializingObject};
|
||||
use once_cell::sync::Lazy;
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(resource = "/org/gnome/Fractal/ui/session/view/content/explore/servers_popover.ui")]
|
||||
#[properties(wrapper_type = super::ExploreServersPopover)]
|
||||
pub struct ExploreServersPopover {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set = Self::set_session, explicit_notify)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
/// The server list.
|
||||
#[property(get)]
|
||||
pub server_list: RefCell<Option<ServerList>>,
|
||||
#[template_child]
|
||||
pub listbox: TemplateChild<gtk::ListBox>,
|
||||
|
@ -61,39 +65,8 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for ExploreServersPopover {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<ServerList>("server-list")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecObject::builder::<Session>("session")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.session().to_value(),
|
||||
"server-list" => obj.server_list().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
@ -113,9 +86,22 @@ mod imp {
|
|||
|
||||
impl WidgetImpl for ExploreServersPopover {}
|
||||
impl PopoverImpl for ExploreServersPopover {}
|
||||
|
||||
impl ExploreServersPopover {
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Option<Session>) {
|
||||
if session == self.session.upgrade() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.session.set(session.as_ref());
|
||||
self.obj().notify_session();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A popover that lists the servers that can be explored.
|
||||
pub struct ExploreServersPopover(ObjectSubclass<imp::ExploreServersPopover>)
|
||||
@extends gtk::Widget, gtk::Popover, @implements gtk::Accessible;
|
||||
}
|
||||
|
@ -125,21 +111,7 @@ impl ExploreServersPopover {
|
|||
glib::Object::builder().property("session", session).build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
if session == self.session() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.imp().session.set(session.as_ref());
|
||||
self.notify("session");
|
||||
}
|
||||
|
||||
/// Initialize the list of servers.
|
||||
pub fn init(&self) {
|
||||
let Some(session) = &self.session() else {
|
||||
return;
|
||||
|
@ -156,20 +128,16 @@ impl ExploreServersPopover {
|
|||
imp.listbox.select_row(imp.listbox.row_at_index(0).as_ref());
|
||||
|
||||
imp.server_list.replace(Some(server_list));
|
||||
self.notify("server-list");
|
||||
}
|
||||
|
||||
/// The server list.
|
||||
pub fn server_list(&self) -> Option<ServerList> {
|
||||
self.imp().server_list.borrow().clone()
|
||||
self.notify_server_list();
|
||||
}
|
||||
|
||||
/// The server that is currently selected, if any.
|
||||
pub fn selected_server(&self) -> Option<Server> {
|
||||
self.imp()
|
||||
.listbox
|
||||
.selected_row()
|
||||
.and_downcast::<ExploreServerRow>()
|
||||
.and_then(|row| row.server().cloned())
|
||||
.and_then(|row| row.server())
|
||||
}
|
||||
|
||||
pub fn connect_selected_server_changed<F: Fn(&Self, Option<Server>) + 'static>(
|
||||
|
@ -179,10 +147,11 @@ impl ExploreServersPopover {
|
|||
self.imp()
|
||||
.listbox
|
||||
.connect_row_selected(clone!(@weak self as obj => move |_, row| {
|
||||
f(&obj, row.and_then(|row| row.downcast_ref::<ExploreServerRow>()).and_then(|row| row.server().cloned()));
|
||||
f(&obj, row.and_then(|row| row.downcast_ref::<ExploreServerRow>()).and_then(|row| row.server()));
|
||||
}))
|
||||
}
|
||||
|
||||
/// Whether the server currently in the text entry can be added.
|
||||
fn can_add_server(&self) -> bool {
|
||||
let server = self.imp().server_entry.text();
|
||||
ServerName::parse(server.as_str()).is_ok()
|
||||
|
@ -193,10 +162,13 @@ impl ExploreServersPopover {
|
|||
.is_some()
|
||||
}
|
||||
|
||||
/// Update the state of the action to add a server according to the current
|
||||
/// state.
|
||||
fn update_add_server_state(&self) {
|
||||
self.action_set_enabled("explore-servers-popover.add-server", self.can_add_server())
|
||||
}
|
||||
|
||||
/// Add the server currently in the text entry.
|
||||
fn add_server(&self) {
|
||||
if !self.can_add_server() {
|
||||
return;
|
||||
|
@ -218,6 +190,7 @@ impl ExploreServersPopover {
|
|||
);
|
||||
}
|
||||
|
||||
/// Remove the given server.
|
||||
fn remove_server(&self, server: &str) {
|
||||
let Some(server_list) = self.server_list() else {
|
||||
return;
|
||||
|
@ -226,7 +199,7 @@ impl ExploreServersPopover {
|
|||
let imp = self.imp();
|
||||
|
||||
// If the selected server is gonna be removed, select the first one.
|
||||
if self.selected_server().unwrap().server() == Some(server) {
|
||||
if self.selected_server().and_then(|s| s.server()).as_deref() == Some(server) {
|
||||
imp.listbox.select_row(imp.listbox.row_at_index(0).as_ref());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue