@effindomv2/fui-rs 0.1.18 → 0.1.19

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.19"
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.19",
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,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,
@@ -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,
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/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;