Skip to content

Commit

Permalink
feat(core): 🎸 builtin widget of tooltips
Browse files Browse the repository at this point in the history
  • Loading branch information
wjian23 committed Nov 25, 2024
1 parent 9934801 commit 09e4870
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he
- **core**: Added `OverrideClass` to override a single class within a subtree. (#657 @M-Adoo)
- **widgets**: Added `LinearProgress` and `SpinnerProgress` widgets along with their respective material themes. (#630 @wjian23 @M-Adoo)
- **painter**: SVG now supports switching the default color, allowing for icon color changes. (#661 @M-Adoo)
- **core**: Added builtin widget of Tooltips. (#pr @wjian23)

### Changed

Expand Down
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ web-time.workspace = true
colored.workspace = true
paste.workspace = true
ribir_dev_helper = {path = "../dev-helper"}
ribir = { path = "../ribir" }
ribir = { path = "../ribir", features = ["material", "slim"] }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { workspace = true, features = ["full"]}
Expand Down
20 changes: 20 additions & 0 deletions core/src/builtin_widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ pub use smooth_layout::*;
mod track_widget_id;
pub use track_widget_id::*;

mod text;
pub use text::*;

mod tooltips;
pub use tooltips::*;

use crate::prelude::*;

/// A fat object that extend the `T` object with all builtin widgets ability.
Expand Down Expand Up @@ -131,6 +137,7 @@ pub struct FatObj<T> {
text_style: Option<State<TextStyleWidget>>,
keep_alive: Option<State<KeepAlive>>,
keep_alive_unsubscribe_handle: Option<Box<dyn Any>>,
tooltips: Option<State<Tooltips>>,
}

impl<T> FatObj<T> {
Expand Down Expand Up @@ -166,6 +173,7 @@ impl<T> FatObj<T> {
opacity: self.opacity,
keep_alive: self.keep_alive,
keep_alive_unsubscribe_handle: self.keep_alive_unsubscribe_handle,
tooltips: self.tooltips,
}
}

Expand Down Expand Up @@ -194,6 +202,7 @@ impl<T> FatObj<T> {
&& self.visibility.is_none()
&& self.opacity.is_none()
&& self.keep_alive.is_none()
&& self.tooltips.is_none()
}

/// Return the host object of the FatObj.
Expand Down Expand Up @@ -414,6 +423,12 @@ impl<T> FatObj<T> {
.keep_alive
.get_or_insert_with(|| State::value(<_>::default()))
}

pub fn get_tooltips_widget(&mut self) -> &State<Tooltips> {
self
.tooltips
.get_or_insert_with(|| State::value(<_>::default()))
}
}

macro_rules! on_mixin {
Expand Down Expand Up @@ -854,6 +869,10 @@ impl<T> FatObj<T> {
self
}

pub fn tooltips<const M: u8>(self, v: impl DeclareInto<CowArc<str>, M>) -> Self {
self.declare_builtin_init(v, Self::get_tooltips_widget, |w, v| w.tips = v)
}

fn declare_builtin_init<V: 'static, B: 'static, const M: u8>(
mut self, init: impl DeclareInto<V, M>, get_builtin: impl FnOnce(&mut Self) -> &State<B>,
set_value: fn(&mut B, V),
Expand Down Expand Up @@ -911,6 +930,7 @@ impl<'a> FatObj<Widget<'a>> {
mix_builtin,
request_focus,
cursor,
tooltips,
margin,
transform,
opacity,
Expand Down
11 changes: 5 additions & 6 deletions widgets/src/text.rs → core/src/builtin_widgets/text.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::cell::{Ref, RefCell};

use ribir_core::prelude::*;
use typography::PlaceLineDirection;

use crate::prelude::*;

/// The text widget display text with a single style.
#[derive(Declare)]
pub struct Text {
Expand Down Expand Up @@ -90,19 +91,17 @@ define_text_with_theme_style!(H4, title_large);
define_text_with_theme_style!(H5, title_medium);
define_text_with_theme_style!(H6, title_small);

#[cfg(test)]
#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use ribir_core::test_helper::*;
use ribir::{core::test_helper::*, material as ribir_material, prelude::*, slim as ribir_slim};
use ribir_dev_helper::*;

use super::*;
use crate::layout::SizedBox;
const WND_SIZE: Size = Size::new(164., 64.);

widget_test_suit!(
text_clip,
WidgetTester::new(fn_widget! {
@SizedBox {
@MockBox {
size: Size::new(50., 45.),
@Text {
text: "hello world,\rnice to meet you.",
Expand Down
59 changes: 59 additions & 0 deletions core/src/builtin_widgets/tooltips.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::prelude::*;
class_names! {
#[doc = "Class name for the tooltips"]
TOOLTIPS,
}

#[derive(Default)]
pub struct Tooltips {
pub(crate) tips: CowArc<str>,
}

impl<'c> ComposeChild<'c> for Tooltips {
type Child = Widget<'c>;
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
fn_widget! {
let wnd = BuildCtx::get().window();
let mut child = FatObj::new(child);

watch!($child.is_hover())
.distinct_until_changed()
.filter(|v| *v)
.subscribe(move |_| {
let this = this.clone_writer();
let overlay = Overlay::new(
fn_widget! {
@Text {
text: pipe!($this.tips.clone()),
class: TOOLTIPS,
}
}, OverlayStyle {
auto_close_policy: AutoClosePolicy::NOT_AUTO_CLOSE,
mask: None,
});

overlay.clone().show_map(move |w: Widget| {
let overlay = overlay.clone();
watch!($child.is_hover())
.distinct_until_changed()
.filter(|v| !v)
.take(1)
.subscribe(move |_| overlay.close());
let mut w = FatObj::new(w);
let anchor_widget = w.get_global_anchor_widget().clone_writer();
@$ w{
anchor: pipe!(Anchor::left(-$w.layout_size().width / 2.)),
on_mounted: move|e| {
if let Some(wid) = $child.track_id() {
anchor_widget.bottom_align_to(&wid, $child.layout_size().height, e.window());
anchor_widget.left_align_to(&wid, $child.layout_size().width / 2., e.window());
}
}
}.into_widget()
}, wnd.clone());
});
@ $child {}
}
.into_widget()
}
}
2 changes: 2 additions & 0 deletions macros/src/variable_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,6 @@ pub static BUILTIN_INFOS: phf::Map<&'static str, BuiltinMember> = phf_map! {
"keep_alive" => builtin_member!{"KeepAlive", Field, "keep_alive"},
// TrackWidgetId
"track_id" => builtin_member!{"TrackWidgetId", Method, "track_id"},
// Tooltips
"tooltips" => builtin_member!{"Tooltips", Field, "tips"},
};
3 changes: 3 additions & 0 deletions themes/material/src/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod icon_cls;
mod progress_cls;
mod radio_cls;
mod scrollbar_cls;
mod tooltips_cls;

pub fn initd_classes() -> Classes {
let mut classes = Classes::default();
Expand All @@ -12,5 +13,7 @@ pub fn initd_classes() -> Classes {
scrollbar_cls::init(&mut classes);
radio_cls::init(&mut classes);
progress_cls::init(&mut classes);
tooltips_cls::init(&mut classes);

classes
}
55 changes: 55 additions & 0 deletions themes/material/src/classes/tooltips_cls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use ribir_core::prelude::*;

class_names! {
TOOLTIPS_BASE,
}

pub(super) fn init(classes: &mut Classes) {
classes.insert(TOOLTIPS_BASE, |w| {
fn_widget! {
let w = FatObj::new(w);
@BoxDecoration {
background: Palette::of(BuildCtx::get()).inverse_surface(),
margin: EdgeInsets::only_bottom(4.),
border_radius: Radius::all(32.),
@ $w {
margin: EdgeInsets::new(8., 4., 4., 8.),
foreground: Palette::of(BuildCtx::get()).inverse_on_surface(),
v_align: VAlign::Center,
h_align: HAlign::Center,
}
}
}
.into_widget()
});

classes.insert(TOOLTIPS, |w| {
fn_widget! {
let w = FatObj::new(w);
let mut w = @ $w {
class: TOOLTIPS_BASE,
};
$w.write().keep_alive = true;

let animate = part_writer!(&mut w.opacity)
.transition(transitions::LINEAR.of(BuildCtx::get()));
@ $w {
on_disposed: move |_| {
animate.run();
$w.write().opacity = 0.;
let animate = animate.clone_watcher();
watch!($animate.is_running())
.distinct_until_changed()
.filter(|v| !v)
.take(1)
.subscribe(move|v| {
if !v {
$w.write().keep_alive = false;
}
});
}
}
}
.into_widget()
});
}
2 changes: 1 addition & 1 deletion widgets/src/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use ribir_core::prelude::*;
use crate::{
common_widget::{Leading, Trailing},
icon::ICON,
prelude::{Icon, Label, Row, Text},
prelude::{Icon, Label, Row},
};

/// Represents a control that a user can select and clear.
Expand Down
2 changes: 0 additions & 2 deletions widgets/src/icon.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use ribir_core::prelude::*;

use crate::text::*;

/// An icon widget represents an icon.
///
/// It can accept either text or another widget as its child. If the child is
Expand Down
1 change: 0 additions & 1 deletion widgets/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use crate::{
text_selectable::{SelectableText, bind_point_listener, select_key_handle},
},
layout::{OnlySizedByParent, Stack, StackFit},
prelude::Text,
};

pub struct Placeholder(DeclareInit<CowArc<str>>);
Expand Down
1 change: 0 additions & 1 deletion widgets/src/layout/sized_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ mod tests {
use ribir_dev_helper::*;

use super::*;
use crate::prelude::*;

widget_layout_test!(
fix_size,
Expand Down
4 changes: 2 additions & 2 deletions widgets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ pub mod progress;
pub mod radio;
pub mod scrollbar;
pub mod tabs;
pub mod text;
pub mod text_field;
pub mod transform_box;

pub mod prelude {
pub use super::{
avatar::*, buttons::*, checkbox::*, common_widget::*, divider::*, grid_view::*, icon::*,
input::*, label::*, layout::*, link::*, lists::*, path::*, progress::*, radio::*, scrollbar::*,
tabs::*, text::*, text_field::*, transform_box::*,
tabs::*, text_field::*, transform_box::*,
};
}

0 comments on commit 09e4870

Please sign in to comment.