@effindomv2/fui-rs 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "fui-rs"
3
- version = "0.1.18"
3
+ version = "0.1.20"
4
4
  edition = "2021"
5
5
  license = "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial"
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effindomv2/fui-rs",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "private": false,
5
5
  "license": "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial",
6
6
  "description": "EffinDom v2 Rust frontend framework SDK and host generators",
package/src/color.rs ADDED
@@ -0,0 +1,112 @@
1
+ pub const fn rgba(red: u32, green: u32, blue: u32, alpha: u32) -> u32 {
2
+ ((red & 0xff) << 24) | ((green & 0xff) << 16) | ((blue & 0xff) << 8) | (alpha & 0xff)
3
+ }
4
+
5
+ pub const fn rgb(red: u32, green: u32, blue: u32) -> u32 {
6
+ rgba(red, green, blue, 0xff)
7
+ }
8
+
9
+ pub const fn with_alpha(color: u32, alpha: u32) -> u32 {
10
+ (color & 0xffff_ff00) | (alpha & 0xff)
11
+ }
12
+
13
+ pub(crate) fn color_red(color: u32) -> u32 {
14
+ (color >> 24) & 0xff
15
+ }
16
+
17
+ pub(crate) fn color_green(color: u32) -> u32 {
18
+ (color >> 16) & 0xff
19
+ }
20
+
21
+ pub(crate) fn color_blue(color: u32) -> u32 {
22
+ (color >> 8) & 0xff
23
+ }
24
+
25
+ pub(crate) fn color_alpha(color: u32) -> u32 {
26
+ color & 0xff
27
+ }
28
+
29
+ fn clamp_unit(value: f32) -> f32 {
30
+ value.clamp(0.0, 1.0)
31
+ }
32
+
33
+ fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
34
+ if t < 0.0 {
35
+ t += 1.0;
36
+ }
37
+ if t > 1.0 {
38
+ t -= 1.0;
39
+ }
40
+ if t < 1.0 / 6.0 {
41
+ return p + (q - p) * 6.0 * t;
42
+ }
43
+ if t < 0.5 {
44
+ return q;
45
+ }
46
+ if t < 2.0 / 3.0 {
47
+ return p + (q - p) * ((2.0 / 3.0) - t) * 6.0;
48
+ }
49
+ p
50
+ }
51
+
52
+ pub fn hsl_to_color(hue: f32, saturation: f32, lightness: f32) -> u32 {
53
+ let normalized_hue = hue % 360.0;
54
+ let hue = if normalized_hue < 0.0 {
55
+ normalized_hue + 360.0
56
+ } else {
57
+ normalized_hue
58
+ };
59
+ let saturation = clamp_unit(saturation);
60
+ let lightness = clamp_unit(lightness);
61
+ if saturation <= 0.0 {
62
+ let channel = (lightness * 255.0) as u32;
63
+ return rgb(channel, channel, channel);
64
+ }
65
+
66
+ let hue_fraction = hue / 360.0;
67
+ let q = if lightness < 0.5 {
68
+ lightness * (1.0 + saturation)
69
+ } else {
70
+ lightness + saturation - (lightness * saturation)
71
+ };
72
+ let p = 2.0 * lightness - q;
73
+ rgba(
74
+ (clamp_unit(hue_to_rgb(p, q, hue_fraction + 1.0 / 3.0)) * 255.0) as u32,
75
+ (clamp_unit(hue_to_rgb(p, q, hue_fraction)) * 255.0) as u32,
76
+ (clamp_unit(hue_to_rgb(p, q, hue_fraction - 1.0 / 3.0)) * 255.0) as u32,
77
+ 0xff,
78
+ )
79
+ }
80
+
81
+ fn mix_channel(from: u32, to: u32, amount: f32) -> u32 {
82
+ let weight = clamp_unit(amount);
83
+ (from as f32 + ((to as f32 - from as f32) * weight)).round() as u32
84
+ }
85
+
86
+ pub fn mix_color(from: u32, to: u32, amount: f32) -> u32 {
87
+ rgba(
88
+ mix_channel(color_red(from), color_red(to), amount),
89
+ mix_channel(color_green(from), color_green(to), amount),
90
+ mix_channel(color_blue(from), color_blue(to), amount),
91
+ mix_channel(color_alpha(from), color_alpha(to), amount),
92
+ )
93
+ }
94
+
95
+ #[cfg(test)]
96
+ mod tests {
97
+ use super::*;
98
+
99
+ #[test]
100
+ fn color_helpers_match_effindom_rgba_packing() {
101
+ assert_eq!(rgb(0x12, 0x34, 0x56), 0x123456ff);
102
+ assert_eq!(rgba(0x12, 0x34, 0x56, 0x78), 0x12345678);
103
+ assert_eq!(with_alpha(0x123456ff, 0x78), 0x12345678);
104
+ }
105
+
106
+ #[test]
107
+ fn color_helpers_mask_channels_and_interpolate() {
108
+ assert_eq!(rgba(0x112, 0x134, 0x156, 0x178), 0x12345678);
109
+ assert_eq!(mix_color(rgb(0, 0, 0), rgb(255, 255, 255), 0.5), rgba(128, 128, 128, 255));
110
+ assert_eq!(hsl_to_color(0.0, 1.0, 0.5), rgb(255, 0, 0));
111
+ }
112
+ }
@@ -59,6 +59,8 @@ impl NavLink {
59
59
  label_node
60
60
  .font_family(theme.fonts.body_family.clone())
61
61
  .font_size(15.0)
62
+ .text_color(theme.colors.accent)
63
+ .selectable(false)
62
64
  .cursor(CursorStyle::Pointer);
63
65
  root.justify_content(JustifyContent::Start)
64
66
  .align_items(AlignItems::Center)
@@ -440,10 +442,7 @@ impl NavLinkEventTarget {
440
442
  let color = if self.hovered.get() {
441
443
  theme.colors.accent_hovered
442
444
  } else {
443
- self.text_color_override
444
- .get()
445
- .or_else(|| self.label.configured_text_color())
446
- .unwrap_or(theme.colors.accent)
445
+ self.text_color_override.get().unwrap_or(theme.colors.accent)
447
446
  };
448
447
  if self.label.handle() != NodeHandle::INVALID {
449
448
  ui::set_text_color(self.label.handle().raw(), color);
@@ -59,10 +59,7 @@ pub(crate) fn update_semantic_checked(
59
59
  state: SemanticCheckedState,
60
60
  announce: bool,
61
61
  ) {
62
- if let Some(handle) = upgraded_handle(weak_root) {
63
- ui::set_semantic_checked(handle.raw(), state as u32);
64
- if announce {
65
- ui::request_semantic_announcement(handle.raw());
66
- }
62
+ if let Some(root) = weak_root.upgrade() {
63
+ root.set_semantic_checked(state, announce);
67
64
  }
68
65
  }
@@ -2927,6 +2927,23 @@ fn checkbox_user_activation_emits_changed_and_semantic_announcement() {
2927
2927
  Application::unmount();
2928
2928
  }
2929
2929
 
2930
+ #[test]
2931
+ fn checkbox_prebuild_state_is_retained_into_semantic_build_state() {
2932
+ ffi::test::reset();
2933
+ let checkbox = checkbox("Agree");
2934
+ checkbox.check(true);
2935
+
2936
+ Application::mount(checkbox.clone());
2937
+ let handle = checkbox.retained_node_ref().handle().raw();
2938
+ let calls = ffi::test::take_calls();
2939
+ assert!(calls.iter().any(|call| matches!(
2940
+ call,
2941
+ Call::SetSemanticChecked { handle: checked_handle, checked_state_enum }
2942
+ if *checked_handle == handle && *checked_state_enum == SemanticCheckedState::True as u32
2943
+ )));
2944
+ Application::unmount();
2945
+ }
2946
+
2930
2947
  #[test]
2931
2948
  fn radio_and_switch_programmatic_changes_emit_without_semantic_announcement() {
2932
2949
  ffi::test::reset();
package/src/lib.rs CHANGED
@@ -4,6 +4,7 @@ pub mod assets;
4
4
  #[doc(hidden)]
5
5
  pub mod bindings;
6
6
  pub mod bitmap;
7
+ pub mod color;
7
8
  #[doc(hidden)]
8
9
  pub mod bridge_callbacks;
9
10
  pub(crate) mod context_menu_manager;
@@ -408,6 +409,7 @@ pub mod prelude {
408
409
  pub use crate::app::{Application, ApplicationRegistration, ManagedApplication};
409
410
  pub use crate::bitmap::{Bitmap, BitmapTextReadyEventArgs};
410
411
  pub use crate::bridge_callbacks::current_route;
412
+ pub use crate::color::{hsl_to_color, mix_color, rgb, rgba, with_alpha};
411
413
  pub use crate::controls::{
412
414
  anti_selection_area, button, checkbox, clear_control_templates, combo_box, context_menu,
413
415
  create_default_button_presenter, create_default_checkbox_indicator_presenter,
@@ -486,10 +488,10 @@ pub mod prelude {
486
488
  auto, column, custom_drawable, fill, flex_box, grid, image, pct, portal, px, row,
487
489
  scroll_box, scroll_view, svg, text, viewport_height, viewport_width, virtual_list, Border,
488
490
  Child, ContextMenuEventArgs, Corners, CustomDrawable, DrawableInvalidator, EdgeInsets,
489
- FlexBox, FlexBoxSurface, GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode,
490
- Length, Node, PresenterHostStyle, ScrollBar, ScrollBarStyle, ScrollBarVisibility,
491
- ScrollBox, ScrollState, ScrollView, Shadow, Svg, SvgNode, Text, TextCore, TextNode,
492
- ThemeBindable, VirtualList,
491
+ FlexBox, FlexBoxSurface, GradientStop, Grid, GridTrack, HasFlexBoxRoot, HasTextNode, Image,
492
+ ImageNode, Length, Node, PresenterHostStyle, ScrollBar, ScrollBarStyle,
493
+ ScrollBarVisibility, ScrollBox, ScrollState, ScrollView, Shadow, Svg, SvgNode, Text,
494
+ TextCore, TextLayoutSurface, TextNode, ThemeBindable, VirtualList,
493
495
  };
494
496
  pub use crate::persisted;
495
497
  pub use crate::platform;
@@ -534,6 +536,7 @@ pub use app::{Application, ApplicationRegistration, ManagedApplication};
534
536
  pub use assets::*;
535
537
  pub use bitmap::{Bitmap, BitmapTextReadyEventArgs};
536
538
  pub use bridge_callbacks::current_route;
539
+ pub use color::{hsl_to_color, mix_color, rgb, rgba, with_alpha};
537
540
  pub use controls::{
538
541
  anti_selection_area, button, checkbox, clear_control_templates, combo_box, context_menu,
539
542
  create_default_button_presenter, create_default_checkbox_indicator_presenter,
@@ -606,9 +609,10 @@ pub use node::{
606
609
  auto, column, custom_drawable, fill, flex_box, grid, image, pct, portal, px, row, scroll_box,
607
610
  scroll_view, svg, text, viewport_height, viewport_width, virtual_list, Border, Child,
608
611
  ContextMenuEventArgs, Corners, CustomDrawable, DrawableInvalidator, EdgeInsets, FlexBox,
609
- FlexBoxSurface, GradientStop, Grid, GridTrack, HasFlexBoxRoot, Image, ImageNode, Length, Node,
610
- PresenterHostStyle, ScrollBar, ScrollBarStyle, ScrollBarVisibility, ScrollBox, ScrollState,
611
- ScrollView, Shadow, Svg, SvgNode, Text, TextCore, TextNode, ThemeBindable, VirtualList,
612
+ FlexBoxSurface, GradientStop, Grid, GridTrack, HasFlexBoxRoot, HasTextNode, Image, ImageNode,
613
+ Length, Node, PresenterHostStyle, ScrollBar, ScrollBarStyle, ScrollBarVisibility, ScrollBox,
614
+ ScrollState, ScrollView, Shadow, Svg, SvgNode, Text, TextCore, TextLayoutSurface, TextNode,
615
+ ThemeBindable, VirtualList,
612
616
  };
613
617
  pub use persisted::*;
614
618
  pub use platform::*;
package/src/node/core.rs CHANGED
@@ -513,6 +513,26 @@ impl NodeRef {
513
513
  self.inner.borrow().handle
514
514
  }
515
515
 
516
+ pub(crate) fn set_semantic_checked(
517
+ &self,
518
+ state: SemanticCheckedState,
519
+ announce: bool,
520
+ ) {
521
+ let handle = {
522
+ let mut core = self.inner.borrow_mut();
523
+ core.behavior.semantic_checked = Some(state);
524
+ core.handle
525
+ };
526
+ if handle == NodeHandle::INVALID {
527
+ return;
528
+ }
529
+ ui::set_semantic_checked(handle.raw(), state as u32);
530
+ if announce {
531
+ ui::request_semantic_announcement(handle.raw());
532
+ }
533
+ crate::frame_scheduler::mark_needs_commit();
534
+ }
535
+
516
536
  pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
517
537
  Rc::ptr_eq(&self.inner, &other.inner)
518
538
  }
package/src/node/mod.rs CHANGED
@@ -100,6 +100,85 @@ impl ThemeBindable for FlexBox {
100
100
  }
101
101
  }
102
102
 
103
+ pub trait HasTextNode {
104
+ fn text_node(&self) -> &TextNode;
105
+ }
106
+
107
+ pub trait TextLayoutSurface: HasTextNode {
108
+ fn width(&self, width: f32, unit: Unit) -> &Self {
109
+ self.text_node().width(width, unit);
110
+ self
111
+ }
112
+
113
+ fn height(&self, height: f32, unit: Unit) -> &Self {
114
+ self.text_node().height(height, unit);
115
+ self
116
+ }
117
+
118
+ fn fill_width(&self) -> &Self {
119
+ self.text_node().fill_width();
120
+ self
121
+ }
122
+
123
+ fn fill_height(&self) -> &Self {
124
+ self.text_node().fill_height();
125
+ self
126
+ }
127
+
128
+ fn fill_size(&self) -> &Self {
129
+ self.text_node().fill_size();
130
+ self
131
+ }
132
+
133
+ fn line_height(&self, line_height: f32) -> &Self {
134
+ self.text_node().line_height(line_height);
135
+ self
136
+ }
137
+
138
+ fn text_align(&self, align: TextAlign) -> &Self {
139
+ self.text_node().text_align(align);
140
+ self
141
+ }
142
+
143
+ fn text_vertical_align(&self, align: TextVerticalAlign) -> &Self {
144
+ self.text_node().text_vertical_align(align);
145
+ self
146
+ }
147
+
148
+ fn text_limits(&self, max_chars: i32, max_lines: i32) -> &Self {
149
+ self.text_node().text_limits(max_chars, max_lines);
150
+ self
151
+ }
152
+
153
+ fn max_lines(&self, max_lines: i32) -> &Self {
154
+ self.text_node().max_lines(max_lines);
155
+ self
156
+ }
157
+
158
+ fn wrapping(&self, wrap: bool) -> &Self {
159
+ self.text_node().wrapping(wrap);
160
+ self
161
+ }
162
+
163
+ fn text_overflow(&self, overflow: TextOverflow) -> &Self {
164
+ self.text_node().text_overflow(overflow);
165
+ self
166
+ }
167
+
168
+ fn text_overflow_fade(&self, horizontal: bool, vertical: bool) -> &Self {
169
+ self.text_node().text_overflow_fade(horizontal, vertical);
170
+ self
171
+ }
172
+ }
173
+
174
+ impl<T: HasTextNode> TextLayoutSurface for T {}
175
+
176
+ impl HasTextNode for TextNode {
177
+ fn text_node(&self) -> &TextNode {
178
+ self
179
+ }
180
+ }
181
+
103
182
  pub trait FlexBoxSurface: HasFlexBoxRoot {
104
183
  fn width(&self, width: f32, unit: Unit) -> &Self {
105
184
  self.flex_box_root().width(width, unit);
@@ -164,10 +164,6 @@ impl TextNode {
164
164
  self
165
165
  }
166
166
 
167
- pub(crate) fn configured_text_color(&self) -> Option<u32> {
168
- self.props.borrow().text_color
169
- }
170
-
171
167
  pub fn style_runs(&self, words: Vec<u32>) -> &Self {
172
168
  {
173
169
  let mut props = self.props.borrow_mut();
package/src/text.rs CHANGED
@@ -3,7 +3,7 @@ use crate::bindings::ui;
3
3
  use crate::ffi::{TextAlign, TextOverflow, TextVerticalAlign, Unit};
4
4
  use crate::frame_scheduler::on_loaded;
5
5
  use crate::logger::error;
6
- use crate::node::{Node, TextNode};
6
+ use crate::node::{HasTextNode, Node, TextNode, ThemeBindable};
7
7
  use crate::theme;
8
8
  use crate::typography::{FontFamily, FontStack, FontStyle, FontWeight};
9
9
  use std::cell::RefCell;
@@ -247,6 +247,29 @@ impl Deref for RichText {
247
247
  }
248
248
  }
249
249
 
250
+ impl HasTextNode for RichText {
251
+ fn text_node(&self) -> &TextNode {
252
+ &self.node
253
+ }
254
+ }
255
+
256
+ impl ThemeBindable for RichText {
257
+ fn theme_binding_node(&self) -> crate::node::NodeRef {
258
+ self.node.retained_node_ref()
259
+ }
260
+
261
+ fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
262
+ let weak_node = self.node.weak_theme_target();
263
+ let weak_state = Rc::downgrade(&self.state);
264
+ Box::new(move || {
265
+ Some(Self {
266
+ node: weak_node()?,
267
+ state: weak_state.upgrade()?,
268
+ })
269
+ })
270
+ }
271
+ }
272
+
250
273
  impl Node for RichText {
251
274
  fn retained_node_ref(&self) -> crate::node::NodeRef {
252
275
  self.node.retained_node_ref()
package/src/theme.rs CHANGED
@@ -1,12 +1,15 @@
1
1
  use crate::generated::framework_host_services;
2
+ use crate::color::{
3
+ color_alpha, color_blue, color_green, color_red, mix_color, rgb, rgba, with_alpha,
4
+ };
2
5
  use crate::signal::{Callback, Signal, SubscriptionGuard};
3
6
  use crate::typography::{FontFamily, FontStack};
4
7
  use std::cell::RefCell;
5
8
  use std::rc::Rc;
6
9
 
7
- const DEFAULT_ACCENT_COLOR: u32 = 0x2563EBFF;
8
- const WHITE: u32 = 0xFFFFFFFF;
9
- const BLACK: u32 = 0x000000FF;
10
+ const DEFAULT_ACCENT_COLOR: u32 = rgb(0x25, 0x63, 0xeb);
11
+ const WHITE: u32 = rgb(0xff, 0xff, 0xff);
12
+ const BLACK: u32 = rgb(0x00, 0x00, 0x00);
10
13
 
11
14
  #[derive(Clone, Copy, Debug, PartialEq, Eq)]
12
15
  pub struct Colors {
@@ -166,49 +169,6 @@ fn default_fonts() -> Fonts {
166
169
  }
167
170
  }
168
171
 
169
- const fn rgba(r: u32, g: u32, b: u32, a: u32) -> u32 {
170
- (r << 24) | (g << 16) | (b << 8) | a
171
- }
172
-
173
- fn color_red(color: u32) -> u32 {
174
- (color >> 24) & 0xff
175
- }
176
- fn color_green(color: u32) -> u32 {
177
- (color >> 16) & 0xff
178
- }
179
- fn color_blue(color: u32) -> u32 {
180
- (color >> 8) & 0xff
181
- }
182
- fn color_alpha(color: u32) -> u32 {
183
- color & 0xff
184
- }
185
- fn clamp_unit(value: f32) -> f32 {
186
- value.clamp(0.0, 1.0)
187
- }
188
-
189
- fn mix_channel(from: u32, to: u32, amount: f32) -> u32 {
190
- let weight = clamp_unit(amount);
191
- (from as f32 + ((to as f32 - from as f32) * weight)).round() as u32
192
- }
193
-
194
- fn mix_color(from: u32, to: u32, amount: f32) -> u32 {
195
- rgba(
196
- mix_channel(color_red(from), color_red(to), amount),
197
- mix_channel(color_green(from), color_green(to), amount),
198
- mix_channel(color_blue(from), color_blue(to), amount),
199
- mix_channel(color_alpha(from), color_alpha(to), amount),
200
- )
201
- }
202
-
203
- fn with_alpha(color: u32, alpha: u32) -> u32 {
204
- rgba(
205
- color_red(color),
206
- color_green(color),
207
- color_blue(color),
208
- alpha,
209
- )
210
- }
211
-
212
172
  fn normalize_accent_color(color: u32) -> u32 {
213
173
  if color == 0 {
214
174
  return DEFAULT_ACCENT_COLOR;