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="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="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="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 compressed="true">style.css</file>
|
||||||
<file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>
|
<file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks">icons/scalable/status/welcome.svg</file>
|
<file preprocess="xml-stripblanks">icons/scalable/status/welcome.svg</file>
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pill-button {
|
||||||
|
border-radius: 9999px;
|
||||||
|
padding: 12px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.user-pill {
|
.user-pill {
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
background-color: alpha(@theme_text_color, 0.1);
|
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-item.ui
|
||||||
data/resources/ui/sidebar-room-row.ui
|
data/resources/ui/sidebar-room-row.ui
|
||||||
data/resources/ui/sidebar.ui
|
data/resources/ui/sidebar.ui
|
||||||
|
data/resources/ui/spinner-button.ui
|
||||||
data/resources/ui/user-pill.ui
|
data/resources/ui/user-pill.ui
|
||||||
data/resources/ui/window.ui
|
data/resources/ui/window.ui
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ data/resources/ui/window.ui
|
||||||
src/application.rs
|
src/application.rs
|
||||||
src/components/context_menu_bin.rs
|
src/components/context_menu_bin.rs
|
||||||
src/components/mod.rs
|
src/components/mod.rs
|
||||||
|
src/components/spinner_button.rs
|
||||||
src/components/user_pill.rs
|
src/components/user_pill.rs
|
||||||
src/login.rs
|
src/login.rs
|
||||||
src/main.rs
|
src/main.rs
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mod context_menu_bin;
|
mod context_menu_bin;
|
||||||
|
mod spinner_button;
|
||||||
mod user_pill;
|
mod user_pill;
|
||||||
|
|
||||||
pub use self::context_menu_bin::{ContextMenuBin, ContextMenuBinImpl};
|
pub use self::context_menu_bin::{ContextMenuBin, ContextMenuBinImpl};
|
||||||
|
pub use self::spinner_button::SpinnerButton;
|
||||||
pub use self::user_pill::UserPill;
|
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/context_menu_bin.rs',
|
||||||
'components/mod.rs',
|
'components/mod.rs',
|
||||||
'components/user_pill.rs',
|
'components/user_pill.rs',
|
||||||
|
'components/spinner_button.rs',
|
||||||
'config.rs',
|
'config.rs',
|
||||||
'main.rs',
|
'main.rs',
|
||||||
'window.rs',
|
'window.rs',
|
||||||
|
|
Loading…
Reference in a new issue