Skip to content

Commit

Permalink
feat(core): 🎸 Added the builtin widget of tooltips
Browse files Browse the repository at this point in the history
  • Loading branch information
wjian23 authored and rchangelog[bot] committed Dec 5, 2024
1 parent 00cfcd8 commit e7eb7f9
Show file tree
Hide file tree
Showing 21 changed files with 183 additions and 16 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ 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 the builtin widget of tooltips (#664 @wjian23)

### Changed

- **widgets**: The `Checkbox` widget uses classes to style and simplify its label syntax. (#666 @M-Adoo)
- **widgets**: The `Icon` widget utilizes classes to configure its style, and it does not have a size property. (#660 @M-Adoo)
- **theme:** Refactor the `Ripple` and `StateLayer` of the material theme to enhance their visual effects. (#666 @M-Adoo)
- **core**: Refactor the builtin widget of global_anchor (#pr @wjian23)
- **core**: Refactor the builtin widget of global_anchor (#664 @wjian23)

### Fixed

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

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { workspace = true, features = ["full"]}
Expand Down
21 changes: 21 additions & 0 deletions core/src/builtin_widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ 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::*;

Expand Down Expand Up @@ -131,6 +135,7 @@ pub struct FatObj<T> {
painting_style: Option<State<PaintingStyleWidget>>,
text_style: Option<State<TextStyleWidget>>,
keep_alive: Option<State<KeepAlive>>,
tooltips: Option<State<Tooltips>>,
keep_alive_unsubscribe_handle: Option<Box<dyn Any>>,
}

Expand Down Expand Up @@ -165,6 +170,7 @@ impl<T> FatObj<T> {
text_style: self.text_style,
visibility: self.visibility,
opacity: self.opacity,
tooltips: self.tooltips,
keep_alive: self.keep_alive,
keep_alive_unsubscribe_handle: self.keep_alive_unsubscribe_handle,
}
Expand Down Expand Up @@ -195,6 +201,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 @@ -415,6 +422,14 @@ impl<T> FatObj<T> {
.keep_alive
.get_or_insert_with(|| State::value(<_>::default()))
}

/// Returns the `State<Tooltips>` widget from the FatObj. If it doesn't
/// exist, a new one is created.
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 @@ -857,6 +872,11 @@ impl<T> FatObj<T> {
self.declare_builtin_init(v, Self::get_opacity_widget, |m, v| m.opacity = v)
}

/// Initializes the tooltips of the widget.
pub fn tooltips<const M: u8>(self, v: impl DeclareInto<CowArc<str>, M>) -> Self {
self.declare_builtin_init(v, Self::get_tooltips_widget, |m, v| m.tooltips = v)
}

/// Initializes the `keep_alive` value of the `KeepAlive` widget.
pub fn keep_alive<const M: u8>(mut self, v: impl DeclareInto<bool, M>) -> Self {
let (v, o) = v.declare_into().unzip();
Expand Down Expand Up @@ -944,6 +964,7 @@ impl<'a> FatObj<Widget<'a>> {
class,
cursor,
constrained_box,
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::*;

pub type TextInit = DeclareInit<CowArc<str>>;
/// The text widget display text with a single style.
#[derive(Declare)]
Expand Down Expand Up @@ -91,19 +92,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::*};
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
105 changes: 105 additions & 0 deletions core/src/builtin_widgets/tooltips.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use std::cell::RefCell;

use crate::prelude::*;

class_names! {
#[doc = "Class name for the tooltips"]
TOOLTIPS,
}
/// Add attributes of tooltips to Widget Declarer.
///
/// ### Example:
/// ```no_run
/// use ribir::prelude::*;
///
/// let w = fn_widget! {
/// @FilledButton{
/// text: "hover to show tooltips!",
/// tooltips: "this is tooltips",
/// }
/// };
/// App::run(w);
/// ```
#[derive(Default)]
pub struct Tooltips {
pub tooltips: CowArc<str>,

overlay: RefCell<Option<Overlay>>,
}

impl Declare for Tooltips {
type Builder = FatObj<()>;
fn declarer() -> Self::Builder { FatObj::new(()) }
}

impl Tooltips {
fn tooltips(&self) -> &CowArc<str> { &self.tooltips }

pub fn show(&self, wnd: Sc<Window>) {
if let Some(overlay) = self.overlay.borrow().clone() {
if !overlay.is_showing() {
overlay.show(wnd);
}
}
}

pub fn hidden(&self) {
if let Some(overlay) = self.overlay.borrow().clone() {
if overlay.is_showing() {
overlay.close();
}
}
}
}

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 mut child = FatObj::new(child);
*$this.overlay.borrow_mut() = Some(Overlay::new(
move || {
let w = @Text {
text: pipe!($this.tooltips().clone()),
class: TOOLTIPS,
};

@ $w {
global_anchor_x: pipe!(
GlobalAnchorX::center_align_to(
$child.track_id(), 0.
).always_follow()
),
global_anchor_y: pipe!(
GlobalAnchorY::bottom_align_to(
$child.track_id(), $child.layout_size().height
).always_follow()
),
}.into_widget()
}, OverlayStyle {
auto_close_policy: AutoClosePolicy::NOT_AUTO_CLOSE,
mask: None,
}
));

let wnd = BuildCtx::get().window();
let u = watch!($child.is_hover())
.distinct_until_changed()
.subscribe(move |v| {
if v {
$this.show(wnd.clone());
} else {
$this.hidden();
}
});

@ $child {
on_disposed: move|_| {
u.unsubscribe();
$this.hidden();
},
}
}
.into_widget()
}
}
1 change: 1 addition & 0 deletions core/src/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ impl Overlay {
}
}
},
@{ w }
}.into_widget();
};
if close_policy.contains(AutoClosePolicy::ESC) {
Expand Down
6 changes: 6 additions & 0 deletions macros/src/declare_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,12 @@ pub(crate) fn declare_derive(input: &mut syn::DeriveInput) -> syn::Result<TokenS
self
}

#[doc="Initializes the `tooltips` value of the `Tooltips` widget."]
#vis fn tooltips<const _M: u8>(mut self, v: impl DeclareInto<CowArc<str>, _M>) -> Self
{
self.fat_obj = self.fat_obj.tooltips(v);
self
}
}
}
};
Expand Down
2 changes: 2 additions & 0 deletions macros/src/variable_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ pub static BUILTIN_INFOS: phf::Map<&'static str, BuiltinMember> = phf_map! {
"opacity" => builtin_member!{"Opacity", Field, "opacity"},
// KeepAlive
"keep_alive" => builtin_member!{"KeepAlive", Field, "keep_alive"},
// Tooltips
"tooltips" => builtin_member!{"Tooltips", Field, "tooltips"},
// TrackWidgetId
"track_id" => builtin_member!{"TrackWidgetId", Method, "track_id"},
};
2 changes: 2 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 checkbox_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,6 +13,7 @@ pub fn initd_classes() -> Classes {
radio_cls::init(&mut classes);
progress_cls::init(&mut classes);
checkbox_cls::init(&mut classes);
tooltips_cls::init(&mut classes);

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

use crate::md;

pub(super) fn init(classes: &mut Classes) {
classes.insert(TOOLTIPS, |w| {
fn_widget! {
let w = FatObj::new(w);
let mut w = @BoxDecoration {
background: Palette::of(BuildCtx::get()).inverse_surface(),
margin: EdgeInsets::only_bottom(4.),
border_radius: Radius::all(4.),
@ $w {
margin: EdgeInsets::new(4., 8., 4., 8.),
foreground: Palette::of(BuildCtx::get()).inverse_on_surface(),
v_align: VAlign::Center,
h_align: HAlign::Center,
}
};
let animate = part_writer!(&mut w.opacity)
.transition(EasingTransition{
easing: md::easing::STANDARD_ACCELERATE,
duration: md::easing::duration::SHORT2
}.box_it());
@ $w {
keep_alive: pipe!($animate.is_running() || $w.opacity != 0.),
on_disposed: move |_| {
$w.write().opacity = 0.;
}
}
}
.into_widget()
});
}
3 changes: 1 addition & 2 deletions widgets/src/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use svg::named_svgs;

use crate::{
common_widget::{Leading, Trailing},
prelude::{Icon, Row, Text},
text::TextInit,
prelude::{Icon, Row},
};

class_names! {
Expand Down
2 changes: 0 additions & 2 deletions widgets/src/icon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ use std::cell::Cell;

use ribir_core::{impl_compose_child_for_wrap_render, prelude::*, wrap_render::WrapRender};

use crate::text::*;

/// An widget represents an icon.
///
/// The icon size is determined by the text line height, so you can use
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 e7eb7f9

Please sign in to comment.