fractal/session/view/content/explore/
servers_popover.rsuse adw::subclass::prelude::*;
use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};
use ruma::ServerName;
use super::{ExploreServerRow, Server, ServerList};
use crate::session::model::Session;
mod imp {
use std::cell::RefCell;
use glib::subclass::InitializingObject;
use super::*;
#[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 {
#[property(get, set = Self::set_session, explicit_notify)]
pub session: glib::WeakRef<Session>,
#[property(get)]
pub server_list: RefCell<Option<ServerList>>,
#[template_child]
pub listbox: TemplateChild<gtk::ListBox>,
#[template_child]
pub server_entry: TemplateChild<gtk::Entry>,
}
#[glib::object_subclass]
impl ObjectSubclass for ExploreServersPopover {
const NAME: &'static str = "ContentExploreServersPopover";
type Type = super::ExploreServersPopover;
type ParentType = gtk::Popover;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
klass.install_action("explore-servers-popover.add-server", None, |obj, _, _| {
obj.add_server();
});
klass.install_action(
"explore-servers-popover.remove-server",
Some(&String::static_variant_type()),
|obj, _, variant| {
if let Some(variant) = variant.and_then(String::from_variant) {
obj.remove_server(&variant);
}
},
);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for ExploreServersPopover {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
self.server_entry.connect_changed(clone!(
#[weak]
obj,
move |_| obj.update_add_server_state()
));
self.server_entry.connect_activate(clone!(
#[weak]
obj,
move |_| obj.add_server()
));
obj.update_add_server_state();
}
}
impl WidgetImpl for ExploreServersPopover {}
impl PopoverImpl for ExploreServersPopover {}
impl ExploreServersPopover {
fn set_session(&self, session: Option<&Session>) {
if session == self.session.upgrade().as_ref() {
return;
}
self.session.set(session);
self.obj().notify_session();
}
}
}
glib::wrapper! {
pub struct ExploreServersPopover(ObjectSubclass<imp::ExploreServersPopover>)
@extends gtk::Widget, gtk::Popover, @implements gtk::Accessible;
}
impl ExploreServersPopover {
pub fn new(session: &Session) -> Self {
glib::Object::builder().property("session", session).build()
}
pub fn init(&self) {
let Some(session) = &self.session() else {
return;
};
let imp = self.imp();
let server_list = ServerList::new(session);
imp.listbox.bind_model(Some(&server_list), |obj| {
ExploreServerRow::new(obj.downcast_ref::<Server>().unwrap()).upcast()
});
imp.listbox.select_row(imp.listbox.row_at_index(0).as_ref());
imp.server_list.replace(Some(server_list));
self.notify_server_list();
}
pub fn selected_server(&self) -> Option<Server> {
self.imp()
.listbox
.selected_row()
.and_downcast::<ExploreServerRow>()
.and_then(|row| row.server())
}
pub fn connect_selected_server_changed<F: Fn(&Self, Option<Server>) + 'static>(
&self,
f: F,
) -> glib::SignalHandlerId {
self.imp().listbox.connect_row_selected(clone!(
#[weak(rename_to = obj)]
self,
move |_, row| {
f(
&obj,
row.and_then(|row| row.downcast_ref::<ExploreServerRow>())
.and_then(ExploreServerRow::server),
);
}
))
}
fn can_add_server(&self) -> bool {
let server = self.imp().server_entry.text();
ServerName::parse(server.as_str()).is_ok()
&& self
.server_list()
.filter(|l| !l.contains_matrix_server(&server))
.is_some()
}
fn update_add_server_state(&self) {
self.action_set_enabled("explore-servers-popover.add-server", self.can_add_server());
}
fn add_server(&self) {
if !self.can_add_server() {
return;
}
let Some(server_list) = self.server_list() else {
return;
};
let imp = self.imp();
let server = imp.server_entry.text();
imp.server_entry.set_text("");
server_list.add_custom_matrix_server(server.into());
let index = i32::try_from(server_list.n_items()).unwrap_or(i32::MAX);
let row = imp.listbox.row_at_index(index - 1);
imp.listbox.select_row(row.as_ref());
}
fn remove_server(&self, server: &str) {
let Some(server_list) = self.server_list() else {
return;
};
let imp = self.imp();
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());
}
server_list.remove_custom_matrix_server(server);
}
}