1use super::color_names::{
3 color_name, color_resource_key, contrast_ratio, default_palette, high_contrast_colors,
4};
5#[path = "ink_toolbar_strings.rs"]
6pub(crate) mod strings;
7use super::{
8 InkDrawingAttributes, InkError, InkHighContrastAdjustment, InkToolbar, InkToolbarFlyoutItem,
9 InkToolbarFlyoutItemKind, InkToolbarMenuKind, InkToolbarStencilKind, InkToolbarToggle,
10 InkToolbarTool, PenTipShape,
11};
12use ferroui_controls::automation::AutomationProperties;
13use ferroui_controls::primitives::{TemplatedControlImpl, ToggleButton, ToggleButtonImpl};
14use ferroui_controls::{
15 Button, ButtonImpl, ButtonImplExt, CheckBox, ContentControlImpl, Control, ControlImpl, Flyout,
16 RadioButton, StackPanel,
17};
18use ferroui_base::collections::FerroList;
19use ferroui_base::controls::ResourceKey;
20use ferroui_base::input::{
21 HoldingRoutedEventArgs, HoldingState, InputElement, InputElementImpl, InputElementImplExt,
22 TappedEventArgs,
23};
24use ferroui_base::interactivity::InteractiveImpl;
25use ferroui_base::layout::LayoutableImpl;
26use ferroui_base::media::immutable::ImmutableSolidColorBrush;
27use ferroui_base::media::{Color, IBrush};
28use ferroui_base::styling::ControlTheme;
29use ferroui_base::{
30 ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, instantiate,
31 DirectProperty, FerroObject, FerroObjectImpl, FerroObjectImplExt, FerroProperty,
32 FerroPropertyChangedEventArgs, Ref, Size, StyledElement, StyledElementImpl, StyledProperty,
33 StyledPropertyOptions, TypeInfo, VisualImpl,
34};
35use std::cell::{Cell, RefCell};
36use std::rc::Rc;
37
38type Palette = FerroList<Rc<dyn IBrush>>;
39
40#[repr(C)]
42pub struct InkToolbarToolButton {
43 base: RadioButton,
44 tool_kind: Cell<InkToolbarTool>,
45 permit_flyout: Cell<bool>,
46}
47ferro_class!(InkToolbarToolButton: RadioButton);
48ferro_class_info!(InkToolbarToolButton { new: InkToolbarToolButton::new, markup: {
49 properties: [ToolKind: InkToolbarTool { get: InkToolbarToolButton::tool_kind }],
50} });
51ferro_impl_classes!(InkToolbarToolButton: FerroObjectImpl, VisualImpl, LayoutableImpl, InteractiveImpl, TemplatedControlImpl, ContentControlImpl, ToggleButtonImpl);
52impl InputElementImpl for InkToolbarToolButton {
53 fn on_right_tapped(this: &Self, event: &TappedEventArgs) {
54 Self::parent_on_right_tapped(this, event);
55 if this.is_effectively_enabled() {
56 this.activate_secondary();
57 event.set_handled(true);
58 }
59 }
60 fn on_holding(this: &Self, event: &HoldingRoutedEventArgs) {
61 Self::parent_on_holding(this, event);
62 if this.is_effectively_enabled() && event.holding_state() == HoldingState::Started {
63 this.activate_secondary();
64 event.set_handled(true);
65 }
66 }
67}
68impl ButtonImpl for InkToolbarToolButton {
69 fn on_click(this: &Self) {
70 let was_checked = this.is_checked() == Some(true);
71 let previous = this.permit_flyout.replace(was_checked);
72 Self::parent_on_click(this);
73 this.permit_flyout.set(previous);
74 }
75 fn open_flyout(this: &Self) {
76 if this.permit_flyout.get() && this.can_open_configuration() {
77 Self::parent_open_flyout(this);
78 }
79 }
80}
81impl ControlImpl for InkToolbarToolButton {
82 fn on_create_automation_peer(this: &Self) -> Ref<ferroui_controls::automation::peers::AutomationPeer> {
83 super::InkToolbarToolButtonAutomationPeer::new(this).upcast()
84 }
85}
86
87impl StyledElementImpl for InkToolbarToolButton {
88 fn style_key_override(this: &Self) -> &'static TypeInfo {
89 toolbar_style_key(this, RadioButton::TYPE)
90 }
91}
92ferro_properties! { impl InkToolbarToolButton {
93 pub fn is_extension_glyph_shown_property() -> StyledProperty<bool> { FerroProperty::register::<InkToolbarToolButton,_>("IsExtensionGlyphShown", true) }
94} }
95impl InkToolbarToolButton {
96 fn static_constructor() {
97 InputElement::is_holding_enabled_property()
98 .override_default_value::<InkToolbarToolButton>(true);
99 }
100 fn activate_secondary(&self) {
101 self.set_current_value(ToggleButton::is_checked_property(), Some(true));
102 self.open_flyout();
103 }
104 pub(crate) fn can_open_configuration(&self) -> bool {
105 !toolbar_is_inking(self)
106 && self
107 .to_ref()
108 .cast::<InkToolbarCustomPenButton>()
109 .is_none_or(|pen| pen.configuration_content().is_some())
110 && self
111 .to_ref()
112 .cast::<InkToolbarCustomToolButton>()
113 .is_none_or(|tool| tool.configuration_content().is_some())
114 && self
115 .to_ref()
116 .cast::<InkToolbarEraserButton>()
117 .is_none_or(|eraser| eraser.is_clear_all_visible())
118 }
119 pub fn construct() -> Self {
120 Self {
121 base: RadioButton::construct(),
122 tool_kind: Cell::new(InkToolbarTool::CustomTool),
123 permit_flyout: Cell::new(true),
124 }
125 }
126 pub fn new() -> Ref<Self> {
127 instantiate(Self::construct())
128 }
129 pub fn tool_kind(&self) -> InkToolbarTool {
130 self.tool_kind.get()
131 }
132 pub(crate) fn set_tool_kind(&self, value: InkToolbarTool) {
133 self.tool_kind.set(value);
134 }
135 pub fn is_extension_glyph_shown(&self) -> bool {
136 self.get_value(Self::is_extension_glyph_shown_property())
137 }
138 pub fn set_is_extension_glyph_shown(&self, value: bool) {
139 self.set_value(Self::is_extension_glyph_shown_property(), value);
140 }
141}
142
143#[repr(C)]
145pub struct InkToolbarPenButton {
146 base: InkToolbarToolButton,
147 palette_subscription: RefCell<Option<(Palette, u64)>>,
148 selected_brush: RefCell<Option<Rc<dyn IBrush>>>,
149 contrast_palette: RefCell<Option<Vec<Rc<dyn IBrush>>>>,
150 contrast_signature: Cell<Option<(InkHighContrastAdjustment, Color, Color)>>,
151 normal_index: Cell<i32>,
152 contrast_index: Cell<i32>,
153}
154ferro_class! { InkToolbarPenButton: InkToolbarToolButton, virtuals InkToolbarPenButtonImpl: ToggleButtonImpl {
155 fn create_ink_drawing_attributes(this) -> Result<InkDrawingAttributes, InkError>;
156} }
157ferro_class_info!(InkToolbarPenButton {
158 new: InkToolbarPenButton::new
159});
160ferro_impl_classes!(InkToolbarPenButton: StyledElementImpl, VisualImpl, LayoutableImpl, InteractiveImpl, InputElementImpl, ControlImpl, TemplatedControlImpl, ContentControlImpl, ButtonImpl, ToggleButtonImpl);
161impl FerroObjectImpl for InkToolbarPenButton {
162 fn constructed(this: &Self) {
163 Self::parent_constructed(this);
164 this.set_palette(default_palette(false));
165 let weak = this.to_ref().downgrade();
166 this.resources_changed(move |_| {
167 if let Some(pen) = weak.upgrade() {
168 pen.refresh_palette();
169 }
170 });
171 let weak = this.to_ref().downgrade();
172 this.actual_theme_variant_changed(move || {
173 if let Some(pen) = weak.upgrade() {
174 pen.refresh_palette();
175 }
176 });
177 }
178 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
179 Self::parent_on_property_changed(this, change);
180 if change.property() == Self::palette_property().as_property() {
181 if let Some((old, token)) = this.palette_subscription.borrow_mut().take() {
182 old.remove_collection_changed(token);
183 }
184 let palette = this.palette();
185 let weak = this.to_ref().downgrade();
186 let token = palette.add_collection_changed(Rc::new(move |_| {
187 if let Some(pen) = weak.upgrade() {
188 pen.rebuild_contrast_palette(true);
189 }
190 }));
191 *this.palette_subscription.borrow_mut() = Some((palette, token));
192 this.rebuild_contrast_palette(true);
193 } else if change.property() == Self::selected_brush_index_property().as_property() {
194 this.update_selected_brush();
195 } else if change.property() == Self::min_stroke_width_property().as_property()
196 || change.property() == Self::max_stroke_width_property().as_property()
197 {
198 this.coerce_value(Self::selected_stroke_width_property().as_property());
199 }
200 }
201}
202impl InkToolbarPenButtonImpl for InkToolbarPenButton {
203 fn create_ink_drawing_attributes(this: &Self) -> Result<InkDrawingAttributes, InkError> {
204 let width = this.determine_stroke_width();
205 let attrs = InkDrawingAttributes {
206 color: this.selected_color()?,
207 size: Size::new(width, width),
208 ..InkDrawingAttributes::default()
209 };
210 attrs.validate()?;
211 Ok(attrs)
212 }
213}
214ferro_properties! { impl InkToolbarPenButton {
215 pub fn palette_property() -> StyledProperty<FerroList<Rc<dyn IBrush>>> { FerroProperty::register::<InkToolbarPenButton,_>("Palette", FerroList::new()) }
216 pub fn min_stroke_width_property() -> StyledProperty<f64> { FerroProperty::register_with::<InkToolbarPenButton,_>("MinStrokeWidth", StyledPropertyOptions::new(0.5_f64).coerce(|_,v| if v.is_finite() && v > 0.0 {v} else {0.5})) }
217 pub fn max_stroke_width_property() -> StyledProperty<f64> { FerroProperty::register_with::<InkToolbarPenButton,_>("MaxStrokeWidth", StyledPropertyOptions::new(32.0_f64).coerce(|_,v| if v.is_finite() && v > 0.0 {v} else {32.0})) }
218 pub fn selected_brush_property() -> DirectProperty<InkToolbarPenButton,Option<Rc<dyn IBrush>>> { FerroProperty::register_direct::<InkToolbarPenButton,_>("SelectedBrush",InkToolbarPenButton::selected_brush,None,None) }
219 pub fn selected_brush_index_property() -> StyledProperty<i32> { FerroProperty::register::<InkToolbarPenButton,_>("SelectedBrushIndex", 0) }
220 pub fn selected_stroke_width_property() -> StyledProperty<f64> {
221 FerroProperty::register_with::<InkToolbarPenButton,_>("SelectedStrokeWidth", StyledPropertyOptions::new(2.0_f64).coerce(|sender,value| {
222 let min = sender.get_value(Self::min_stroke_width_property());
223 let max = sender.get_value(Self::max_stroke_width_property()).max(min);
224 if value.is_finite() { value.clamp(min,max) } else { min }
225 }))
226 }
227} }
228impl InkToolbarPenButton {
229 pub fn construct() -> Self {
230 Self {
231 base: InkToolbarToolButton::construct(),
232 palette_subscription: RefCell::new(None),
233 selected_brush: RefCell::new(None),
234 contrast_palette: RefCell::new(None),
235 contrast_signature: Cell::new(None),
236 normal_index: Cell::new(0),
237 contrast_index: Cell::new(0),
238 }
239 }
240 pub fn new() -> Ref<Self> {
241 instantiate(Self::construct())
242 }
243 pub fn palette(&self) -> Palette {
244 self.get_value(Self::palette_property())
245 }
246 pub fn set_palette(&self, value: Palette) {
247 self.set_value(Self::palette_property(), value);
248 }
249 pub fn min_stroke_width(&self) -> f64 {
250 self.get_value(Self::min_stroke_width_property())
251 }
252 pub fn set_min_stroke_width(&self, value: f64) {
253 self.set_value(Self::min_stroke_width_property(), value);
254 }
255 pub fn max_stroke_width(&self) -> f64 {
256 self.get_value(Self::max_stroke_width_property())
257 }
258 pub fn set_max_stroke_width(&self, value: f64) {
259 self.set_value(Self::max_stroke_width_property(), value);
260 }
261 pub fn selected_brush(&self) -> Option<Rc<dyn IBrush>> {
262 self.selected_brush.borrow().clone()
263 }
264 pub fn selected_brush_index(&self) -> i32 {
265 self.get_value(Self::selected_brush_index_property())
266 }
267 pub fn set_selected_brush_index(&self, value: i32) {
268 self.set_value(Self::selected_brush_index_property(), value);
269 }
270 pub fn selected_stroke_width(&self) -> f64 {
271 self.get_value(Self::selected_stroke_width_property())
272 }
273 pub fn set_selected_stroke_width(&self, value: f64) {
274 self.set_value(Self::selected_stroke_width_property(), value);
275 }
276 pub fn determine_stroke_width(&self) -> f64 {
277 self.selected_stroke_width().clamp(
278 self.min_stroke_width(),
279 self.max_stroke_width().max(self.min_stroke_width()),
280 )
281 }
282 pub fn selected_color(&self) -> Result<Color, InkError> {
283 self.selected_brush()
284 .and_then(|b| b.as_solid_color_brush().map(|s| s.color()))
285 .ok_or(InkError::InvalidInput(
286 "Ink pens require a selected solid-color brush",
287 ))
288 }
289 pub(crate) fn effective_palette(&self) -> Vec<Rc<dyn IBrush>> {
290 self.contrast_palette
291 .borrow()
292 .clone()
293 .unwrap_or_else(|| self.palette().snapshot().as_ref().clone())
294 }
295 pub(crate) fn refresh_palette(&self) {
296 self.rebuild_contrast_palette(false);
297 }
298 fn rebuild_contrast_palette(&self, force: bool) {
299 let mut parent = self.parent();
300 let mut mode = InkHighContrastAdjustment::UseSystemColorsWhenNecessary;
301 while let Some(element) = parent {
302 if let Some(toolbar) = element.cast::<InkToolbar>() {
303 mode = toolbar
304 .effective_ink_presenter()
305 .map(|p| p.high_contrast_adjustment())
306 .unwrap_or(mode);
307 break;
308 }
309 parent = element.parent();
310 }
311 let signature = high_contrast_colors(self, self.tool_kind() == InkToolbarTool::Highlighter)
312 .map(|(background, system)| (mode, background, system));
313 let previous = self.contrast_signature.replace(signature);
314 if force || previous != signature {
315 let palette = signature.map(|(mode, background, system)| {
316 let mut result = self
317 .palette()
318 .snapshot()
319 .iter()
320 .filter(|brush| {
321 brush
322 .as_solid_color_brush()
323 .is_some_and(|brush| match mode {
324 InkHighContrastAdjustment::UseOriginalColors => true,
325 InkHighContrastAdjustment::UseSystemColors => false,
326 InkHighContrastAdjustment::UseSystemColorsWhenNecessary => {
327 contrast_ratio(brush.color(), background) >= 7.0
328 }
329 })
330 })
331 .cloned()
332 .collect::<Vec<_>>();
333 if !result.iter().any(|brush| {
334 brush
335 .as_solid_color_brush()
336 .is_some_and(|brush| brush.color() == system)
337 }) {
338 result.push(Rc::new(ImmutableSolidColorBrush::new(system)) as Rc<dyn IBrush>);
339 }
340 result
341 });
342 *self.contrast_palette.borrow_mut() = palette;
343 if previous.is_some() != signature.is_some() {
344 let index = if signature.is_some() {
345 self.contrast_index.get()
346 } else {
347 self.normal_index.get()
348 };
349 self.set_current_value(Self::selected_brush_index_property(), index);
350 }
351 }
352 self.update_selected_brush();
353 }
354 fn update_selected_brush(&self) {
355 let index = self.selected_brush_index();
356 let items = self.effective_palette();
357 let Some(selected) = (index >= 0)
359 .then(|| items.get(index as usize).cloned())
360 .flatten()
361 else {
362 return;
363 };
364 if self.contrast_signature.get().is_some() {
365 self.contrast_index.set(index);
366 } else {
367 self.normal_index.set(index);
368 }
369 let selected = Some(selected);
370 self.set_and_raise(
371 Self::selected_brush_property(),
372 &self.selected_brush,
373 selected.clone(),
374 );
375 let help = selected.and_then(|b| {
376 b.as_solid_color_brush().and_then(|brush| {
377 color_resource_key(brush.color()).map(|_| color_name(self, brush.color()))
378 })
379 });
380 AutomationProperties::set_help_text(self, help.as_deref());
381 }
382}
383
384#[repr(C)]
385pub struct InkToolbarBallpointPenButton {
386 base: InkToolbarPenButton,
387}
388ferro_class!(InkToolbarBallpointPenButton:InkToolbarPenButton);
389ferro_class_info!(InkToolbarBallpointPenButton {
390 new: InkToolbarBallpointPenButton::new
391});
392ferro_impl_classes!(InkToolbarBallpointPenButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
393impl FerroObjectImpl for InkToolbarBallpointPenButton {
394 fn constructed(this: &Self) {
395 Self::parent_constructed(this);
396 this.set_tool_kind(InkToolbarTool::BallpointPen);
397 strings::bind_content(this, "InkToolbarBallpointPenButtonName");
398 }
399}
400impl InkToolbarPenButtonImpl for InkToolbarBallpointPenButton {
401 fn create_ink_drawing_attributes(this: &Self) -> Result<InkDrawingAttributes, InkError> {
402 let width = this.determine_stroke_width();
403 Ok(InkDrawingAttributes {
404 color: this.selected_color()?,
405 size: Size::new(width, width),
406 ..InkDrawingAttributes::default()
407 })
408 }
409}
410impl InkToolbarBallpointPenButton {
411 fn static_constructor() {
412 override_stroke_widths::<InkToolbarBallpointPenButton>(1.0, 24.0, 2.0);
415 }
416 pub fn construct() -> Self {
417 Self {
418 base: InkToolbarPenButton::construct(),
419 }
420 }
421 pub fn new() -> Ref<Self> {
422 instantiate(Self::construct())
423 }
424}
425#[repr(C)]
426pub struct InkToolbarPencilButton {
427 base: InkToolbarPenButton,
428}
429ferro_class!(InkToolbarPencilButton:InkToolbarPenButton);
430ferro_class_info!(InkToolbarPencilButton {
431 new: InkToolbarPencilButton::new
432});
433ferro_impl_classes!(InkToolbarPencilButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
434impl FerroObjectImpl for InkToolbarPencilButton {
435 fn constructed(this: &Self) {
436 Self::parent_constructed(this);
437 this.set_tool_kind(InkToolbarTool::Pencil);
438 strings::bind_content(this, "InkToolbarPencilButtonName");
439 }
440}
441impl InkToolbarPenButtonImpl for InkToolbarPencilButton {
442 fn create_ink_drawing_attributes(this: &Self) -> Result<InkDrawingAttributes, InkError> {
443 let width = this.determine_stroke_width();
444 Ok(InkDrawingAttributes {
445 color: this.selected_color()?,
446 size: Size::new(width, width),
447 ..InkDrawingAttributes::create_for_pencil()
448 })
449 }
450}
451impl InkToolbarPencilButton {
452 fn static_constructor() {
453 override_stroke_widths::<InkToolbarPencilButton>(1.0, 24.0, 4.0);
456 }
457 pub fn construct() -> Self {
458 Self {
459 base: InkToolbarPenButton::construct(),
460 }
461 }
462 pub fn new() -> Ref<Self> {
463 instantiate(Self::construct())
464 }
465}
466#[repr(C)]
467pub struct InkToolbarHighlighterButton {
468 base: InkToolbarPenButton,
469}
470ferro_class!(InkToolbarHighlighterButton:InkToolbarPenButton);
471ferro_class_info!(InkToolbarHighlighterButton {
472 new: InkToolbarHighlighterButton::new
473});
474ferro_impl_classes!(InkToolbarHighlighterButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
475impl FerroObjectImpl for InkToolbarHighlighterButton {
476 fn constructed(this: &Self) {
477 Self::parent_constructed(this);
478 this.set_tool_kind(InkToolbarTool::Highlighter);
479 strings::bind_content(this, "InkToolbarHighlighterButtonName");
480 this.set_palette(default_palette(true));
481 this.set_selected_stroke_width(16.0);
483 }
484}
485impl InkToolbarPenButtonImpl for InkToolbarHighlighterButton {
486 fn create_ink_drawing_attributes(this: &Self) -> Result<InkDrawingAttributes, InkError> {
487 let width = this.determine_stroke_width();
488 Ok(InkDrawingAttributes {
489 color: this.selected_color()?,
490 size: Size::new(width / 3.0, width),
491 pen_tip: PenTipShape::Rectangle,
492 draw_as_highlighter: true,
493 ..InkDrawingAttributes::default()
494 })
495 }
496}
497impl InkToolbarHighlighterButton {
498 fn static_constructor() {
499 override_stroke_widths::<InkToolbarHighlighterButton>(12.0, 64.0, 16.0);
502 }
503 pub fn construct() -> Self {
504 Self {
505 base: InkToolbarPenButton::construct(),
506 }
507 }
508 pub fn new() -> Ref<Self> {
509 instantiate(Self::construct())
510 }
511}
512
513#[repr(C)]
515pub struct InkToolbarCustomPen {
516 base: FerroObject,
517}
518ferro_class! { InkToolbarCustomPen: FerroObject, virtuals InkToolbarCustomPenImpl: FerroObjectImpl {
519 fn create_ink_drawing_attributes_core(this, brush: Option<Rc<dyn IBrush>>, stroke_width: f64) -> Result<InkDrawingAttributes,InkError>;
520} }
521ferro_class_info!(InkToolbarCustomPen {
522 new: InkToolbarCustomPen::new
523});
524ferro_impl_classes!(InkToolbarCustomPen: FerroObjectImpl);
525impl InkToolbarCustomPenImpl for InkToolbarCustomPen {
526 fn create_ink_drawing_attributes_core(
527 _this: &Self,
528 brush: Option<Rc<dyn IBrush>>,
529 stroke_width: f64,
530 ) -> Result<InkDrawingAttributes, InkError> {
531 let color = brush
532 .as_ref()
533 .and_then(|brush| brush.as_solid_color_brush())
534 .map(|brush| brush.color())
535 .unwrap_or_else(|| InkDrawingAttributes::default().color);
536 let attrs = InkDrawingAttributes {
537 color,
538 size: Size::new(stroke_width, stroke_width),
539 ..InkDrawingAttributes::default()
540 };
541 attrs.validate()?;
542 Ok(attrs)
543 }
544}
545impl InkToolbarCustomPen {
546 pub fn construct() -> Self {
547 Self {
548 base: FerroObject::construct(),
549 }
550 }
551 pub fn new() -> Ref<Self> {
552 instantiate(Self::construct())
553 }
554 pub fn create_ink_drawing_attributes(
555 &self,
556 brush: Option<Rc<dyn IBrush>>,
557 stroke_width: f64,
558 ) -> Result<InkDrawingAttributes, InkError> {
559 self.create_ink_drawing_attributes_core(brush, stroke_width)
560 }
561}
562#[repr(C)]
563pub struct InkToolbarCustomPenButton {
564 base: InkToolbarPenButton,
565}
566ferro_class!(InkToolbarCustomPenButton:InkToolbarPenButton);
567ferro_class_info!(InkToolbarCustomPenButton {
568 new: InkToolbarCustomPenButton::new
569});
570ferro_impl_classes!(InkToolbarCustomPenButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
571impl FerroObjectImpl for InkToolbarCustomPenButton {
572 fn constructed(this: &Self) {
573 Self::parent_constructed(this);
574 this.set_tool_kind(InkToolbarTool::CustomPen);
575 }
576}
577impl InkToolbarPenButtonImpl for InkToolbarCustomPenButton {
578 fn create_ink_drawing_attributes(this: &Self) -> Result<InkDrawingAttributes, InkError> {
579 match this.custom_pen() {
580 Some(pen) => pen.create_ink_drawing_attributes(
581 this.selected_brush(),
582 this.determine_stroke_width(),
583 ),
584 None => {
585 let width = this.determine_stroke_width();
586 Ok(InkDrawingAttributes {
587 color: this.selected_color()?,
588 size: Size::new(width, width),
589 ..InkDrawingAttributes::default()
590 })
591 }
592 }
593 }
594}
595ferro_properties! {impl InkToolbarCustomPenButton{
596 pub fn custom_pen_property()->StyledProperty<Option<Ref<InkToolbarCustomPen>>>{FerroProperty::register::<InkToolbarCustomPenButton,_>("CustomPen",None)}
597 pub fn configuration_content_property()->StyledProperty<Option<Ref<Control>>>{FerroProperty::register::<InkToolbarCustomPenButton,_>("ConfigurationContent",None)}
598}}
599impl InkToolbarCustomPenButton {
600 fn static_constructor() {
601 override_stroke_widths::<InkToolbarCustomPenButton>(1.0, 10.0, 5.0);
604 }
605 pub fn construct() -> Self {
606 Self {
607 base: InkToolbarPenButton::construct(),
608 }
609 }
610 pub fn new() -> Ref<Self> {
611 instantiate(Self::construct())
612 }
613 pub fn custom_pen(&self) -> Option<Ref<InkToolbarCustomPen>> {
614 self.get_value(Self::custom_pen_property())
615 }
616 pub fn set_custom_pen(&self, value: Option<Ref<InkToolbarCustomPen>>) {
617 self.set_value(Self::custom_pen_property(), value);
618 }
619 pub fn configuration_content(&self) -> Option<Ref<Control>> {
620 self.get_value(Self::configuration_content_property())
621 }
622 pub fn set_configuration_content(&self, value: Option<Ref<Control>>) {
623 self.set_value(Self::configuration_content_property(), value);
624 }
625}
626
627#[repr(C)]
628pub struct InkToolbarEraserButton {
629 base: InkToolbarToolButton,
630}
631ferro_class!(InkToolbarEraserButton:InkToolbarToolButton);
632ferro_class_info!(InkToolbarEraserButton {
633 new: InkToolbarEraserButton::new
634});
635ferro_impl_classes!(InkToolbarEraserButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
636impl FerroObjectImpl for InkToolbarEraserButton {
637 fn constructed(this: &Self) {
638 Self::parent_constructed(this);
639 this.set_tool_kind(InkToolbarTool::Eraser);
640 strings::bind_content(this, "InkToolbarEraserButtonName");
641 }
642 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
643 Self::parent_on_property_changed(this, change);
644 if change.property() == Self::is_clear_all_visible_property().as_property() {
645 this.set_is_extension_glyph_shown(this.is_clear_all_visible());
646 }
647 }
648}
649impl InkToolbarEraserButton {
650 pub fn construct() -> Self {
651 Self {
652 base: InkToolbarToolButton::construct(),
653 }
654 }
655 pub fn new() -> Ref<Self> {
656 instantiate(Self::construct())
657 }
658}
659#[repr(C)]
660pub struct InkToolbarCustomToolButton {
661 base: InkToolbarToolButton,
662}
663ferro_class!(InkToolbarCustomToolButton:InkToolbarToolButton);
664ferro_class_info!(InkToolbarCustomToolButton {
665 new: InkToolbarCustomToolButton::new
666});
667ferro_impl_classes!(InkToolbarCustomToolButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
668impl FerroObjectImpl for InkToolbarCustomToolButton {
669 fn constructed(this: &Self) {
670 Self::parent_constructed(this);
671 this.set_tool_kind(InkToolbarTool::CustomTool);
672 }
673}
674impl InkToolbarCustomToolButton {
675 pub fn construct() -> Self {
676 Self {
677 base: InkToolbarToolButton::construct(),
678 }
679 }
680 pub fn new() -> Ref<Self> {
681 instantiate(Self::construct())
682 }
683}
684fn override_stroke_widths<T: ferroui_base::ObjectType>(minimum: f64, maximum: f64, selected: f64) {
685 InkToolbarPenButton::min_stroke_width_property().override_default_value::<T>(minimum);
686 InkToolbarPenButton::max_stroke_width_property().override_default_value::<T>(maximum);
687 InkToolbarPenButton::selected_stroke_width_property().override_default_value::<T>(selected);
688}
689
690pub(crate) fn affects_pen_attributes(property: &FerroProperty) -> bool {
694 property == InkToolbarPenButton::selected_brush_property().as_property()
695 || property == InkToolbarPenButton::selected_brush_index_property().as_property()
696 || property == InkToolbarPenButton::selected_stroke_width_property().as_property()
697 || property == InkToolbarPenButton::min_stroke_width_property().as_property()
698 || property == InkToolbarPenButton::max_stroke_width_property().as_property()
699 || property == InkToolbarPenButton::palette_property().as_property()
700 || property == InkToolbarCustomPenButton::custom_pen_property().as_property()
701}
702
703ferro_properties! {impl InkToolbarEraserButton{pub fn is_clear_all_visible_property()->StyledProperty<bool>{FerroProperty::register::<InkToolbarEraserButton,_>("IsClearAllVisible",true)}}}
704impl InkToolbarEraserButton {
705 pub fn is_clear_all_visible(&self) -> bool {
706 self.get_value(Self::is_clear_all_visible_property())
707 }
708 pub fn set_is_clear_all_visible(&self, value: bool) {
709 self.set_value(Self::is_clear_all_visible_property(), value);
710 }
711}
712ferro_properties! {impl InkToolbarCustomToolButton{pub fn configuration_content_property()->StyledProperty<Option<Ref<Control>>>{FerroProperty::register::<InkToolbarCustomToolButton,_>("ConfigurationContent",None)}}}
713impl InkToolbarCustomToolButton {
714 pub fn configuration_content(&self) -> Option<Ref<Control>> {
715 self.get_value(Self::configuration_content_property())
716 }
717 pub fn set_configuration_content(&self, value: Option<Ref<Control>>) {
718 self.set_value(Self::configuration_content_property(), value);
719 }
720}
721
722#[repr(C)]
723pub struct InkToolbarToggleButton {
724 base: CheckBox,
725 toggle_kind: Cell<InkToolbarToggle>,
726}
727ferro_class!(InkToolbarToggleButton:CheckBox);
728ferro_class_info!(InkToolbarToggleButton{new:InkToolbarToggleButton::new,markup:{properties:[ToggleKind:InkToolbarToggle{get:InkToolbarToggleButton::toggle_kind}],}});
729ferro_impl_classes!(InkToolbarToggleButton:FerroObjectImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
730impl StyledElementImpl for InkToolbarToggleButton {
731 fn style_key_override(this: &Self) -> &'static TypeInfo {
732 toolbar_style_key(this, CheckBox::TYPE)
733 }
734}
735impl InkToolbarToggleButton {
736 pub fn construct() -> Self {
737 Self {
738 base: CheckBox::construct(),
739 toggle_kind: Cell::new(InkToolbarToggle::Custom),
740 }
741 }
742 pub fn new() -> Ref<Self> {
743 instantiate(Self::construct())
744 }
745 pub fn toggle_kind(&self) -> InkToolbarToggle {
746 self.toggle_kind.get()
747 }
748}
749#[repr(C)]
750pub struct InkToolbarCustomToggleButton {
751 base: InkToolbarToggleButton,
752}
753ferro_class!(InkToolbarCustomToggleButton:InkToolbarToggleButton);
754ferro_class_info!(InkToolbarCustomToggleButton {
755 new: InkToolbarCustomToggleButton::new
756});
757ferro_impl_classes!(InkToolbarCustomToggleButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
758impl FerroObjectImpl for InkToolbarCustomToggleButton {
759 fn constructed(this: &Self) {
760 Self::parent_constructed(this);
761 this.toggle_kind.set(InkToolbarToggle::Custom);
762 }
763}
764impl InkToolbarCustomToggleButton {
765 pub fn construct() -> Self {
766 Self {
767 base: InkToolbarToggleButton::construct(),
768 }
769 }
770 pub fn new() -> Ref<Self> {
771 instantiate(Self::construct())
772 }
773}
774#[repr(C)]
775pub struct InkToolbarRulerButton {
776 base: InkToolbarToggleButton,
777}
778ferro_class!(InkToolbarRulerButton:InkToolbarToggleButton);
779ferro_class_info!(InkToolbarRulerButton {
780 new: InkToolbarRulerButton::new
781});
782ferro_impl_classes!(InkToolbarRulerButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,InputElementImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl,ButtonImpl,ToggleButtonImpl);
783impl FerroObjectImpl for InkToolbarRulerButton {
784 fn constructed(this: &Self) {
785 Self::parent_constructed(this);
786 this.toggle_kind.set(InkToolbarToggle::Ruler);
787 strings::bind_content(this, "InkToolbarStencilRulerName");
788 }
789}
790impl InkToolbarRulerButton {
791 pub fn construct() -> Self {
792 Self {
793 base: InkToolbarToggleButton::construct(),
794 }
795 }
796 pub fn new() -> Ref<Self> {
797 instantiate(Self::construct())
798 }
799}
800#[repr(C)]
801pub struct InkToolbarMenuButton {
802 base: ToggleButton,
803}
804ferro_class!(InkToolbarMenuButton:ToggleButton);
805ferro_class_info!(InkToolbarMenuButton{new:InkToolbarMenuButton::new,markup:{properties:[MenuKind:InkToolbarMenuKind{get:InkToolbarMenuButton::menu_kind}],}});
806ferro_impl_classes!(InkToolbarMenuButton: FerroObjectImpl, VisualImpl, LayoutableImpl, InteractiveImpl, InputElementImpl, TemplatedControlImpl, ContentControlImpl, ButtonImpl, ToggleButtonImpl);
807impl ControlImpl for InkToolbarMenuButton {
808 fn on_create_automation_peer(this: &Self) -> Ref<ferroui_controls::automation::peers::AutomationPeer> {
809 super::InkToolbarMenuButtonAutomationPeer::new(this).upcast()
810 }
811}
812
813impl StyledElementImpl for InkToolbarMenuButton {
814 fn style_key_override(this: &Self) -> &'static TypeInfo {
815 toolbar_style_key(this, ToggleButton::TYPE)
816 }
817}
818ferro_properties! {impl InkToolbarMenuButton{pub fn is_extension_glyph_shown_property()->StyledProperty<bool>{FerroProperty::register::<InkToolbarMenuButton,_>("IsExtensionGlyphShown",true)}}}
819impl InkToolbarMenuButton {
820 pub fn construct() -> Self {
821 Self {
822 base: ToggleButton::construct(),
823 }
824 }
825 pub fn new() -> Ref<Self> {
826 instantiate(Self::construct())
827 }
828 pub fn menu_kind(&self) -> InkToolbarMenuKind {
829 InkToolbarMenuKind::Stencil
830 }
831 pub fn is_extension_glyph_shown(&self) -> bool {
832 self.get_value(Self::is_extension_glyph_shown_property())
833 }
834 pub fn set_is_extension_glyph_shown(&self, value: bool) {
835 self.set_value(Self::is_extension_glyph_shown_property(), value);
836 }
837}
838
839#[repr(C)]
840pub struct InkToolbarStencilButton {
841 base: InkToolbarMenuButton,
842 presenter: RefCell<Option<Rc<super::InkPresenter>>>,
843 ruler: RefCell<Option<Rc<super::InkPresenterRuler>>>,
844 protractor: RefCell<Option<Rc<super::InkPresenterProtractor>>>,
845 menu_items: RefCell<Vec<(InkToolbarStencilKind, Ref<InkToolbarFlyoutItem>)>>,
846 syncing_menu: Cell<bool>,
847 permit_flyout: Cell<bool>,
848}
849ferro_class!(InkToolbarStencilButton:InkToolbarMenuButton);
850ferro_class_info!(InkToolbarStencilButton {
851 new: InkToolbarStencilButton::new
852});
853ferro_impl_classes!(InkToolbarStencilButton:StyledElementImpl,VisualImpl,LayoutableImpl,InteractiveImpl,ControlImpl,TemplatedControlImpl,ContentControlImpl);
854impl InputElementImpl for InkToolbarStencilButton {
855 fn on_right_tapped(this: &Self, event: &TappedEventArgs) {
856 Self::parent_on_right_tapped(this, event);
857 if this.is_effectively_enabled() {
858 this.activate_secondary();
859 event.set_handled(true);
860 }
861 }
862 fn on_holding(this: &Self, event: &HoldingRoutedEventArgs) {
863 Self::parent_on_holding(this, event);
864 if this.is_effectively_enabled() && event.holding_state() == HoldingState::Started {
865 this.activate_secondary();
866 event.set_handled(true);
867 }
868 }
869}
870impl ToggleButtonImpl for InkToolbarStencilButton {
871 fn toggle(this: &Self) {
872 let checked = if this.is_ruler_item_visible() && this.is_protractor_item_visible() {
875 true
876 } else {
877 this.is_checked() != Some(true)
878 };
879 this.set_current_value(ToggleButton::is_checked_property(), Some(checked));
880 }
881}
882impl ButtonImpl for InkToolbarStencilButton {
883 fn on_click(this: &Self) {
884 let previous = this.permit_flyout.replace(this.is_checked() == Some(true));
885 Self::parent_on_click(this);
886 this.permit_flyout.set(previous);
887 }
888 fn open_flyout(this: &Self) {
889 if this.is_ruler_item_visible()
890 && this.is_protractor_item_visible()
891 && this.permit_flyout.get()
892 && !toolbar_is_inking(this)
893 {
894 Self::parent_open_flyout(this);
895 }
896 }
897}
898impl FerroObjectImpl for InkToolbarStencilButton {
899 fn constructed(this: &Self) {
900 Self::parent_constructed(this);
901 strings::bind_content(this, "InkToolbarStencilButtonName");
902 this.create_stencil_flyout();
903 }
904 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
905 Self::parent_on_property_changed(this, change);
906 let property = change.property();
907 if property == Self::selected_stencil_property().as_property()
908 && !this.syncing_menu.get()
909 {
910 match this.selected_stencil() {
913 InkToolbarStencilKind::Ruler => this.set_is_ruler_item_visible(true),
914 InkToolbarStencilKind::Protractor => this.set_is_protractor_item_visible(true),
915 }
916 }
917 if property == Self::selected_stencil_property().as_property()
918 || property == ToggleButton::is_checked_property().as_property()
919 || property == Self::is_ruler_item_visible_property().as_property()
920 || property == Self::is_protractor_item_visible_property().as_property()
921 {
922 this.synchronize_stencil_menu();
923 }
924 }
925}
926ferro_properties! {impl InkToolbarStencilButton{
927 pub fn ruler_property()->DirectProperty<InkToolbarStencilButton,Option<Rc<super::InkPresenterRuler>>>{FerroProperty::register_direct::<InkToolbarStencilButton,_>("Ruler",InkToolbarStencilButton::ruler,None,None)}
928 pub fn protractor_property()->DirectProperty<InkToolbarStencilButton,Option<Rc<super::InkPresenterProtractor>>>{FerroProperty::register_direct::<InkToolbarStencilButton,_>("Protractor",InkToolbarStencilButton::protractor,None,None)}
929 pub fn selected_stencil_property()->StyledProperty<InkToolbarStencilKind>{FerroProperty::register::<InkToolbarStencilButton,_>("SelectedStencil",InkToolbarStencilKind::Ruler)}
930 pub fn is_ruler_item_visible_property()->StyledProperty<bool>{FerroProperty::register::<InkToolbarStencilButton,_>("IsRulerItemVisible",true)}
931 pub fn is_protractor_item_visible_property()->StyledProperty<bool>{FerroProperty::register::<InkToolbarStencilButton,_>("IsProtractorItemVisible",true)}
932}}
933impl InkToolbarStencilButton {
934 fn static_constructor() {
935 InputElement::is_holding_enabled_property()
936 .override_default_value::<InkToolbarStencilButton>(true);
937 }
938 fn activate_secondary(&self) {
939 if self.is_ruler_item_visible() && self.is_protractor_item_visible() {
940 self.set_current_value(ToggleButton::is_checked_property(), Some(true));
941 self.open_flyout();
942 } else {
943 self.on_click();
944 }
945 }
946 pub fn construct() -> Self {
947 Self {
948 base: InkToolbarMenuButton::construct(),
949 presenter: RefCell::new(None),
950 ruler: RefCell::new(None),
951 protractor: RefCell::new(None),
952 menu_items: RefCell::new(Vec::new()),
953 syncing_menu: Cell::new(false),
954 permit_flyout: Cell::new(true),
955 }
956 }
957 pub fn new() -> Ref<Self> {
958 instantiate(Self::construct())
959 }
960 fn create_stencil_flyout(&self) {
961 let panel = StackPanel::new();
962 let mut items = Vec::new();
963 for (kind, label) in [
964 (InkToolbarStencilKind::Ruler, "InkToolbarStencilRulerName"),
965 (
966 InkToolbarStencilKind::Protractor,
967 "InkToolbarStencilProtractorName",
968 ),
969 ] {
970 let item = InkToolbarFlyoutItem::new();
971 item.set_kind(InkToolbarFlyoutItemKind::RadioCheck);
972 item.set_name(Some(if kind == InkToolbarStencilKind::Ruler {
973 "StencilRuler".to_owned()
974 } else {
975 "StencilProtractor".to_owned()
976 }));
977 strings::bind_content(&item, label);
978 let weak = self.to_ref().downgrade();
979 item.checked(move |_| {
980 if let Some(button) = weak.upgrade() {
981 if button.syncing_menu.replace(true) {
982 return;
983 }
984 button.set_selected_stencil(kind);
985 button.set_is_checked(Some(true));
986 button.syncing_menu.set(false);
987 button.synchronize_stencil_menu();
988 }
989 });
990 let weak = self.to_ref().downgrade();
991 item.unchecked(move |_| {
992 if let Some(button) = weak.upgrade() {
993 let other_checked = button
997 .menu_items
998 .borrow()
999 .iter()
1000 .any(|(other, item)| *other != kind && item.is_checked() == Some(true));
1001 if other_checked {
1002 return;
1003 }
1004 if button.syncing_menu.replace(true) {
1005 return;
1006 }
1007 if button.selected_stencil() == kind {
1008 button.set_is_checked(Some(false));
1009 }
1010 button.syncing_menu.set(false);
1011 button.synchronize_stencil_menu();
1012 }
1013 });
1014 let weak = self.to_ref().downgrade();
1015 item.add_handler(Button::click_event(), move |_, _| {
1016 if let Some(button) = weak.upgrade() {
1017 if let Some(flyout) = button.flyout() {
1018 flyout.hide();
1019 }
1020 }
1021 });
1022 panel.children().add(&item);
1023 items.push((kind, item));
1024 }
1025 *self.menu_items.borrow_mut() = items;
1026 let flyout = toolbar_flyout(self);
1027 flyout.set_content(Some(Rc::new(panel.upcast::<Control>())));
1028 self.set_flyout(flyout);
1029 self.synchronize_stencil_menu();
1030 }
1031 fn synchronize_stencil_menu(&self) {
1032 if self.syncing_menu.replace(true) {
1033 return;
1034 }
1035 let items = self.menu_items.borrow().clone();
1036 let ruler = self.is_ruler_item_visible();
1037 let protractor = self.is_protractor_item_visible();
1038 if ruler != protractor {
1039 self.set_current_value(
1040 Self::selected_stencil_property(),
1041 if ruler {
1042 InkToolbarStencilKind::Ruler
1043 } else {
1044 InkToolbarStencilKind::Protractor
1045 },
1046 );
1047 }
1048 self.set_current_value(
1049 InkToolbarMenuButton::is_extension_glyph_shown_property(),
1050 ruler && protractor,
1051 );
1052 let size = i32::from(ruler) + i32::from(protractor);
1053 let mut position = 0;
1054 for (kind, item) in items {
1055 let visible = if kind == InkToolbarStencilKind::Ruler {
1056 ruler
1057 } else {
1058 protractor
1059 };
1060 item.set_is_visible(visible);
1061 item.set_is_checked(Some(
1062 self.selected_stencil() == kind && self.is_checked() == Some(true),
1063 ));
1064 if visible {
1065 position += 1;
1066 }
1067 AutomationProperties::set_position_in_set(&item, if visible { position } else { 0 });
1068 AutomationProperties::set_size_of_set(&item, size);
1069 }
1070 self.syncing_menu.set(false);
1071 }
1072 pub(crate) fn set_presenter(&self, value: Option<Rc<super::InkPresenter>>) {
1073 let ruler = value.as_ref().map(|p| p.ruler());
1074 let protractor = value.as_ref().map(|p| p.protractor());
1075 *self.presenter.borrow_mut() = value;
1076 self.set_and_raise(Self::ruler_property(), &self.ruler, ruler);
1077 self.set_and_raise(Self::protractor_property(), &self.protractor, protractor);
1078 }
1079 pub fn ruler(&self) -> Option<Rc<super::InkPresenterRuler>> {
1080 self.ruler.borrow().clone()
1081 }
1082 pub fn protractor(&self) -> Option<Rc<super::InkPresenterProtractor>> {
1083 self.protractor.borrow().clone()
1084 }
1085 pub fn selected_stencil(&self) -> InkToolbarStencilKind {
1086 self.get_value(Self::selected_stencil_property())
1087 }
1088 pub fn set_selected_stencil(&self, value: InkToolbarStencilKind) {
1089 self.set_value(Self::selected_stencil_property(), value);
1090 }
1091 pub fn is_ruler_item_visible(&self) -> bool {
1092 self.get_value(Self::is_ruler_item_visible_property())
1093 }
1094 pub fn set_is_ruler_item_visible(&self, value: bool) {
1095 self.set_value(Self::is_ruler_item_visible_property(), value);
1096 }
1097 pub fn is_protractor_item_visible(&self) -> bool {
1098 self.get_value(Self::is_protractor_item_visible_property())
1099 }
1100 pub fn set_is_protractor_item_visible(&self, value: bool) {
1101 self.set_value(Self::is_protractor_item_visible_property(), value);
1102 }
1103}
1104
1105pub(crate) fn toolbar_flyout(owner: &Control) -> Ref<Flyout> {
1109 let flyout = Flyout::new();
1110 let weak_owner = owner.to_ref().downgrade();
1111 let weak_flyout = flyout.downgrade();
1112 let _ = flyout.opening(move |_| {
1113 let (Some(owner), Some(flyout)) = (weak_owner.upgrade(), weak_flyout.upgrade()) else {
1114 return;
1115 };
1116 let theme = owner
1117 .try_find_resource(&ResourceKey::from("InkToolbarFlyoutStyle"), None)
1118 .flatten();
1119 if let Some(theme) = ferroui_base::metadata::from_markup_value::<Ref<ControlTheme>>(&theme) {
1120 flyout.set_flyout_presenter_theme(theme);
1121 }
1122 });
1123 flyout
1124}
1125
1126pub(crate) fn toolbar_is_inking(control: &Control) -> bool {
1127 let mut parent = control.parent();
1128 while let Some(element) = parent {
1129 if let Some(toolbar) = element.cast::<InkToolbar>() {
1130 return toolbar
1131 .effective_ink_presenter()
1132 .is_some_and(|presenter| presenter.is_inking());
1133 }
1134 parent = element.parent();
1135 }
1136 false
1137}
1138
1139pub(crate) fn toolbar_style_key(
1142 this: &StyledElement,
1143 fallback: &'static TypeInfo,
1144) -> &'static TypeInfo {
1145 let actual = this.get_type();
1146 if this.theme().is_some() {
1147 return actual;
1148 }
1149 let mut candidate = Some(actual);
1150 while let Some(kind) = candidate {
1151 if kind == fallback {
1152 break;
1153 }
1154 if this
1155 .try_find_resource(&ResourceKey::Type(kind), None)
1156 .flatten()
1157 .is_some_and(|resource| resource.downcast_ref::<Ref<ControlTheme>>().is_some())
1158 {
1159 return kind;
1160 }
1161 candidate = kind.base_type();
1162 }
1163 fallback
1164}