components: Add button with loading spinner
This commit is contained in:
parent
c2f73e551f
commit
a180118a8e
7 changed files with 177 additions and 0 deletions
|
@ -18,6 +18,7 @@
|
|||
<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" preprocess="xml-stripblanks" alias="spinner-button.ui">ui/spinner-button.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>
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pill-button {
|
||||
border-radius: 9999px;
|
||||
padding: 12px 40px;
|
||||
}
|
||||
|
||||
.user-pill {
|
||||
border-radius: 9999px;
|
||||
background-color: alpha(@theme_text_color, 0.1);
|
||||
|
|
26
data/resources/ui/spinner-button.ui
Normal file
26
data/resources/ui/spinner-button.ui
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="SpinnerButton" parent="GtkButton">
|
||||
<property name="child">
|
||||
<object class="GtkStack" id="stack">
|
||||
<child>
|
||||
<object class="GtkLabel" id="label">
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">SpinnerButton</property>
|
||||
<style>
|
||||
<class name="text-button"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinner" id="spinner">
|
||||
<property name="spinning">True</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="halign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
@ -21,6 +21,7 @@ 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/spinner-button.ui
|
||||
data/resources/ui/user-pill.ui
|
||||
data/resources/ui/window.ui
|
||||
|
||||
|
@ -28,6 +29,7 @@ data/resources/ui/window.ui
|
|||
src/application.rs
|
||||
src/components/context_menu_bin.rs
|
||||
src/components/mod.rs
|
||||
src/components/spinner_button.rs
|
||||
src/components/user_pill.rs
|
||||
src/login.rs
|
||||
src/main.rs
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
mod context_menu_bin;
|
||||
mod spinner_button;
|
||||
mod user_pill;
|
||||
|
||||
pub use self::context_menu_bin::{ContextMenuBin, ContextMenuBinImpl};
|
||||
pub use self::spinner_button::SpinnerButton;
|
||||
pub use self::user_pill::UserPill;
|
||||
|
|
140
src/components/spinner_button.rs
Normal file
140
src/components/spinner_button.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
use adw::subclass::prelude::*;
|
||||
use gtk::prelude::*;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::{glib, CompositeTemplate};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use glib::object::ObjectClass;
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[template(resource = "/org/gnome/FractalNext/spinner-button.ui")]
|
||||
pub struct SpinnerButton {
|
||||
#[template_child]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
pub label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub spinner: TemplateChild<gtk::Spinner>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SpinnerButton {
|
||||
const NAME: &'static str = "SpinnerButton";
|
||||
type Type = super::SpinnerButton;
|
||||
type ParentType = gtk::Button;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Self::bind_template(klass);
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for SpinnerButton {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpec::new_override(
|
||||
"label",
|
||||
&ObjectClass::from_type(gtk::Button::static_type())
|
||||
.unwrap()
|
||||
.find_property("label")
|
||||
.unwrap(),
|
||||
),
|
||||
glib::ParamSpec::new_boolean(
|
||||
"loading",
|
||||
"Loading",
|
||||
"Wheter to display the loading spinner or the content",
|
||||
false,
|
||||
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() {
|
||||
"label" => obj.set_label(value.get().unwrap()),
|
||||
"loading" => obj.set_loading(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"label" => obj.label().to_value(),
|
||||
"loading" => obj.loading().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for SpinnerButton {}
|
||||
|
||||
impl ButtonImpl for SpinnerButton {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct SpinnerButton(ObjectSubclass<imp::SpinnerButton>)
|
||||
@extends gtk::Widget, gtk::Button, @implements gtk::Accessible;
|
||||
}
|
||||
|
||||
/// A widget displaying a `User`
|
||||
impl SpinnerButton {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new(&[]).expect("Failed to create SpinnerButton")
|
||||
}
|
||||
|
||||
pub fn set_label(&self, label: &str) {
|
||||
let priv_ = imp::SpinnerButton::from_instance(self);
|
||||
|
||||
if priv_.label.label().as_str() == label {
|
||||
return;
|
||||
}
|
||||
|
||||
priv_.label.set_label(label);
|
||||
|
||||
self.notify("label");
|
||||
}
|
||||
|
||||
pub fn label(&self) -> glib::GString {
|
||||
let priv_ = imp::SpinnerButton::from_instance(self);
|
||||
priv_.label.label()
|
||||
}
|
||||
|
||||
pub fn set_loading(&self, loading: bool) {
|
||||
let priv_ = imp::SpinnerButton::from_instance(self);
|
||||
|
||||
if self.loading() == loading {
|
||||
return;
|
||||
}
|
||||
|
||||
self.set_sensitive(!loading);
|
||||
|
||||
if loading {
|
||||
priv_.stack.set_visible_child(&*priv_.spinner);
|
||||
} else {
|
||||
priv_.stack.set_visible_child(&*priv_.label);
|
||||
}
|
||||
|
||||
self.notify("loading");
|
||||
}
|
||||
|
||||
pub fn loading(&self) -> bool {
|
||||
let priv_ = imp::SpinnerButton::from_instance(self);
|
||||
priv_.stack.visible_child().as_ref() == Some(priv_.spinner.upcast_ref())
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ sources = files(
|
|||
'components/context_menu_bin.rs',
|
||||
'components/mod.rs',
|
||||
'components/user_pill.rs',
|
||||
'components/spinner_button.rs',
|
||||
'config.rs',
|
||||
'main.rs',
|
||||
'window.rs',
|
||||
|
|
Loading…
Reference in a new issue