components: Add button with loading spinner

This commit is contained in:
Julian Sparber 2021-05-21 19:50:37 +02:00
parent c2f73e551f
commit a180118a8e
7 changed files with 177 additions and 0 deletions

View file

@ -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>

View 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);

View 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>

View file

@ -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

View file

@ -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;

View 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())
}
}

View file

@ -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',