use super::gtk_props::apply;
use crate::{handlers::Handler, new_gc, Interactable};
use glib::prelude::Cast;
use grx_macros::{gtk_component, props};
use gtk::glib;
use gtk::{
prelude::{AdjustmentExt, EditableExt, WidgetExt},
Adjustment,
};
use std::rc::Rc;
#[props]
#[derive(Default)]
pub struct Props {
pub on_change: Option<Handler<SpinButton>>,
pub lower: f64,
pub upper: f64,
pub value: f64,
pub step: f64,
pub page: f64,
pub value_mapper: Option<Mapper>,
}
impl std::fmt::Debug for Props {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Props")
.field("id", &self.id)
.field("classes", &self.classes)
.field("styles", &self.styles)
.field("children", &self.children)
.field("on_change", &self.on_change.is_some())
.field("lower", &self.lower)
.field("upper", &self.upper)
.field("value", &self.value)
.field("step", &self.step)
.field("page", &self.page)
.field("value_mapper", &self.value_mapper.is_some())
.finish()
}
}
type Mapper = (
Rc<dyn Fn(f64) -> String + 'static>,
Rc<dyn Fn(String) -> Option<f64> + 'static>,
);
pub fn spin_button(mut props: Props) -> Rc<SpinButton> {
let switch = gtk::SpinButton::builder().build();
let sw = switch.clone();
let value_mapper = props.value_mapper.take();
let on_change = props.on_change.take();
let lower = props.lower;
let upper = props.upper;
let step = props.step;
let page = props.page;
let switch = new_gc!(SpinButton { switch, props });
let adj = Adjustment::builder()
.lower(lower)
.upper(upper)
.step_increment(step)
.page_increment(page)
.build();
sw.set_adjustment(&adj);
if let Some((to, from)) = value_mapper {
sw.connect_output(move |sb| {
let value = sb.value();
sb.set_text(&to(value));
glib::Propagation::Proceed
});
sw.connect_input(move |sb| {
let text = sb.text().to_string();
from(text).map(Ok)
});
}
sw.set_value(switch.props.value);
apply(switch.clone());
if let Some(onchange) = on_change {
let btn = switch.clone();
let onc = onchange.clone();
adj.connect_value_changed(move |_| {
onc(btn.clone());
});
}
switch
}
#[gtk_component(gtk::SpinButton)]
#[derive(Debug)]
pub struct SpinButton {}
impl SpinButton {
pub fn has_focus(self: &Rc<Self>) -> bool {
let mut focus = self.widget.has_focus();
let mut c = self.widget.first_child();
while let Some(child) = c {
focus |= child.has_focus();
c = child.next_sibling();
}
focus
}
pub fn set_value(self: &Rc<Self>, value: f64) {
self.widget.set_value(value);
}
pub fn value(self: &Rc<Self>) -> f64 {
self.widget.adjustment().value()
}
pub fn upper(self: &Rc<Self>) -> f64 {
self.widget.adjustment().upper()
}
pub fn lower(self: &Rc<Self>) -> f64 {
self.widget.adjustment().lower()
}
pub fn step(self: &Rc<Self>) -> f64 {
self.widget.adjustment().step_increment()
}
pub fn page(self: &Rc<Self>) -> f64 {
self.widget.adjustment().page_increment()
}
}
impl Interactable for SpinButton {
#[allow(unused_variables)]
fn on_click(self: &Rc<Self>, handler: impl Fn(&Rc<Self>) + 'static) {
}
#[allow(unused_variables)]
fn on_change(self: &Rc<Self>, handler: impl Fn(&Rc<Self>) + 'static) {
let s = self.clone();
self.widget.adjustment().connect_value_changed(move |_| {
handler(&s);
});
}
#[allow(unused_variables)]
fn on_swipe(self: &Rc<Self>, handler: impl Fn(&Rc<Self>, f64, f64) + 'static) {
}
#[allow(unused_variables)]
fn on_blur(self: &Rc<Self>, handler: impl Fn(&Rc<Self>) + 'static) {
}
}