use adw::{prelude::*, subclass::prelude::*};
use gtk::{glib, CompositeTemplate};
use super::PowerLevelSelectionPopover;
use crate::{
components::{LoadingBin, RoleBadge},
session::model::{Permissions, PowerLevel},
};
mod imp {
use std::{
cell::{Cell, RefCell},
marker::PhantomData,
};
use glib::subclass::InitializingObject;
use super::*;
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/components/power_level_selection/row.ui")]
#[properties(wrapper_type = super::PowerLevelSelectionRow)]
pub struct PowerLevelSelectionRow {
#[template_child]
pub subtitle_bin: TemplateChild<adw::Bin>,
#[template_child]
pub combo_selection_bin: TemplateChild<adw::Bin>,
#[template_child]
pub arrow_box: TemplateChild<gtk::Box>,
#[template_child]
pub loading_bin: TemplateChild<LoadingBin>,
#[template_child]
pub popover: TemplateChild<PowerLevelSelectionPopover>,
#[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>,
#[property(get, set = Self::set_permissions, explicit_notify, nullable)]
pub permissions: RefCell<Option<Permissions>>,
#[property(get, set = Self::set_selected_power_level, explicit_notify)]
pub selected_power_level: Cell<PowerLevel>,
#[property(get, set = Self::set_use_subtitle, explicit_notify)]
pub use_subtitle: Cell<bool>,
#[property(get = Self::is_loading, set = Self::set_is_loading)]
pub is_loading: PhantomData<bool>,
#[property(get, set = Self::set_read_only, explicit_notify)]
pub read_only: Cell<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for PowerLevelSelectionRow {
const NAME: &'static str = "PowerLevelSelectionRow";
type Type = super::PowerLevelSelectionRow;
type ParentType = adw::PreferencesRow;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
Self::Type::bind_template_callbacks(klass);
klass.set_accessible_role(gtk::AccessibleRole::ComboBox);
klass.install_action("power-level-selection-row.popup", None, |obj, _, _| {
if !obj.read_only() && !obj.is_loading() {
obj.imp().popover.popup();
}
});
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for PowerLevelSelectionRow {
fn constructed(&self) {
self.parent_constructed();
self.update_selected_position();
}
}
impl WidgetImpl for PowerLevelSelectionRow {}
impl ListBoxRowImpl for PowerLevelSelectionRow {}
impl PreferencesRowImpl for PowerLevelSelectionRow {}
impl PowerLevelSelectionRow {
fn set_permissions(&self, permissions: Option<Permissions>) {
if *self.permissions.borrow() == permissions {
return;
}
self.permissions.replace(permissions);
self.update_selected_label();
self.obj().notify_permissions();
}
fn update_selected_label(&self) {
let Some(permissions) = self.permissions.borrow().clone() 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)]);
}
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.obj().notify_selected_power_level();
}
fn set_use_subtitle(&self, use_subtitle: bool) {
if self.use_subtitle.get() == use_subtitle {
return;
}
self.use_subtitle.set(use_subtitle);
self.update_selected_position();
self.obj().notify_use_subtitle();
}
fn is_loading(&self) -> bool {
self.loading_bin.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();
}
fn update_selected_position(&self) {
if self.use_subtitle.get() {
if !self
.selected_box
.parent()
.is_some_and(|p| p == *self.subtitle_bin)
{
if self.selected_box.parent().is_some() {
self.combo_selection_bin.set_child(None::<>k::Widget>);
}
self.subtitle_bin.set_child(Some(&*self.selected_box));
}
} else if !self
.selected_box
.parent()
.is_some_and(|p| p == *self.combo_selection_bin)
{
if self.selected_box.parent().is_some() {
self.subtitle_bin.set_child(None::<>k::Widget>);
}
self.combo_selection_bin
.set_child(Some(&*self.selected_box));
}
}
fn set_read_only(&self, read_only: bool) {
if self.read_only.get() == read_only {
return;
}
let obj = self.obj();
self.read_only.set(read_only);
obj.update_property(&[gtk::accessible::Property::ReadOnly(read_only)]);
obj.notify_read_only();
}
}
}
glib::wrapper! {
pub struct PowerLevelSelectionRow(ObjectSubclass<imp::PowerLevelSelectionRow>)
@extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow,
@implements gtk::Actionable, gtk::Accessible;
}
#[gtk::template_callbacks]
impl PowerLevelSelectionRow {
pub fn new() -> Self {
glib::Object::new()
}
#[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");
}
}
}