sidebar: Add things needed for the Sidebar
This includes gobject wrappers and the ui.
This commit is contained in:
parent
e4220835ab
commit
a22fa19e38
|
@ -6,6 +6,10 @@
|
|||
<file compressed="true" preprocess="xml-stripblanks" alias="login.ui">ui/login.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="session.ui">ui/session.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="sidebar.ui">ui/sidebar.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="sidebar-category-item.ui">ui/sidebar-category-item.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="sidebar-category-row.ui">ui/sidebar-category-row.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="sidebar-room-item.ui">ui/sidebar-room-item.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="sidebar-room-row.ui">ui/sidebar-room-row.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="window.ui">ui/window.ui</file>
|
||||
<file compressed="true">style.css</file>
|
||||
<file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>
|
||||
|
|
|
@ -19,3 +19,40 @@
|
|||
.send-message-area {
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar .navigation-sidebar row {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.sidebar row .dim-label {
|
||||
padding: 6px 12px;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sidebar row .bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sidebar .view {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.sidebar row .notification_count {
|
||||
/* TODO: use correct color variable */
|
||||
background-color: #555;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
border-radius: 10px;
|
||||
min-width: 0.7em;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.sidebar row .highlight {
|
||||
/* TODO: use correct color variable */
|
||||
background-color: @theme_selected_bg_color;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="selectable">False</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="focusable">False</property>
|
||||
<child>
|
||||
<object class="FrctlSidebarCategoryRow">
|
||||
<binding name="display-name">
|
||||
<lookup type="FrctlCategory" name="display-name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="expanded">
|
||||
<lookup type="FrctlCategory" name="expanded">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRevealer">
|
||||
<binding name="reveal-child">
|
||||
<lookup type="FrctlCategory" name="expanded">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<property name="child">
|
||||
<object class="GtkListView">
|
||||
<property name="model" bind-source="GtkListItem" bind-property="item" bind-flags="sync-create"/>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="resource">/org/gnome/FractalNext/sidebar-room-item.ui</property>
|
||||
</object>
|
||||
</property>
|
||||
<accessibility>
|
||||
<property name="label" translatable="yes">Room List</property>
|
||||
</accessibility>
|
||||
<style>
|
||||
<class name="navigation-sidebar"/>
|
||||
</style>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="FrctlSidebarCategoryRow" parent="AdwBin">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="display_name">
|
||||
<property name="ellipsize">end</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkImage" id="arrow">
|
||||
<style>
|
||||
<class name="arrow"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="FrctlSidebarRoomRow">
|
||||
<binding name="avatar">
|
||||
<lookup type="FrctlRoom" name="avatar">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="display-name">
|
||||
<lookup type="FrctlRoom" name="display-name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="notification-count">
|
||||
<lookup type="FrctlRoom" name="notification-count">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="highlight">
|
||||
<lookup type="FrctlRoom" name="highlight">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="FrctlSidebarRoomRow" parent="AdwBin">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="AdwAvatar" id="avatar">
|
||||
<property name="show-initials">True</property>
|
||||
<property name="size">24</property>
|
||||
<property name="text" bind-source="display_name" bind-property="label" bind-flags="sync-create" />
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="display_name">
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel" id="notification_count">
|
||||
<property name="hexpand">True</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="yalign">1.0</property>
|
||||
<style>
|
||||
<class name="notification_count"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
|
@ -25,7 +25,7 @@
|
|||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="AdwHeaderBar" id="headerbar">
|
||||
<property name="show-end-title-buttons" bind-source="FrctlSidebar" bind-property="compact" bind-flags="sync-create" />
|
||||
<property name="show-end-title-buttons" bind-source="FrctlSidebar" bind-property="compact" bind-flags="sync-create"/>
|
||||
<child type="start">
|
||||
<object class="GtkToggleButton" id="search_button">
|
||||
<property name="icon-name">system-search-symbolic</property>
|
||||
|
@ -41,7 +41,7 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkSearchBar" id="room_search">
|
||||
<property name="search-mode-enabled" bind-source="search_button" bind-property="active" />
|
||||
<property name="search-mode-enabled" bind-source="search_button" bind-property="active"/>
|
||||
<property name="child">
|
||||
<object class="GtkSearchEntry"/>
|
||||
</property>
|
||||
|
@ -49,9 +49,26 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="vexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="child">
|
||||
<object class="GtkListView" id="listview" />
|
||||
<object class="GtkListView" id="listview">
|
||||
<property name="model">
|
||||
<object class="GtkNoSelection">
|
||||
<property name="model">
|
||||
<object class="FrctlCategoryList" />
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="resource">/org/gnome/FractalNext/sidebar-category-item.ui</property>
|
||||
</object>
|
||||
</property>
|
||||
<accessibility>
|
||||
<property name="label" translatable="yes">Sidebar</property>
|
||||
</accessibility>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
|
@ -59,3 +76,4 @@
|
|||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
||||
|
|
|
@ -27,8 +27,13 @@ sources = files(
|
|||
'secret.rs',
|
||||
'session/mod.rs',
|
||||
'session/content.rs',
|
||||
'session/sidebar.rs',
|
||||
'session/supervisor.rs',
|
||||
'session/sidebar/mod.rs',
|
||||
'session/sidebar/category_row.rs',
|
||||
'session/sidebar/room_row.rs',
|
||||
'session/sidebar/category.rs',
|
||||
'session/sidebar/category_list.rs',
|
||||
'session/sidebar/room.rs',
|
||||
)
|
||||
|
||||
custom_target(
|
||||
|
|
|
@ -39,6 +39,7 @@ mod imp {
|
|||
pub homeserver: OnceCell<String>,
|
||||
/// Contains the error if something went wrong
|
||||
pub error: RefCell<Option<matrix_sdk::Error>>,
|
||||
pub client: OnceCell<Client>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -53,6 +54,7 @@ mod imp {
|
|||
content: TemplateChild::default(),
|
||||
homeserver: OnceCell::new(),
|
||||
error: RefCell::new(None),
|
||||
client: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +173,8 @@ impl FrctlSession {
|
|||
|
||||
let client = client.unwrap();
|
||||
|
||||
priv_.client.set(client.clone()).unwrap();
|
||||
|
||||
let sidebar_sender = priv_.sidebar.get().setup_channel();
|
||||
let content_sender = priv_.content.get().setup_channel();
|
||||
|
||||
|
@ -257,6 +261,8 @@ impl FrctlSession {
|
|||
Ok(None) => {}
|
||||
}
|
||||
|
||||
obj.load();
|
||||
|
||||
obj.emit_by_name("ready", &[]).unwrap();
|
||||
|
||||
glib::Continue(false)
|
||||
|
@ -265,6 +271,14 @@ impl FrctlSession {
|
|||
sender
|
||||
}
|
||||
|
||||
/// Loads the state from the `Store`
|
||||
/// Note that the `Store` currently doesn't store all events, therefore, we arn't really
|
||||
/// loading much via this function.
|
||||
pub fn load(&self) {
|
||||
let priv_ = imp::FrctlSession::from_instance(self);
|
||||
priv_.sidebar.load(&priv_.client.get().unwrap());
|
||||
}
|
||||
|
||||
/// Returns and consumes the `error` that was generated when the session failed to login,
|
||||
/// on a successful login this will be `None`.
|
||||
/// Unfortunatly it's not possible to connect the Error direclty to the `ready` signals.
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
use crate::session::sidebar::FrctlRoom;
|
||||
use gettextrs::gettext;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::{self, gio, glib, prelude::*};
|
||||
use matrix_sdk::{identifiers::RoomId, Client};
|
||||
use matrix_sdk::{room::Room, RoomType};
|
||||
|
||||
// TODO: do we also want the categorie `People` and a custom categorie support?
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, glib::GEnum)]
|
||||
#[repr(u32)]
|
||||
#[genum(type_name = "CategoryName")]
|
||||
pub enum CategoryName {
|
||||
Invited = 0,
|
||||
Favorite = 1,
|
||||
Normal = 2,
|
||||
LowPriority = 3,
|
||||
Left = 4,
|
||||
}
|
||||
|
||||
impl CategoryName {
|
||||
pub fn get_room_type(&self) -> RoomType {
|
||||
match self {
|
||||
CategoryName::Invited => RoomType::Invited,
|
||||
CategoryName::Favorite => RoomType::Joined,
|
||||
CategoryName::Normal => RoomType::Joined,
|
||||
CategoryName::LowPriority => RoomType::Joined,
|
||||
CategoryName::Left => RoomType::Left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CategoryName {
|
||||
fn default() -> Self {
|
||||
CategoryName::Normal
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for CategoryName {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
CategoryName::Invited => gettext("Invited"),
|
||||
CategoryName::Favorite => gettext("Favorite"),
|
||||
CategoryName::Normal => gettext("Rooms"),
|
||||
CategoryName::LowPriority => gettext("Low Priority"),
|
||||
CategoryName::Left => gettext("Historical"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use gio::subclass::prelude::*;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FrctlCategory {
|
||||
pub client: OnceCell<Client>,
|
||||
pub map: RefCell<HashMap<RoomId, (u32, FrctlRoom)>>,
|
||||
pub list: RefCell<Vec<RoomId>>,
|
||||
pub name: Cell<CategoryName>,
|
||||
pub expanded: Cell<bool>,
|
||||
pub selected: Cell<u32>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for FrctlCategory {
|
||||
const NAME: &'static str = "FrctlCategory";
|
||||
type Type = super::FrctlCategory;
|
||||
type ParentType = glib::Object;
|
||||
type Interfaces = (gio::ListModel, gtk::SelectionModel);
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
client: OnceCell::new(),
|
||||
map: RefCell::new(HashMap::new()),
|
||||
list: RefCell::new(Vec::new()),
|
||||
name: Cell::new(CategoryName::default()),
|
||||
expanded: Cell::new(true),
|
||||
selected: Cell::new(u32::MAX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for FrctlCategory {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpec::enum_(
|
||||
"display-name",
|
||||
"Display Name",
|
||||
"The name of this category",
|
||||
CategoryName::static_type(),
|
||||
CategoryName::default() as i32,
|
||||
glib::ParamFlags::READWRITE,
|
||||
),
|
||||
glib::ParamSpec::boolean(
|
||||
"expanded",
|
||||
"Expanded",
|
||||
"Wheter this category is expanded or not",
|
||||
true,
|
||||
glib::ParamFlags::READWRITE,
|
||||
),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(
|
||||
&self,
|
||||
_obj: &Self::Type,
|
||||
_id: usize,
|
||||
value: &glib::Value,
|
||||
pspec: &glib::ParamSpec,
|
||||
) {
|
||||
match pspec.get_name() {
|
||||
"expanded" => {
|
||||
let expanded: Option<bool> = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
self.expanded.set(expanded.unwrap());
|
||||
}
|
||||
"display-name" => {
|
||||
let name = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
self.name.set(name.unwrap());
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property(
|
||||
&self,
|
||||
_obj: &Self::Type,
|
||||
_id: usize,
|
||||
pspec: &glib::ParamSpec,
|
||||
) -> glib::Value {
|
||||
match pspec.get_name() {
|
||||
"display-name" => self.name.get().to_value(),
|
||||
"expanded" => self.expanded.get().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ListModelImpl for FrctlCategory {
|
||||
fn get_item_type(&self, _list_model: &Self::Type) -> glib::Type {
|
||||
FrctlRoom::static_type()
|
||||
}
|
||||
fn get_n_items(&self, _list_model: &Self::Type) -> u32 {
|
||||
self.list.borrow().len() as u32
|
||||
}
|
||||
fn get_item(&self, _list_model: &Self::Type, position: u32) -> Option<glib::Object> {
|
||||
let list = self.list.borrow();
|
||||
let room_id = list.get(position as usize);
|
||||
if let Some(room_id) = room_id {
|
||||
self.map
|
||||
.borrow()
|
||||
.get(&room_id)
|
||||
.map(|(_, o)| o.clone().upcast::<glib::Object>())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SelectionModelImpl for FrctlCategory {
|
||||
fn get_selection_in_range(
|
||||
&self,
|
||||
_model: &Self::Type,
|
||||
_position: u32,
|
||||
_n_items: u32,
|
||||
) -> gtk::Bitset {
|
||||
let result = gtk::Bitset::new_empty();
|
||||
if self.selected.get() != u32::MAX {
|
||||
result.add(self.selected.get());
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn is_selected(&self, _model: &Self::Type, position: u32) -> bool {
|
||||
self.selected.get() == position
|
||||
}
|
||||
|
||||
fn select_item(&self, model: &Self::Type, position: u32, _unselect_rest: bool) -> bool {
|
||||
model.select(position);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct FrctlCategory(ObjectSubclass<imp::FrctlCategory>)
|
||||
@implements gio::ListModel, gtk::SelectionModel;
|
||||
}
|
||||
|
||||
// TODO: sort the rooms in FrctlCategory, i guess we want last active room first
|
||||
impl FrctlCategory {
|
||||
pub fn new(client: Client, name: CategoryName) -> Self {
|
||||
let obj =
|
||||
glib::Object::new(&[("display-name", &name)]).expect("Failed to create FrctlCategory");
|
||||
// We don't need to set the client as a GObject property since it's used only internally
|
||||
let priv_ = imp::FrctlCategory::from_instance(&obj);
|
||||
priv_.client.set(client).unwrap();
|
||||
obj
|
||||
}
|
||||
|
||||
pub fn select(&self, position: u32) {
|
||||
let priv_ = imp::FrctlCategory::from_instance(self);
|
||||
let old_position = priv_.selected.get();
|
||||
|
||||
if position == old_position {
|
||||
return;
|
||||
}
|
||||
|
||||
priv_.selected.set(position);
|
||||
|
||||
if old_position == u32::MAX {
|
||||
self.selection_changed(position, 1);
|
||||
} else if position == u32::MAX {
|
||||
self.selection_changed(old_position, 1);
|
||||
} else if position < old_position {
|
||||
self.selection_changed(position, old_position - position + 1);
|
||||
} else {
|
||||
self.selection_changed(old_position, position - old_position + 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unselect(&self) {
|
||||
self.select(u32::MAX);
|
||||
}
|
||||
|
||||
pub fn update(&self, room_id: &RoomId) {
|
||||
let priv_ = imp::FrctlCategory::from_instance(self);
|
||||
let category_type = priv_.name.get().get_room_type();
|
||||
let client = priv_.client.get().unwrap();
|
||||
let room: Option<Room> = match category_type {
|
||||
RoomType::Invited => client.get_invited_room(room_id).map(Into::into),
|
||||
RoomType::Joined => client.get_joined_room(room_id).map(Into::into),
|
||||
RoomType::Left => client.get_left_room(room_id).map(Into::into),
|
||||
};
|
||||
|
||||
let mut found = false;
|
||||
if let Some((_, room_obj)) = priv_.map.borrow().get(room_id) {
|
||||
if room.is_some() {
|
||||
room_obj.update();
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if found && room.is_none() {
|
||||
if let Some((position, _)) = priv_.map.borrow_mut().remove(&room_id.clone()) {
|
||||
priv_.list.borrow_mut().remove(position as usize);
|
||||
self.items_changed(position, 1, 0);
|
||||
}
|
||||
} else if !found {
|
||||
if let Some(room) = room {
|
||||
self.append(&room);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(&self, room: &Room) {
|
||||
let priv_ = imp::FrctlCategory::from_instance(self);
|
||||
let room_id = room.room_id();
|
||||
let room_obj = FrctlRoom::new(room);
|
||||
let index = {
|
||||
let mut map = priv_.map.borrow_mut();
|
||||
let mut list = priv_.list.borrow_mut();
|
||||
let index = list.len();
|
||||
map.insert(room_id.clone(), (index as u32, room_obj));
|
||||
list.push(room_id.clone());
|
||||
index
|
||||
};
|
||||
self.items_changed(index as u32, 0, 1);
|
||||
}
|
||||
|
||||
pub fn append_batch(&self, rooms: Vec<Room>) {
|
||||
let priv_ = imp::FrctlCategory::from_instance(self);
|
||||
let index = {
|
||||
let mut map = priv_.map.borrow_mut();
|
||||
let mut list = priv_.list.borrow_mut();
|
||||
let index = list.len();
|
||||
let mut position = index;
|
||||
for room in &rooms {
|
||||
let room_id = room.room_id();
|
||||
let room_obj = FrctlRoom::new(room);
|
||||
map.insert(room_id.clone(), (position as u32, room_obj));
|
||||
list.push(room_id.clone());
|
||||
position += 1;
|
||||
}
|
||||
index
|
||||
};
|
||||
self.items_changed(index as u32, 0, rooms.len() as u32);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
use crate::session::sidebar::FrctlCategory;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::{self, gio, glib, glib::clone, prelude::*};
|
||||
use matrix_sdk::identifiers::RoomId;
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use gio::subclass::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FrctlCategoryList {
|
||||
pub list: RefCell<Vec<FrctlCategory>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for FrctlCategoryList {
|
||||
const NAME: &'static str = "FrctlCategoryList";
|
||||
type Type = super::FrctlCategoryList;
|
||||
type ParentType = glib::Object;
|
||||
type Interfaces = (gio::ListModel,);
|
||||
}
|
||||
|
||||
impl ObjectImpl for FrctlCategoryList {}
|
||||
|
||||
impl ListModelImpl for FrctlCategoryList {
|
||||
fn get_item_type(&self, _list_model: &Self::Type) -> glib::Type {
|
||||
FrctlCategory::static_type()
|
||||
}
|
||||
fn get_n_items(&self, _list_model: &Self::Type) -> u32 {
|
||||
self.list.borrow().len() as u32
|
||||
}
|
||||
fn get_item(&self, _list_model: &Self::Type, position: u32) -> Option<glib::Object> {
|
||||
self.list
|
||||
.borrow()
|
||||
.get(position as usize)
|
||||
.map(glib::object::Cast::upcast_ref::<glib::Object>)
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct FrctlCategoryList(ObjectSubclass<imp::FrctlCategoryList>)
|
||||
@implements gio::ListModel;
|
||||
}
|
||||
// TODO allow moving between categories
|
||||
// TODO allow selection only in one category
|
||||
|
||||
impl FrctlCategoryList {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new(&[]).expect("Failed to create FrctlCategoryList")
|
||||
}
|
||||
|
||||
pub fn update(&self, room_id: &RoomId) {
|
||||
let priv_ = imp::FrctlCategoryList::from_instance(self);
|
||||
let list = priv_.list.borrow();
|
||||
for category in list.iter() {
|
||||
category.update(room_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(&self, category: FrctlCategory) {
|
||||
let priv_ = imp::FrctlCategoryList::from_instance(self);
|
||||
let index = {
|
||||
let mut list = priv_.list.borrow_mut();
|
||||
category.connect_selection_changed(
|
||||
clone!(@weak self as obj => move |category, position, _| {
|
||||
if category.is_selected(position) {
|
||||
obj.unselect_other_lists(&category);
|
||||
}
|
||||
}),
|
||||
);
|
||||
list.push(category);
|
||||
list.len() - 1
|
||||
};
|
||||
self.items_changed(index as u32, 0, 1);
|
||||
}
|
||||
|
||||
fn unselect_other_lists(&self, category: &FrctlCategory) {
|
||||
let priv_ = imp::FrctlCategoryList::from_instance(self);
|
||||
let list = priv_.list.borrow();
|
||||
|
||||
for item in list.iter() {
|
||||
if item != category {
|
||||
item.unselect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append_batch(&self, batch: &[FrctlCategory]) {
|
||||
let priv_ = imp::FrctlCategoryList::from_instance(self);
|
||||
let index = {
|
||||
let mut list = priv_.list.borrow_mut();
|
||||
let index = list.len();
|
||||
for category in batch.iter() {
|
||||
category.connect_selection_changed(
|
||||
clone!(@weak self as obj => move |category, position, _| {
|
||||
if category.is_selected(position) {
|
||||
obj.unselect_other_lists(&category);
|
||||
}
|
||||
}),
|
||||
);
|
||||
list.push(category.clone());
|
||||
}
|
||||
index
|
||||
};
|
||||
self.items_changed(index as u32, 0, batch.len() as u32);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
use crate::session::sidebar::CategoryName;
|
||||
use adw;
|
||||
use adw::subclass::prelude::BinImpl;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::{self, prelude::*};
|
||||
use gtk::{glib, CompositeTemplate};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
#[derive(Debug, CompositeTemplate)]
|
||||
#[template(resource = "/org/gnome/FractalNext/sidebar-category-row.ui")]
|
||||
pub struct FrctlSidebarCategoryRow {
|
||||
#[template_child]
|
||||
pub display_name: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub arrow: TemplateChild<gtk::Image>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for FrctlSidebarCategoryRow {
|
||||
const NAME: &'static str = "FrctlSidebarCategoryRow";
|
||||
type Type = super::FrctlSidebarCategoryRow;
|
||||
type ParentType = adw::Bin;
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
display_name: TemplateChild::default(),
|
||||
arrow: TemplateChild::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Self::bind_template(klass);
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for FrctlSidebarCategoryRow {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpec::enum_(
|
||||
"display-name",
|
||||
"Display Name",
|
||||
"The display name of this category",
|
||||
CategoryName::static_type(),
|
||||
CategoryName::default() as i32,
|
||||
glib::ParamFlags::WRITABLE,
|
||||
),
|
||||
glib::ParamSpec::boolean(
|
||||
"expanded",
|
||||
"Expanded",
|
||||
"Wheter this category is expanded or not",
|
||||
true,
|
||||
glib::ParamFlags::WRITABLE,
|
||||
),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(
|
||||
&self,
|
||||
_obj: &Self::Type,
|
||||
_id: usize,
|
||||
value: &glib::Value,
|
||||
pspec: &glib::ParamSpec,
|
||||
) {
|
||||
match pspec.get_name() {
|
||||
"display-name" => {
|
||||
let display_name: CategoryName = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`")
|
||||
.expect("A room always needs a display name");
|
||||
self.display_name.set_label(&display_name.to_string());
|
||||
}
|
||||
"expanded" => {
|
||||
let expanded = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`")
|
||||
.unwrap();
|
||||
if expanded {
|
||||
//self.add_css_class("expanded");
|
||||
} else {
|
||||
//self.remove_css_class("expanded");
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for FrctlSidebarCategoryRow {}
|
||||
impl BinImpl for FrctlSidebarCategoryRow {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct FrctlSidebarCategoryRow(ObjectSubclass<imp::FrctlSidebarCategoryRow>)
|
||||
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
|
||||
}
|
||||
|
||||
impl FrctlSidebarCategoryRow {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new(&[]).expect("Failed to create FrctlSidebarCategoryRow")
|
||||
}
|
||||
}
|
|
@ -1,8 +1,20 @@
|
|||
mod category;
|
||||
mod category_list;
|
||||
mod category_row;
|
||||
mod room;
|
||||
mod room_row;
|
||||
|
||||
use self::category::{CategoryName, FrctlCategory};
|
||||
use self::category_list::FrctlCategoryList;
|
||||
use self::category_row::FrctlSidebarCategoryRow;
|
||||
use self::room::{FrctlRoom, HighlightFlags};
|
||||
use self::room_row::FrctlSidebarRoomRow;
|
||||
|
||||
use adw;
|
||||
use adw::subclass::prelude::BinImpl;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::{self, prelude::*};
|
||||
use gtk::{glib, glib::SyncSender, CompositeTemplate};
|
||||
use gtk::{glib, glib::clone, glib::SyncSender, CompositeTemplate};
|
||||
use matrix_sdk::{identifiers::RoomId, Client};
|
||||
|
||||
mod imp {
|
||||
|
@ -35,6 +47,9 @@ mod imp {
|
|||
}
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
FrctlCategoryList::static_type();
|
||||
FrctlSidebarRoomRow::static_type();
|
||||
FrctlSidebarCategoryRow::static_type();
|
||||
Self::bind_template(klass);
|
||||
}
|
||||
|
||||
|
@ -107,10 +122,42 @@ impl FrctlSidebar {
|
|||
/// Sets up the required channel to recive async updates from the `Client`
|
||||
pub fn setup_channel(&self) -> SyncSender<RoomId> {
|
||||
let (sender, receiver) = glib::MainContext::sync_channel::<RoomId>(Default::default(), 100);
|
||||
receiver.attach(None, move |_room_id| {
|
||||
//TODO: actually do something: update the message GListModel
|
||||
glib::Continue(true)
|
||||
});
|
||||
|
||||
receiver.attach(
|
||||
None,
|
||||
clone!(@weak self as obj => move |room_id| {
|
||||
obj.get_list_model().update(&room_id);
|
||||
glib::Continue(true)
|
||||
}),
|
||||
);
|
||||
sender
|
||||
}
|
||||
|
||||
/// Loads the state from the `Store`
|
||||
pub fn load(&self, client: &Client) {
|
||||
let list = self.get_list_model();
|
||||
// TODO: Add list for user defined categories e.g. favorite
|
||||
let invited = FrctlCategory::new(client.clone(), CategoryName::Invited);
|
||||
let joined = FrctlCategory::new(client.clone(), CategoryName::Normal);
|
||||
let left = FrctlCategory::new(client.clone(), CategoryName::Left);
|
||||
|
||||
invited.append_batch(client.invited_rooms().into_iter().map(Into::into).collect());
|
||||
joined.append_batch(client.joined_rooms().into_iter().map(Into::into).collect());
|
||||
left.append_batch(client.left_rooms().into_iter().map(Into::into).collect());
|
||||
|
||||
list.append_batch(&[invited, joined, left]);
|
||||
}
|
||||
|
||||
fn get_list_model(&self) -> FrctlCategoryList {
|
||||
imp::FrctlSidebar::from_instance(self)
|
||||
.listview
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<gtk::NoSelection>()
|
||||
.unwrap()
|
||||
.get_model()
|
||||
.unwrap()
|
||||
.downcast::<FrctlCategoryList>()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
use gtk::subclass::prelude::*;
|
||||
use gtk::{self, prelude::*};
|
||||
use gtk::{gio, glib};
|
||||
use gtk_macros::spawn;
|
||||
use matrix_sdk::room::Room;
|
||||
|
||||
#[glib::gflags("HighlightFlags")]
|
||||
pub enum HighlightFlags {
|
||||
#[glib::gflags(name = "NONE")]
|
||||
NONE = 0b00000000,
|
||||
#[glib::gflags(name = "HIGHLIGHT")]
|
||||
HIGHLIGHT = 0b00000001,
|
||||
#[glib::gflags(name = "BOLD")]
|
||||
BOLD = 0b00000010,
|
||||
#[glib::gflags(skip)]
|
||||
HIGHLIGHT_BOLD = Self::HIGHLIGHT.bits() | Self::BOLD.bits(),
|
||||
}
|
||||
|
||||
impl Default for HighlightFlags {
|
||||
fn default() -> Self {
|
||||
HighlightFlags::NONE
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FrctlRoom {
|
||||
pub room: OnceCell<Room>,
|
||||
pub name: RefCell<Option<String>>,
|
||||
pub avatar: RefCell<Option<gio::LoadableIcon>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for FrctlRoom {
|
||||
const NAME: &'static str = "FrctlRoom";
|
||||
type Type = super::FrctlRoom;
|
||||
type ParentType = glib::Object;
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
room: OnceCell::new(),
|
||||
name: RefCell::new(Some("Unknown".to_string())),
|
||||
avatar: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for FrctlRoom {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpec::boxed(
|
||||
"room",
|
||||
"Room",
|
||||
"The matrix room",
|
||||
BoxedRoom::static_type(),
|
||||
glib::ParamFlags::WRITABLE | glib::ParamFlags::CONSTRUCT_ONLY,
|
||||
),
|
||||
glib::ParamSpec::string(
|
||||
"display-name",
|
||||
"Display Name",
|
||||
"The display name of this room",
|
||||
None,
|
||||
glib::ParamFlags::READABLE | glib::ParamFlags::EXPLICIT_NOTIFY,
|
||||
),
|
||||
glib::ParamSpec::object(
|
||||
"avatar",
|
||||
"Avatar",
|
||||
"The url of the avatar of this room",
|
||||
gio::LoadableIcon::static_type(),
|
||||
glib::ParamFlags::READABLE | glib::ParamFlags::EXPLICIT_NOTIFY,
|
||||
),
|
||||
glib::ParamSpec::flags(
|
||||
"highlight",
|
||||
"Highlight",
|
||||
"How this room is highlighted",
|
||||
HighlightFlags::static_type(),
|
||||
HighlightFlags::default().bits(),
|
||||
glib::ParamFlags::READABLE | glib::ParamFlags::EXPLICIT_NOTIFY,
|
||||
),
|
||||
glib::ParamSpec::uint64(
|
||||
"notification-count",
|
||||
"Notification count",
|
||||
"The notification count of this room",
|
||||
std::u64::MIN,
|
||||
std::u64::MAX,
|
||||
0,
|
||||
glib::ParamFlags::READABLE | glib::ParamFlags::EXPLICIT_NOTIFY,
|
||||
),
|
||||
]
|
||||
// TODO: add other needed properties e.g. is_direct, and category
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(
|
||||
&self,
|
||||
obj: &Self::Type,
|
||||
_id: usize,
|
||||
value: &glib::Value,
|
||||
pspec: &glib::ParamSpec,
|
||||
) {
|
||||
match pspec.get_name() {
|
||||
"room" => {
|
||||
let room = value
|
||||
.get_some::<&BoxedRoom>()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
let _ = self.room.set(room.clone().0);
|
||||
obj.update();
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property(
|
||||
&self,
|
||||
_obj: &Self::Type,
|
||||
_id: usize,
|
||||
pspec: &glib::ParamSpec,
|
||||
) -> glib::Value {
|
||||
let room = self.room.get().unwrap();
|
||||
match pspec.get_name() {
|
||||
"display-name" => self.name.borrow().to_value(),
|
||||
"avatar" => self.avatar.borrow().to_value(),
|
||||
"highlight" => {
|
||||
let count = room.unread_notification_counts().highlight_count;
|
||||
|
||||
// TODO: how do we know when to set the row to be bold
|
||||
if count > 0 {
|
||||
HighlightFlags::HIGHLIGHT
|
||||
} else {
|
||||
HighlightFlags::NONE
|
||||
}
|
||||
.to_value()
|
||||
}
|
||||
"notification-count" => {
|
||||
let highlight = room.unread_notification_counts().highlight_count;
|
||||
let notification = room.unread_notification_counts().notification_count;
|
||||
|
||||
if highlight > 0 {
|
||||
highlight
|
||||
} else {
|
||||
notification
|
||||
}
|
||||
.to_value()
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct FrctlRoom(ObjectSubclass<imp::FrctlRoom>);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, glib::GBoxed)]
|
||||
#[gboxed(type_name = "BoxedRoom")]
|
||||
struct BoxedRoom(Room);
|
||||
|
||||
impl FrctlRoom {
|
||||
pub fn new(room: &Room) -> Self {
|
||||
glib::Object::new(&[("room", &BoxedRoom(room.clone()))])
|
||||
.expect("Failed to create FrctlRoom")
|
||||
}
|
||||
|
||||
/// This should be called when any field on the Room has changed
|
||||
pub fn update(&self) {
|
||||
self.load_display_name();
|
||||
self.load_avatar();
|
||||
self.notify("highlight");
|
||||
self.notify("notification-count");
|
||||
}
|
||||
|
||||
fn load_display_name(&self) {
|
||||
let obj = self.downgrade();
|
||||
spawn!(async move {
|
||||
if let Some(obj) = obj.upgrade() {
|
||||
let priv_ = imp::FrctlRoom::from_instance(&obj);
|
||||
let name = &priv_.name;
|
||||
let new_name = priv_.room.get().unwrap().display_name().await.ok();
|
||||
|
||||
if *name.borrow() != new_name {
|
||||
name.replace(new_name);
|
||||
obj.notify("display-name");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn load_avatar(&self) {
|
||||
// TODO: load avatar and create a LoadableIcon
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
use crate::session::sidebar::HighlightFlags;
|
||||
use adw;
|
||||
use adw::subclass::prelude::BinImpl;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::{self, prelude::*};
|
||||
use gtk::{gio, glib, CompositeTemplate};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
#[derive(Debug, CompositeTemplate)]
|
||||
#[template(resource = "/org/gnome/FractalNext/sidebar-room-row.ui")]
|
||||
pub struct FrctlSidebarRoomRow {
|
||||
#[template_child]
|
||||
pub avatar: TemplateChild<adw::Avatar>,
|
||||
#[template_child]
|
||||
pub display_name: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub notification_count: TemplateChild<gtk::Label>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for FrctlSidebarRoomRow {
|
||||
const NAME: &'static str = "FrctlSidebarRoomRow";
|
||||
type Type = super::FrctlSidebarRoomRow;
|
||||
type ParentType = adw::Bin;
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
avatar: TemplateChild::default(),
|
||||
display_name: TemplateChild::default(),
|
||||
notification_count: TemplateChild::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Self::bind_template(klass);
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for FrctlSidebarRoomRow {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpec::object(
|
||||
"avatar",
|
||||
"Avatar",
|
||||
"The url of the avatar of this room",
|
||||
gio::LoadableIcon::static_type(),
|
||||
glib::ParamFlags::WRITABLE,
|
||||
),
|
||||
glib::ParamSpec::string(
|
||||
"display-name",
|
||||
"Display Name",
|
||||
"The display name of this room",
|
||||
None,
|
||||
glib::ParamFlags::WRITABLE,
|
||||
),
|
||||
glib::ParamSpec::flags(
|
||||
"highlight",
|
||||
"Highlight",
|
||||
"What type of highligh this room needs",
|
||||
HighlightFlags::static_type(),
|
||||
HighlightFlags::default().bits(),
|
||||
glib::ParamFlags::WRITABLE,
|
||||
),
|
||||
glib::ParamSpec::uint64(
|
||||
"notification-count",
|
||||
"Notification count",
|
||||
"The notification count of this room",
|
||||
std::u64::MIN,
|
||||
std::u64::MAX,
|
||||
0,
|
||||
glib::ParamFlags::WRITABLE,
|
||||
),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(
|
||||
&self,
|
||||
_obj: &Self::Type,
|
||||
_id: usize,
|
||||
value: &glib::Value,
|
||||
pspec: &glib::ParamSpec,
|
||||
) {
|
||||
match pspec.get_name() {
|
||||
"avatar" => {
|
||||
let _avatar = value
|
||||
.get::<gio::LoadableIcon>()
|
||||
.expect("type conformity checked by `Object::set_property`");
|
||||
// TODO: set custom avatar https://gitlab.gnome.org/exalm/libadwaita/-/issues/29
|
||||
}
|
||||
"display-name" => {
|
||||
let display_name = value
|
||||
.get()
|
||||
.expect("type conformity checked by `Object::set_property`")
|
||||
.expect("A room always needs a display name");
|
||||
self.display_name.set_label(display_name);
|
||||
}
|
||||
"highlight" => {
|
||||
let flags = value
|
||||
.get::<HighlightFlags>()
|
||||
.expect("type conformity checked by `Object::set_property`")
|
||||
.unwrap();
|
||||
match flags {
|
||||
HighlightFlags::NONE => {
|
||||
self.notification_count.remove_css_class("highlight");
|
||||
self.display_name.remove_css_class("bold");
|
||||
}
|
||||
HighlightFlags::HIGHLIGHT => {
|
||||
self.notification_count.add_css_class("highlight");
|
||||
self.display_name.remove_css_class("bold");
|
||||
}
|
||||
HighlightFlags::BOLD => {
|
||||
self.display_name.add_css_class("bold");
|
||||
self.notification_count.remove_css_class("highlight");
|
||||
}
|
||||
HighlightFlags::HIGHLIGHT_BOLD => {
|
||||
self.notification_count.add_css_class("highlight");
|
||||
self.display_name.add_css_class("bold");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
"notification-count" => {
|
||||
let count = value
|
||||
.get::<u64>()
|
||||
.expect("type conformity checked by `Object::set_property`")
|
||||
.unwrap();
|
||||
self.notification_count.set_label(&count.to_string());
|
||||
self.notification_count.set_visible(count > 0);
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for FrctlSidebarRoomRow {}
|
||||
impl BinImpl for FrctlSidebarRoomRow {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct FrctlSidebarRoomRow(ObjectSubclass<imp::FrctlSidebarRoomRow>)
|
||||
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
|
||||
}
|
||||
|
||||
impl FrctlSidebarRoomRow {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new(&[]).expect("Failed to create FrctlSidebarRoomRow")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue