18 changed files with 1173 additions and 251 deletions
@ -1,94 +0,0 @@
|
||||
use adw::{prelude::*, subclass::prelude::*}; |
||||
use gtk::glib; |
||||
|
||||
use crate::session::model::{MemberRole, PowerLevel, POWER_LEVEL_MAX, POWER_LEVEL_MIN}; |
||||
|
||||
mod imp { |
||||
use std::cell::Cell; |
||||
|
||||
use super::*; |
||||
|
||||
#[derive(Debug, Default, glib::Properties)] |
||||
#[properties(wrapper_type = super::PowerLevelBadge)] |
||||
pub struct PowerLevelBadge { |
||||
pub label: gtk::Label, |
||||
/// The power level displayed by this badge.
|
||||
#[property(get, set = Self::set_power_level, explicit_notify, minimum = POWER_LEVEL_MIN, maximum = POWER_LEVEL_MAX)] |
||||
pub power_level: Cell<PowerLevel>, |
||||
} |
||||
|
||||
#[glib::object_subclass] |
||||
impl ObjectSubclass for PowerLevelBadge { |
||||
const NAME: &'static str = "PowerLevelBadge"; |
||||
type Type = super::PowerLevelBadge; |
||||
type ParentType = adw::Bin; |
||||
|
||||
fn class_init(klass: &mut Self::Class) { |
||||
klass.set_css_name("power-level-badge"); |
||||
} |
||||
} |
||||
|
||||
#[glib::derived_properties] |
||||
impl ObjectImpl for PowerLevelBadge { |
||||
fn constructed(&self) { |
||||
self.parent_constructed(); |
||||
let obj = self.obj(); |
||||
|
||||
obj.set_child(Some(&self.label)); |
||||
} |
||||
} |
||||
|
||||
impl WidgetImpl for PowerLevelBadge {} |
||||
impl BinImpl for PowerLevelBadge {} |
||||
|
||||
impl PowerLevelBadge { |
||||
/// Set the power level this badge displays.
|
||||
fn set_power_level(&self, power_level: PowerLevel) { |
||||
let obj = self.obj(); |
||||
obj.update_badge(power_level); |
||||
|
||||
self.power_level.set(power_level); |
||||
obj.notify_power_level(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
glib::wrapper! { |
||||
/// Inline widget displaying a badge with a power level.
|
||||
///
|
||||
/// The badge displays admin for a power level of 100 and mod for levels
|
||||
/// over or equal to 50.
|
||||
pub struct PowerLevelBadge(ObjectSubclass<imp::PowerLevelBadge>) |
||||
@extends gtk::Widget, adw::Bin; |
||||
} |
||||
|
||||
impl PowerLevelBadge { |
||||
pub fn new() -> Self { |
||||
glib::Object::new() |
||||
} |
||||
|
||||
/// Update the badge for the given power level.
|
||||
fn update_badge(&self, power_level: PowerLevel) { |
||||
let label = &self.imp().label; |
||||
let role = MemberRole::from(power_level); |
||||
|
||||
match role { |
||||
MemberRole::Admin => { |
||||
label.set_text(&format!("{role} {power_level}")); |
||||
self.add_css_class("admin"); |
||||
self.remove_css_class("mod"); |
||||
} |
||||
MemberRole::Mod => { |
||||
label.set_text(&format!("{role} {power_level}")); |
||||
self.add_css_class("mod"); |
||||
self.remove_css_class("admin"); |
||||
} |
||||
MemberRole::Peasant => { |
||||
label.set_text(&power_level.to_string()); |
||||
self.remove_css_class("admin"); |
||||
self.remove_css_class("mod"); |
||||
} |
||||
}; |
||||
self.set_visible(power_level != 0); |
||||
} |
||||
} |
||||
@ -0,0 +1,112 @@
|
||||
use adw::{prelude::*, subclass::prelude::*}; |
||||
use gtk::glib; |
||||
|
||||
use crate::session::model::MemberRole; |
||||
|
||||
mod imp { |
||||
use std::cell::Cell; |
||||
|
||||
use super::*; |
||||
|
||||
#[derive(Debug, Default, glib::Properties)] |
||||
#[properties(wrapper_type = super::RoleBadge)] |
||||
pub struct RoleBadge { |
||||
pub label: gtk::Label, |
||||
/// The role displayed by this badge.
|
||||
#[property(get, set = Self::set_role, explicit_notify, builder(MemberRole::default()))] |
||||
pub role: Cell<MemberRole>, |
||||
/// Whether the role displayed by this badge is the default role.
|
||||
#[property(get)] |
||||
pub is_default_role: Cell<bool>, |
||||
} |
||||
|
||||
#[glib::object_subclass] |
||||
impl ObjectSubclass for RoleBadge { |
||||
const NAME: &'static str = "RoleBadge"; |
||||
type Type = super::RoleBadge; |
||||
type ParentType = adw::Bin; |
||||
|
||||
fn class_init(klass: &mut Self::Class) { |
||||
klass.set_css_name("role-badge"); |
||||
} |
||||
} |
||||
|
||||
#[glib::derived_properties] |
||||
impl ObjectImpl for RoleBadge { |
||||
fn constructed(&self) { |
||||
self.parent_constructed(); |
||||
let obj = self.obj(); |
||||
|
||||
obj.set_child(Some(&self.label)); |
||||
self.update_badge(); |
||||
self.update_is_default_role(); |
||||
} |
||||
} |
||||
|
||||
impl WidgetImpl for RoleBadge {} |
||||
impl BinImpl for RoleBadge {} |
||||
|
||||
impl RoleBadge { |
||||
/// Set the role displayed by this badge.
|
||||
fn set_role(&self, role: MemberRole) { |
||||
if self.role.get() == role { |
||||
return; |
||||
} |
||||
|
||||
self.role.set(role); |
||||
self.update_badge(); |
||||
self.update_is_default_role(); |
||||
self.obj().notify_role(); |
||||
} |
||||
|
||||
/// Update whether the role displayed by this badge is the default role.
|
||||
fn update_is_default_role(&self) { |
||||
let is_default = self.role.get() == MemberRole::Default; |
||||
|
||||
if self.is_default_role.get() == is_default { |
||||
return; |
||||
} |
||||
|
||||
self.is_default_role.set(is_default); |
||||
self.obj().notify_is_default_role(); |
||||
} |
||||
|
||||
/// Update the badge for the current state.
|
||||
fn update_badge(&self) { |
||||
let obj = self.obj(); |
||||
let role = self.role.get(); |
||||
|
||||
self.label.set_text(&role.to_string()); |
||||
|
||||
if role == MemberRole::Administrator { |
||||
obj.add_css_class("admin"); |
||||
} else { |
||||
obj.remove_css_class("admin"); |
||||
} |
||||
|
||||
if role == MemberRole::Moderator { |
||||
obj.add_css_class("mod"); |
||||
} else { |
||||
obj.remove_css_class("mod"); |
||||
} |
||||
|
||||
if role == MemberRole::Muted { |
||||
obj.add_css_class("muted"); |
||||
} else { |
||||
obj.remove_css_class("muted"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
glib::wrapper! { |
||||
/// Inline widget displaying a badge with the role of a room member.
|
||||
pub struct RoleBadge(ObjectSubclass<imp::RoleBadge>) |
||||
@extends gtk::Widget, adw::Bin; |
||||
} |
||||
|
||||
impl RoleBadge { |
||||
pub fn new() -> Self { |
||||
glib::Object::new() |
||||
} |
||||
} |
||||
@ -0,0 +1,385 @@
|
||||
use adw::{prelude::*, subclass::prelude::*}; |
||||
use gtk::{glib, glib::clone, CompositeTemplate}; |
||||
|
||||
use crate::{ |
||||
components::{LoadingBin, RoleBadge}, |
||||
session::model::{Permissions, PowerLevel, POWER_LEVEL_ADMIN, POWER_LEVEL_MOD}, |
||||
utils::BoundObject, |
||||
}; |
||||
|
||||
mod imp { |
||||
use std::{cell::Cell, marker::PhantomData}; |
||||
|
||||
use glib::subclass::InitializingObject; |
||||
|
||||
use super::*; |
||||
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)] |
||||
#[template(resource = "/org/gnome/Fractal/ui/components/rows/power_level_selection_row.ui")] |
||||
#[properties(wrapper_type = super::PowerLevelSelectionRow)] |
||||
pub struct PowerLevelSelectionRow { |
||||
#[template_child] |
||||
pub selected_box: TemplateChild<gtk::Box>, |
||||
#[template_child] |
||||
pub selected_level_label: TemplateChild<gtk::Label>, |
||||
#[template_child] |
||||
pub selected_role_badge: TemplateChild<RoleBadge>, |
||||
#[template_child] |
||||
pub loading_bin: TemplateChild<LoadingBin>, |
||||
#[template_child] |
||||
pub popover: TemplateChild<gtk::Popover>, |
||||
#[template_child] |
||||
pub admin_row: TemplateChild<gtk::ListBoxRow>, |
||||
#[template_child] |
||||
pub admin_selected: TemplateChild<gtk::Image>, |
||||
#[template_child] |
||||
pub mod_row: TemplateChild<gtk::ListBoxRow>, |
||||
#[template_child] |
||||
pub mod_selected: TemplateChild<gtk::Image>, |
||||
#[template_child] |
||||
pub default_row: TemplateChild<gtk::ListBoxRow>, |
||||
#[template_child] |
||||
pub default_pl_label: TemplateChild<gtk::Label>, |
||||
#[template_child] |
||||
pub default_selected: TemplateChild<gtk::Image>, |
||||
#[template_child] |
||||
pub muted_row: TemplateChild<gtk::ListBoxRow>, |
||||
#[template_child] |
||||
pub muted_pl_label: TemplateChild<gtk::Label>, |
||||
#[template_child] |
||||
pub muted_selected: TemplateChild<gtk::Image>, |
||||
#[template_child] |
||||
pub custom_row: TemplateChild<adw::SpinRow>, |
||||
#[template_child] |
||||
pub custom_adjustment: TemplateChild<gtk::Adjustment>, |
||||
#[template_child] |
||||
pub custom_confirm: TemplateChild<gtk::Button>, |
||||
/// The permissions to watch.
|
||||
#[property(get, set = Self::set_permissions, explicit_notify, nullable)] |
||||
pub permissions: BoundObject<Permissions>, |
||||
/// The selected power level.
|
||||
#[property(get, set = Self::set_selected_power_level, explicit_notify)] |
||||
pub selected_power_level: Cell<PowerLevel>, |
||||
/// Whether the row is loading.
|
||||
#[property(get = Self::is_loading, set = Self::set_is_loading)] |
||||
pub is_loading: PhantomData<bool>, |
||||
} |
||||
|
||||
#[glib::object_subclass] |
||||
impl ObjectSubclass for PowerLevelSelectionRow { |
||||
const NAME: &'static str = "PowerLevelSelectionRow"; |
||||
type Type = super::PowerLevelSelectionRow; |
||||
type ParentType = adw::ActionRow; |
||||
|
||||
fn class_init(klass: &mut Self::Class) { |
||||
Self::bind_template(klass); |
||||
Self::Type::bind_template_callbacks(klass); |
||||
} |
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) { |
||||
obj.init_template(); |
||||
} |
||||
} |
||||
|
||||
#[glib::derived_properties] |
||||
impl ObjectImpl for PowerLevelSelectionRow { |
||||
fn constructed(&self) { |
||||
self.parent_constructed(); |
||||
|
||||
self.obj() |
||||
.reset_relation(gtk::AccessibleRelation::DescribedBy); |
||||
} |
||||
} |
||||
|
||||
impl WidgetImpl for PowerLevelSelectionRow {} |
||||
impl ListBoxRowImpl for PowerLevelSelectionRow {} |
||||
impl PreferencesRowImpl for PowerLevelSelectionRow {} |
||||
|
||||
impl ActionRowImpl for PowerLevelSelectionRow { |
||||
fn activate(&self) { |
||||
if !self.is_loading() { |
||||
self.popover.popup(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl PowerLevelSelectionRow { |
||||
/// Set the permissions to watch.
|
||||
fn set_permissions(&self, permissions: Option<Permissions>) { |
||||
if self.permissions.obj() == permissions { |
||||
return; |
||||
} |
||||
let obj = self.obj(); |
||||
|
||||
self.permissions.disconnect_signals(); |
||||
|
||||
if let Some(permissions) = permissions { |
||||
let own_pl_handler = permissions.connect_own_power_level_notify( |
||||
clone!(@weak self as imp => move |_| { |
||||
imp.update(); |
||||
}), |
||||
); |
||||
let default_pl_handler = permissions.connect_default_power_level_notify( |
||||
clone!(@weak self as imp => move |_| { |
||||
imp.update_default(); |
||||
imp.update_muted(); |
||||
imp.update_selection(); |
||||
}), |
||||
); |
||||
let muted_pl_handler = permissions.connect_mute_power_level_notify( |
||||
clone!(@weak self as imp => move |_| { |
||||
imp.update_muted(); |
||||
imp.update_selection(); |
||||
}), |
||||
); |
||||
|
||||
self.permissions.set( |
||||
permissions, |
||||
vec![own_pl_handler, default_pl_handler, muted_pl_handler], |
||||
); |
||||
} |
||||
|
||||
self.update(); |
||||
self.update_selected_label(); |
||||
obj.notify_permissions(); |
||||
} |
||||
|
||||
/// Update the label of the selected power level.
|
||||
fn update_selected_label(&self) { |
||||
let Some(permissions) = self.permissions.obj() else { |
||||
return; |
||||
}; |
||||
let obj = self.obj(); |
||||
|
||||
let power_level = self.selected_power_level.get(); |
||||
let role = permissions.role(power_level); |
||||
|
||||
self.selected_role_badge.set_role(role); |
||||
self.selected_level_label |
||||
.set_label(&power_level.to_string()); |
||||
|
||||
let role_string = format!("{power_level} {role}"); |
||||
obj.update_property(&[gtk::accessible::Property::Description(&role_string)]); |
||||
} |
||||
|
||||
/// Set the selected power level.
|
||||
fn set_selected_power_level(&self, power_level: PowerLevel) { |
||||
if self.selected_power_level.get() == power_level { |
||||
return; |
||||
} |
||||
|
||||
self.selected_power_level.set(power_level); |
||||
|
||||
self.update_selected_label(); |
||||
self.update_selection(); |
||||
self.update_custom(); |
||||
self.obj().notify_selected_power_level(); |
||||
} |
||||
|
||||
/// Whether the row is loading.
|
||||
fn is_loading(&self) -> bool { |
||||
self.loading_bin.is_loading() |
||||
} |
||||
|
||||
/// Set whether the row is loading.
|
||||
fn set_is_loading(&self, loading: bool) { |
||||
if self.is_loading() == loading { |
||||
return; |
||||
} |
||||
|
||||
self.loading_bin.set_is_loading(loading); |
||||
self.obj().notify_is_loading(); |
||||
} |
||||
|
||||
/// Update the rows.
|
||||
fn update(&self) { |
||||
self.update_admin(); |
||||
self.update_mod(); |
||||
self.update_default(); |
||||
self.update_muted(); |
||||
self.update_custom(); |
||||
self.update_selection(); |
||||
} |
||||
|
||||
/// Update the admin row.
|
||||
fn update_admin(&self) { |
||||
let Some(permissions) = self.permissions.obj() else { |
||||
return; |
||||
}; |
||||
|
||||
let can_change_to_admin = permissions.own_power_level() >= POWER_LEVEL_ADMIN; |
||||
|
||||
if can_change_to_admin { |
||||
self.admin_row.set_sensitive(true); |
||||
self.admin_row.set_activatable(true); |
||||
} else { |
||||
self.admin_row.set_sensitive(false); |
||||
self.admin_row.set_activatable(false); |
||||
} |
||||
} |
||||
|
||||
/// Update the moderator row.
|
||||
fn update_mod(&self) { |
||||
let Some(permissions) = self.permissions.obj() else { |
||||
return; |
||||
}; |
||||
|
||||
let can_change_to_mod = permissions.own_power_level() >= POWER_LEVEL_MOD; |
||||
|
||||
if can_change_to_mod { |
||||
self.mod_row.set_sensitive(true); |
||||
self.mod_row.set_activatable(true); |
||||
} else { |
||||
self.mod_row.set_sensitive(false); |
||||
self.mod_row.set_activatable(false); |
||||
} |
||||
} |
||||
|
||||
/// Update the default row.
|
||||
fn update_default(&self) { |
||||
let Some(permissions) = self.permissions.obj() else { |
||||
return; |
||||
}; |
||||
|
||||
let default = permissions.default_power_level(); |
||||
self.default_pl_label.set_label(&default.to_string()); |
||||
|
||||
let can_change_to_default = permissions.own_power_level() >= default; |
||||
|
||||
if can_change_to_default { |
||||
self.default_row.set_sensitive(true); |
||||
self.default_row.set_activatable(true); |
||||
} else { |
||||
self.default_row.set_sensitive(false); |
||||
self.default_row.set_activatable(false); |
||||
} |
||||
} |
||||
|
||||
/// Update the muted row.
|
||||
fn update_muted(&self) { |
||||
let Some(permissions) = self.permissions.obj() else { |
||||
return; |
||||
}; |
||||
|
||||
let mute = permissions.mute_power_level(); |
||||
let default = permissions.default_power_level(); |
||||
|
||||
if mute >= default { |
||||
// There is no point in having the muted row since all users are muted by
|
||||
// default.
|
||||
self.muted_row.set_visible(false); |
||||
return; |
||||
} |
||||
|
||||
self.muted_pl_label.set_label(&mute.to_string()); |
||||
|
||||
let can_change_to_muted = permissions.own_power_level() >= mute; |
||||
|
||||
if can_change_to_muted { |
||||
self.muted_row.set_sensitive(true); |
||||
self.muted_row.set_activatable(true); |
||||
} else { |
||||
self.muted_row.set_sensitive(false); |
||||
self.muted_row.set_activatable(false); |
||||
} |
||||
|
||||
self.muted_row.set_visible(true); |
||||
} |
||||
|
||||
/// Update the custom row.
|
||||
fn update_custom(&self) { |
||||
let Some(permissions) = self.permissions.obj() else { |
||||
return; |
||||
}; |
||||
|
||||
self.custom_adjustment |
||||
.set_upper(permissions.own_power_level() as f64); |
||||
self.custom_adjustment |
||||
.set_value(self.selected_power_level.get() as f64); |
||||
} |
||||
|
||||
/// Update the selected row.
|
||||
fn update_selection(&self) { |
||||
let Some(permissions) = self.permissions.obj() else { |
||||
return; |
||||
}; |
||||
|
||||
let power_level = self.selected_power_level.get(); |
||||
|
||||
self.admin_selected |
||||
.set_opacity((power_level == POWER_LEVEL_ADMIN).into()); |
||||
self.mod_selected |
||||
.set_opacity((power_level == POWER_LEVEL_MOD).into()); |
||||
self.default_selected |
||||
.set_opacity((power_level == permissions.default_power_level()).into()); |
||||
self.muted_selected |
||||
.set_opacity((power_level == permissions.mute_power_level()).into()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
glib::wrapper! { |
||||
/// An `AdwActionRow` behaving like a combo box to select a room member's power level.
|
||||
pub struct PowerLevelSelectionRow(ObjectSubclass<imp::PowerLevelSelectionRow>) |
||||
@extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow, |
||||
@implements gtk::Actionable, gtk::Accessible; |
||||
} |
||||
|
||||
#[gtk::template_callbacks] |
||||
impl PowerLevelSelectionRow { |
||||
pub fn new() -> Self { |
||||
glib::Object::new() |
||||
} |
||||
|
||||
/// The custom value changed.
|
||||
#[template_callback] |
||||
fn custom_value_changed(&self) { |
||||
let imp = self.imp(); |
||||
let power_level = imp.custom_adjustment.value() as PowerLevel; |
||||
let can_confirm = power_level != self.selected_power_level(); |
||||
|
||||
imp.custom_confirm.set_sensitive(can_confirm); |
||||
} |
||||
|
||||
/// The custom value was confirmed.
|
||||
#[template_callback] |
||||
fn custom_value_confirmed(&self) { |
||||
let imp = self.imp(); |
||||
let power_level = imp.custom_adjustment.value() as PowerLevel; |
||||
|
||||
imp.popover.popdown(); |
||||
self.set_selected_power_level(power_level); |
||||
} |
||||
|
||||
/// A row was activated.
|
||||
#[template_callback] |
||||
fn row_activated(&self, row: >k::ListBoxRow) { |
||||
let Some(permissions) = self.permissions() else { |
||||
return; |
||||
}; |
||||
let imp = self.imp(); |
||||
|
||||
let power_level = match row.index() { |
||||
0 => POWER_LEVEL_ADMIN, |
||||
1 => POWER_LEVEL_MOD, |
||||
2 => permissions.default_power_level(), |
||||
3 => permissions.mute_power_level(), |
||||
_ => return, |
||||
}; |
||||
|
||||
imp.popover.popdown(); |
||||
self.set_selected_power_level(power_level); |
||||
} |
||||
|
||||
/// The popover's visibility changed.
|
||||
#[template_callback] |
||||
fn popover_visible(&self) { |
||||
let is_visible = self.imp().popover.is_visible(); |
||||
|
||||
if is_visible { |
||||
self.add_css_class("has-open-popup"); |
||||
} else { |
||||
self.remove_css_class("has-open-popup"); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,228 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<interface> |
||||
<template class="PowerLevelSelectionRow" parent="AdwActionRow"> |
||||
<property name="selectable">False</property> |
||||
<property name="activatable">True</property> |
||||
<property name="accessible-role">combo-box</property> |
||||
<style> |
||||
<class name="combo" /> |
||||
<class name="role-selection-row" /> |
||||
</style> |
||||
<child> |
||||
<object class="GtkBox" id="selected_box"> |
||||
<property name="accessible-role">group</property> |
||||
<property name="spacing">12</property> |
||||
<property name="margin-end">6</property> |
||||
<child> |
||||
<object class="GtkLabel" id="selected_level_label" /> |
||||
</child> |
||||
<child> |
||||
<object class="RoleBadge" id="selected_role_badge"> |
||||
<property name="valign">center</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkBox" id="arrow_box"> |
||||
<property name="valign">center</property> |
||||
<child> |
||||
<object class="LoadingBin" id="loading_bin"> |
||||
<property name="child"> |
||||
<object class="GtkImage"> |
||||
<property name="icon_name">pan-down-symbolic</property> |
||||
<property name="accessible-role">presentation</property> |
||||
<style> |
||||
<class name="dropdown-arrow" /> |
||||
</style> |
||||
</object> |
||||
</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkPopover" id="popover"> |
||||
<signal name="notify::visible" handler="popover_visible" swapped="true" /> |
||||
<style> |
||||
<class name="menu" /> |
||||
</style> |
||||
<property name="child"> |
||||
<object class="GtkScrolledWindow"> |
||||
<property name="hscrollbar_policy">never</property> |
||||
<property name="max-content-height">400</property> |
||||
<property name="propagate-natural-width">True</property> |
||||
<property name="propagate-natural-height">True</property> |
||||
<property name="child"> |
||||
<object class="GtkBox"> |
||||
<property name="orientation">vertical</property> |
||||
<child> |
||||
<object class="GtkListBox"> |
||||
<signal name="row-activated" handler="row_activated" swapped="true" /> |
||||
<child> |
||||
<object class="GtkListBoxRow" id="admin_row"> |
||||
<property name="selectable">False</property> |
||||
<property name="child"> |
||||
<object class="GtkBox"> |
||||
<property name="accessible-role">group</property> |
||||
<property name="spacing">6</property> |
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">100</property> |
||||
<property name="width-chars">3</property> |
||||
<property name="xalign">1.0</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="RoleBadge"> |
||||
<property name="role">administrator</property> |
||||
<property name="valign">center</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkImage" id="admin_selected"> |
||||
<property name="icon-name">object-select-symbolic</property> |
||||
<property name="accessible-role">presentation</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkListBoxRow" id="mod_row"> |
||||
<property name="selectable">False</property> |
||||
<property name="child"> |
||||
<object class="GtkBox"> |
||||
<property name="accessible-role">group</property> |
||||
<property name="spacing">6</property> |
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">50</property> |
||||
<property name="width-chars">3</property> |
||||
<property name="xalign">1.0</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="RoleBadge"> |
||||
<property name="role">moderator</property> |
||||
<property name="valign">center</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkImage" id="mod_selected"> |
||||
<property name="icon-name">object-select-symbolic</property> |
||||
<property name="accessible-role">presentation</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkListBoxRow" id="default_row"> |
||||
<property name="selectable">False</property> |
||||
<property name="child"> |
||||
<object class="GtkBox"> |
||||
<property name="accessible-role">group</property> |
||||
<property name="spacing">6</property> |
||||
<child> |
||||
<object class="GtkLabel" id="default_pl_label"> |
||||
<property name="width-chars">3</property> |
||||
<property name="xalign">1.0</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="RoleBadge"> |
||||
<property name="role">default</property> |
||||
<property name="valign">center</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkImage" id="default_selected"> |
||||
<property name="icon-name">object-select-symbolic</property> |
||||
<property name="accessible-role">presentation</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkListBoxRow" id="muted_row"> |
||||
<property name="selectable">False</property> |
||||
<property name="child"> |
||||
<object class="GtkBox"> |
||||
<property name="accessible-role">group</property> |
||||
<property name="spacing">6</property> |
||||
<child> |
||||
<object class="GtkLabel" id="muted_pl_label"> |
||||
<property name="width-chars">3</property> |
||||
<property name="xalign">1.0</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="RoleBadge"> |
||||
<property name="role">muted</property> |
||||
<property name="valign">center</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkImage" id="muted_selected"> |
||||
<property name="icon-name">object-select-symbolic</property> |
||||
<property name="accessible-role">presentation</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkSeparator" /> |
||||
</child> |
||||
<child> |
||||
<object class="GtkListBox"> |
||||
<child> |
||||
<object class="AdwSpinRow" id="custom_row"> |
||||
<property name="selectable">False</property> |
||||
<!-- Translators: As in 'Custom role'. --> |
||||
<property name="title" translatable="yes">Custom</property> |
||||
<property name="numeric">True</property> |
||||
<property name="adjustment"> |
||||
<object class="GtkAdjustment" id="custom_adjustment"> |
||||
<!-- js_int::MIN_SAFE_INT --> |
||||
<property name="lower">-9007199254740991</property> |
||||
<property name="upper">100</property> |
||||
<property name="page-increment">10</property> |
||||
<property name="step-increment">1</property> |
||||
<signal name="notify::value" handler="custom_value_changed" swapped="yes" /> |
||||
</object> |
||||
</property> |
||||
<child> |
||||
<object class="GtkButton" id="custom_confirm"> |
||||
<property name="icon-name">checkmark-symbolic</property> |
||||
<property name="tooltip-text" translatable="yes">Confirm Custom Role</property> |
||||
<signal name="clicked" handler="custom_value_confirmed" swapped="yes" /> |
||||
<property name="valign">center</property> |
||||
<property name="sensitive">False</property> |
||||
<style> |
||||
<class name="suggested-action" /> |
||||
<class name="circular" /> |
||||
<class name="spin-confirm" /> |
||||
</style> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</property> |
||||
</object> |
||||
</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</template> |
||||
</interface> |
||||
@ -1,55 +0,0 @@
|
||||
use std::fmt; |
||||
|
||||
use gettextrs::gettext; |
||||
use gtk::glib; |
||||
|
||||
use super::PowerLevel; |
||||
|
||||
/// Role of a room member, like admin or moderator.
|
||||
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)] |
||||
#[repr(u32)] |
||||
#[enum_type(name = "MemberRole")] |
||||
pub enum MemberRole { |
||||
/// An administrator.
|
||||
Admin = 1, |
||||
/// A moderator.
|
||||
Mod = 2, |
||||
/// A regular room member.
|
||||
Peasant = 0, |
||||
} |
||||
|
||||
impl MemberRole { |
||||
pub fn is_admin(&self) -> bool { |
||||
matches!(*self, Self::Admin) |
||||
} |
||||
|
||||
pub fn is_mod(&self) -> bool { |
||||
matches!(*self, Self::Mod) |
||||
} |
||||
|
||||
pub fn is_peasant(&self) -> bool { |
||||
matches!(*self, Self::Peasant) |
||||
} |
||||
} |
||||
|
||||
impl From<PowerLevel> for MemberRole { |
||||
fn from(power_level: PowerLevel) -> Self { |
||||
if (100..).contains(&power_level) { |
||||
Self::Admin |
||||
} else if (50..100).contains(&power_level) { |
||||
Self::Mod |
||||
} else { |
||||
Self::Peasant |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl fmt::Display for MemberRole { |
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||
match *self { |
||||
Self::Admin => write!(f, "{}", gettext("Admin")), |
||||
Self::Mod => write!(f, "{}", gettext("Moderator")), |
||||
_ => write!(f, "{}", gettext("Normal user")), |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue