Skip to content

Commit

Permalink
feat(core): 🎸 Support for modifying the theme at runtime.
Browse files Browse the repository at this point in the history
Removed `FullTheme` and `InheritTheme`, now only using `Theme`.
  • Loading branch information
M-Adoo committed Aug 20, 2024
1 parent e112dfe commit f9086cb
Show file tree
Hide file tree
Showing 87 changed files with 1,900 additions and 1,668 deletions.
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

### Features

- **core**: Support for modifying the theme at runtime. (#pr @M-Adoo)
<img src="./static/theme-switch.gif" style="transform:scale(0.5);"/>

The code:

```rust
use ribir::prelude::*;

let w = fn_widget! {
@Text {
on_tap: |e| {
// Query the `Palette` of the application theme.
let mut p = Palette::write_of(e);
if p.brightness == Brightness::Light {
p.brightness = Brightness::Dark;
} else {
p.brightness = Brightness::Light;
}
},
text : "Click me!"
}
};

App::run(w);
```

- **core**: Added `Provider` widget to share data between sub-tree. (#pr @M-Adoo)
```rust
Provider::new(Box::new(State::value(0i32))).with_child(fn_widget! {
Expand Down Expand Up @@ -54,6 +80,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he
### Breaking

- **core**: `GenWidget::gen_widget` no longer requires a `&mut BuildCtx` parameter. (#616 @M-Adoo)
- **core**: Removed `FullTheme` and `InheritTheme`, now only using `Theme`. Any part of the theme, such as `Palette`, can be directly used to overwrite its corresponding theme component. (#pr @M-Adoo)

## [0.4.0-alpha.5] - 2024-08-14

Expand Down Expand Up @@ -120,7 +147,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he
...
}
}
```
```

### Changed

Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ fn main() {
use ribir::prelude::*;

fn main() {
let counter = |ctx: &BuildCtx| {
let counter = |ctx: &mut BuildCtx| {
let cnt = Stateful::new(0);

let c_cnt = cnt.clone_writer();
Expand Down Expand Up @@ -106,8 +106,7 @@ More [Examples]
- **Non-intrusive state** converts your data to a listenable state and updates the view according to the change of the state.
- **Layout system** learning and inspired by [Flutter] Sublinear layout, but not the same.
- **Event system** is a composition event system, that supports event bubbling and capture. Allow to compose with any widget, and exists only if you use it.
- **Theme system** supports full and inherit/partial themes, so you can use it to override or dynamically switch the theme of the subtree. Include palette, icons, animate transitions, the decoration widget of the widget, etc. In a very rough state and the API will be redesigned soon.
- **Animations** based on the state but no side effect, it's almost stable in concept, but not many predefined animations yet.
- **Theme System**: Supports using different themes for various parts of the sub-tree and enables theme modifications at runtime.
- **Painter** converts the view to 2D paths.
- **GPU render** is a backend of the **Painter**, do path tessellation so that you can easily render the triangles in any GPU render engine. A [wgpu] implementation is provided as the default GPU render engine. Tessellation base on [lyon].
- **Text** support basic text typography and IME input, in a usable but rough stage.
Expand Down
11 changes: 6 additions & 5 deletions core/src/animation/stagger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ mod tests {
use super::*;
use crate::{reset_test_env, test_helper::*};

fn stagger_run_and_stop() -> Widget<'static> {
widget_layout_test!(
stagger_run_and_stop,
fn_widget! {
let stagger = Stagger::new(Duration::from_millis(100), transitions::EASE_IN.of(ctx!()));
let mut mock_box = @MockBox { size: Size::new(100., 100.) };
Expand All @@ -252,10 +253,10 @@ mod tests {
assert!(!stagger.is_running());

mock_box
}
.into_widget()
}
widget_layout_test!(stagger_run_and_stop, width == 100., height == 100.,);
},
width == 100.,
height == 100.,
);

#[test]
fn stagger_not_running_after_all_animation_end() {
Expand Down
2 changes: 1 addition & 1 deletion core/src/builtin_widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ impl<T> FatObj<T> {
) -> Self {
let builtin = get_builtin(&mut self);
let (v, o) = init.declare_into().unzip();
set_value(&mut *builtin.write(), v);
set_value(&mut *builtin.silent(), v);
if let Some(o) = o {
let c_builtin = builtin.clone_writer();
let u = o.subscribe(move |(_, v)| {
Expand Down
23 changes: 13 additions & 10 deletions core/src/builtin_widgets/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,82 +168,85 @@ mod tests {
const CHILD_SIZE: Size = Size::new(10., 10.);
const WND_SIZE: Size = Size::new(100., 100.);

fn h_align(h_align: HAlign) -> impl IntoWidget<'static, FN> {
fn h_align(h_align: HAlign) -> GenWidget {
fn_widget! {
@HAlignWidget {
h_align,
@MockBox { size: CHILD_SIZE }
}
}
.into()
}
fn left_align() -> impl IntoWidget<'static, FN> { h_align(HAlign::Left) }

widget_layout_test!(
left_align,
h_align(HAlign::Left),
wnd_size = WND_SIZE,
{ path = [0], width == 100., height == 10.,}
{ path = [0, 0], size == CHILD_SIZE, }
);

fn h_center_align() -> impl IntoWidget<'static, FN> { h_align(HAlign::Center) }
widget_layout_test!(
h_center_align,
h_align(HAlign::Center),
wnd_size = WND_SIZE,
{ path = [0], width == 100., height == 10.,}
{ path = [0, 0], x == 45., size == CHILD_SIZE,}
);

fn right_align() -> impl IntoWidget<'static, FN> { h_align(HAlign::Right) }
widget_layout_test!(
right_align,
h_align(HAlign::Right),
wnd_size = WND_SIZE,
{ path = [0], width == 100., height == 10.,}
{ path = [0, 0], x == 90., size == CHILD_SIZE,}
);

fn h_stretch_algin() -> impl IntoWidget<'static, FN> { h_align(HAlign::Stretch) }
widget_layout_test!(
h_stretch_algin,
h_align(HAlign::Stretch),
wnd_size = WND_SIZE,
{ path = [0], width == 100., height == 10.,}
{ path = [0, 0], x == 0., width == 100., height == 10.,}
);

fn v_align(v_align: VAlign) -> impl IntoWidget<'static, FN> {
fn v_align(v_align: VAlign) -> GenWidget {
fn_widget! {
@VAlignWidget {
v_align,
@MockBox { size: CHILD_SIZE }
}
}
.into()
}

fn top_align() -> impl IntoWidget<'static, FN> { v_align(VAlign::Top) }
widget_layout_test!(
top_align,
v_align(VAlign::Top),
wnd_size = WND_SIZE,
{ path = [0], width == 10., height == 100.,}
{ path = [0, 0], size == CHILD_SIZE,}
);

fn v_center_align() -> impl IntoWidget<'static, FN> { v_align(VAlign::Center) }
widget_layout_test!(
v_center_align,
v_align(VAlign::Center),
wnd_size = WND_SIZE,
{ path = [0], width == 10., height == 100.,}
{ path = [0, 0], y == 45., size == CHILD_SIZE,}
);

fn bottom_align() -> impl IntoWidget<'static, FN> { v_align(VAlign::Bottom) }
widget_layout_test!(
bottom_align,
v_align(VAlign::Bottom),
wnd_size = WND_SIZE,
{ path = [0], width == 10., height == 100.,}
{ path = [0, 0], y == 90., size == CHILD_SIZE,}
);

fn v_stretch_align() -> impl IntoWidget<'static, FN> { v_align(VAlign::Stretch) }
widget_layout_test!(
v_stretch_align,
v_align(VAlign::Stretch),
wnd_size = WND_SIZE,
{ path = [0], width == 10., height == 100.,}
{ path = [0, 0], width == 10., height == 100.,}
Expand Down
32 changes: 12 additions & 20 deletions core/src/builtin_widgets/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,61 +205,53 @@ mod test {
const CHILD_SIZE: Size = Size::new(50., 50.);
const WND_SIZE: Size = Size::new(100., 100.);

fn pixel_left_top() -> impl IntoWidget<'static, FN> {
widget_layout_test!(
pixel_left_top,
fn_widget! {
@MockBox {
size: CHILD_SIZE,
anchor: Anchor::left_top(1., 1.),
}
}
}
widget_layout_test!(
pixel_left_top,
},
wnd_size = WND_SIZE,
{ path = [0, 0], y == 1., }
{ path = [0, 0], x == 1., }
);

fn pixel_left_bottom() -> impl IntoWidget<'static, FN> {
widget_layout_test!(
pixel_left_bottom,
fn_widget! {
@MockBox {
size: CHILD_SIZE,
anchor: Anchor::left_bottom(1., 1.),
}
}
}
widget_layout_test!(
pixel_left_bottom,
},
wnd_size = WND_SIZE,
{ path = [0, 0], y == 49.,}
{ path = [0, 0], x == 1., }
);

fn pixel_top_right() -> impl IntoWidget<'static, FN> {
widget_layout_test!(
pixel_top_right,
fn_widget! {
@MockBox {
size: CHILD_SIZE,
anchor: Anchor::right_top(1., 1.),
}
}
}
widget_layout_test!(
pixel_top_right,
},
wnd_size = WND_SIZE,
{ path = [0, 0], y == 1.,}
{ path = [0, 0], x == 49.,}
);

fn pixel_bottom_right() -> impl IntoWidget<'static, FN> {
widget_layout_test!(
pixel_bottom_right,
fn_widget! {
@MockBox {
size: CHILD_SIZE,
anchor: Anchor::right_bottom(1., 1.)
}
}
}
widget_layout_test!(
pixel_bottom_right,
},
wnd_size = WND_SIZE,
{ path = [0, 0], y == 49.,}
{ path = [0, 0], x == 49.,}
Expand Down
9 changes: 4 additions & 5 deletions core/src/builtin_widgets/box_decoration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ mod tests {
}

const SIZE: Size = Size::new(100., 100.);
fn with_border() -> impl IntoWidget<'static, FN> {

widget_layout_test!(
with_border,
fn_widget! {
@MockBox {
size: SIZE,
Expand All @@ -207,10 +209,7 @@ mod tests {
bottom: BorderSide::new(4., Color::BLACK.into()),
},
}
}
}
widget_layout_test!(
with_border,
},
{ path = [0], width == 100., height == 100., }
{ path = [0, 0], rect == ribir_geom::rect(0., 0., 100., 100.), }
);
Expand Down
5 changes: 1 addition & 4 deletions core/src/builtin_widgets/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,5 @@ mod tests {
use crate::test_helper::*;

const SIZE: Size = Size::new(100., 100.);
fn smoke() -> impl IntoWidget<'static, FN> {
fn_widget! { @Container { size: SIZE }}
}
widget_layout_test!(smoke, size == SIZE,);
widget_layout_test!(smoke, fn_widget! { @Container { size: SIZE }}, size == SIZE,);
}
24 changes: 14 additions & 10 deletions core/src/builtin_widgets/fitted_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ mod tests {
use ribir_dev_helper::*;

use super::*;
use crate::test_helper::*;
use crate::{reset_test_env, test_helper::*};

const WND_SIZE: Size = Size::new(300., 300.);

Expand All @@ -128,22 +128,24 @@ mod tests {
impl FitTestCase {
fn test(self) {
let Self { box_fit, size, expect, expected_scale } = self;
let fit = Stateful::new(FittedBox { box_fit, scale_cache: <_>::default() });
let c_fit = fit.clone_reader();

let (fit, w_fit) = split_value(FittedBox { box_fit, scale_cache: <_>::default() });

let w = fn_widget! {
@$fit { @MockBox { size } }
let w_fit = w_fit.clone_writer();
@$w_fit { @MockBox { size } }
};
let mut wnd = TestWindow::new_with_size(w, WND_SIZE);
wnd.draw_frame();

assert_layout_result_by_path!(wnd, {path = [0], size == expect,} );
assert_eq!(c_fit.read().scale_cache.get(), expected_scale);
assert_eq!(fit.read().scale_cache.get(), expected_scale);
}
}

#[test]
fn fit_test() {
let _guard = unsafe { AppCtx::new_lock_scope() };
reset_test_env!();

let small_size: Size = Size::new(100., 150.);

Expand Down Expand Up @@ -189,13 +191,15 @@ mod tests {
.test();
}

fn as_builtin_field() -> impl IntoWidget<'static, FN> {
widget_layout_test!(
as_builtin_field,
fn_widget! {
@MockBox {
size: Size::new(200., 200.),
box_fit: BoxFit::Fill,
}
}
}
widget_layout_test!(as_builtin_field, wnd_size = WND_SIZE, size == WND_SIZE,);
},
wnd_size = WND_SIZE,
size == WND_SIZE,
);
}
10 changes: 4 additions & 6 deletions core/src/builtin_widgets/global_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,9 @@ mod tests {
use crate::test_helper::*;

const WND_SIZE: Size = Size::new(100., 100.);
fn global_anchor() -> impl IntoWidget<'static, FN> {

widget_layout_test!(
global_anchor,
fn_widget! {
let parent = @MockBox {
anchor: Anchor::left_top(10., 10.),
Expand All @@ -202,11 +204,7 @@ mod tests {
@ { bottom_right }
}
}
}
}

widget_layout_test!(
global_anchor,
},
wnd_size = WND_SIZE,
{ path = [0, 0, 0, 0, 0], x == 20.,}
{ path = [0, 0, 0, 0, 0], y == 10.,}
Expand Down
Loading

0 comments on commit f9086cb

Please sign in to comment.