components: Add widget for user pills

This commit is contained in:
Julian Sparber 2021-05-20 09:22:47 +02:00
parent 13871637e3
commit c2f73e551f
8 changed files with 156 additions and 1 deletions

View file

@ -17,6 +17,7 @@
<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" preprocess="xml-stripblanks" alias="context-menu-bin.ui">ui/context-menu-bin.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="user-pill.ui">ui/user-pill.ui</file>
<file compressed="true">style.css</file>
<file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/welcome.svg</file>

View file

@ -3,6 +3,12 @@
font-weight: bold;
}
.user-pill {
border-radius: 9999px;
background-color: alpha(@theme_text_color, 0.1);
padding-right: 6px;
}
/* Login */
.login {
min-width: 250px;

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="UserPill" parent="AdwBin">
<property name="focusable">True</property>
<style>
<class name="user-pill"/>
</style>
<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">middle</property>
<property name="max-width-chars">30</property>
<binding name="label">
<lookup name="display-name">
<lookup name="user">UserPill</lookup>
</lookup>
</binding>
</object>
</child>
</object>
</child>
</template>
</interface>

View file

@ -21,12 +21,14 @@ data/resources/ui/sidebar-category-row.ui
data/resources/ui/sidebar-item.ui
data/resources/ui/sidebar-room-row.ui
data/resources/ui/sidebar.ui
data/resources/ui/user-pill.ui
data/resources/ui/window.ui
# Rust files
src/application.rs
src/components/context_menu_bin.rs
src/components/mod.rs
src/components/user_pill.rs
src/login.rs
src/main.rs
src/secret.rs

View file

@ -1,3 +1,5 @@
mod context_menu_bin;
mod user_pill;
pub use self::context_menu_bin::{ContextMenuBin, ContextMenuBinImpl};
pub use self::user_pill::UserPill;

111
src/components/user_pill.rs Normal file
View file

@ -0,0 +1,111 @@
use adw::subclass::prelude::*;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::{glib, CompositeTemplate};
use crate::session::User;
mod imp {
use super::*;
use glib::subclass::InitializingObject;
use std::cell::RefCell;
#[derive(Debug, Default, CompositeTemplate)]
#[template(resource = "/org/gnome/FractalNext/user-pill.ui")]
pub struct UserPill {
/// The user displayed by this widget
pub user: RefCell<Option<User>>,
#[template_child]
pub display_name: TemplateChild<gtk::Label>,
#[template_child]
pub avatar: TemplateChild<adw::Avatar>,
}
#[glib::object_subclass]
impl ObjectSubclass for UserPill {
const NAME: &'static str = "UserPill";
type Type = super::UserPill;
type ParentType = adw::Bin;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for UserPill {
fn properties() -> &'static [glib::ParamSpec] {
use once_cell::sync::Lazy;
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpec::new_object(
"user",
"User",
"The user displayed by this widget",
User::static_type(),
glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
)]
});
PROPERTIES.as_ref()
}
fn set_property(
&self,
obj: &Self::Type,
_id: usize,
value: &glib::Value,
pspec: &glib::ParamSpec,
) {
match pspec.name() {
"user" => {
let user = value.get().unwrap();
obj.set_user(user);
}
_ => unimplemented!(),
}
}
fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"user" => obj.user().to_value(),
_ => unimplemented!(),
}
}
}
impl WidgetImpl for UserPill {}
impl BinImpl for UserPill {}
}
glib::wrapper! {
pub struct UserPill(ObjectSubclass<imp::UserPill>)
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
}
/// A widget displaying a `User`
impl UserPill {
pub fn new() -> Self {
glib::Object::new(&[]).expect("Failed to create UserPill")
}
pub fn set_user(&self, user: Option<User>) {
let priv_ = imp::UserPill::from_instance(self);
if *priv_.user.borrow() == user {
return;
}
priv_.user.replace(user);
self.notify("user");
}
pub fn user(&self) -> Option<User> {
let priv_ = imp::UserPill::from_instance(self);
priv_.user.borrow().clone()
}
}

View file

@ -22,6 +22,7 @@ sources = files(
'application.rs',
'components/context_menu_bin.rs',
'components/mod.rs',
'components/user_pill.rs',
'config.rs',
'main.rs',
'window.rs',

View file

@ -8,7 +8,7 @@ use self::categories::Categories;
use self::content::Content;
use self::room::Room;
use self::sidebar::Sidebar;
use self::user::User;
pub use self::user::User;
use crate::event_from_sync_event;
use crate::secret;