1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Copyright (c) 2022-2025 R3BL LLC. Licensed under Apache License, Version 2.0.
use super::{ComponentRegistryMap, EventPropagation, GlobalData, HasFocus};
use crate::{CommonResult, FlexBox, FlexBoxId, InputEvent, RenderPipeline, Surface,
SurfaceBounds};
use std::fmt::Debug;
/// See [`crate::App`].
pub trait Component<S, AS>
where
S: Debug + Default + Clone + Sync + Send,
AS: Debug + Default + Clone + Sync + Send,
{
/// This is an optional method that can be used to initialize the state of the
/// component's engines. This applies to modal dialog components that need their
/// engine to be initialized before they are shown / activated.
fn reset(&mut self);
fn get_id(&self) -> FlexBoxId;
/// Use the state to render the output. The state is immutable. If you want to change
/// it then it should be done in the [`Component::handle_event`] method. Here are
/// all the arguments that are passed in (which can be used to render the output):
///
/// - Arguments:
/// - Get from `current_box`:
/// - `box_origin_pos`: Position
/// - `box_bounding_size`: Size
/// - `maybe_box_style`: `Option<Style>`
/// - Get from `state`:
/// - Content to render
/// - `get_focus_id()`: String to determine if this component has keyboard focus
/// (might affect the way it gets rendered)
/// - Maybe use `shared_store`:
/// - Dispatch an action if needed
/// - Maybe use `surface`:
/// - Get the origin and size of the surface that can be drawn to (maybe different
/// than the size of the window)
///
/// - Returns:
/// - [`RenderPipeline`] which must be rendered by the caller
///
/// - Clipping, scrolling, overdrawing:
/// - Each implementation of this trait is solely responsible of taking care of
/// these behaviors
///
/// # Errors
///
/// Returns an error if the rendering operation fails.
fn render(
&mut self,
global_data: &mut GlobalData<S, AS>,
current_box: FlexBox,
surface_bounds: SurfaceBounds,
has_focus: &mut HasFocus,
) -> CommonResult<RenderPipeline>;
/// If this component has focus [`HasFocus`] then this method will be called to handle
/// input event that is meant for it.
///
/// More granularly, here is the journey:
/// 1. This method might end up calling on an underlying engine function & pass the
/// `input_event` & state (from the redux store) to it.
/// - Engines tend to have a corresponding `apply_event` method which returns a new
/// result or response type, eg: [`crate::DialogEngineApplyResponse`] or
/// [`crate::EditorEngineApplyEventResult`].
/// 2. Then the response or result is used to run a callback function that was passed
/// in when the component was created (which will then end up dispatching an action
/// to the redux store).
/// 3. Finally an [`EventPropagation`] is returned to let the caller know whether the
/// `input_event` was consumed or not & whether it should re-render (outside of a
/// redux store state change).
///
/// # Errors
///
/// Returns an error if the event handling operation fails.
fn handle_event(
&mut self,
global_data: &mut GlobalData<S, AS>,
input_event: InputEvent,
has_focus: &mut HasFocus,
) -> CommonResult<EventPropagation>;
}
pub trait SurfaceRender<S, AS>
where
S: Debug + Default + Clone + Sync + Send,
AS: Debug + Default + Clone + Sync + Send,
{
/// # Errors
///
/// Returns an error if the surface rendering operation fails.
fn render_in_surface(
&mut self,
surface: &mut Surface,
global_data: &mut GlobalData<S, AS>,
component_registry_map: &mut ComponentRegistryMap<S, AS>,
has_focus: &mut HasFocus,
) -> CommonResult<()>;
}