StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.
| 1 | use super::{ |
| 2 | AfterLayoutContext, AppContext, Element, EventContext, Fill, LayoutContext, PaintContext, |
| 3 | Point, SizeConstraint, |
| 4 | }; |
| 5 | use crate::event::DispatchedEvent; |
| 6 | pub use crate::scene::Border; |
| 7 | pub use crate::scene::{CornerRadius, DropShadow}; |
| 8 | use pathfinder_color::ColorU; |
| 9 | use pathfinder_geometry::{ |
| 10 | rect::RectF, |
| 11 | vector::{vec2f, Vector2F}, |
| 12 | }; |
| 13 | pub struct Rect { |
| 14 | background: Fill, |
| 15 | drop_shadow: Option<DropShadow>, |
| 16 | border: Border, |
| 17 | corner_radius: CornerRadius, |
| 18 | size: Option<Vector2F>, |
| 19 | origin: Option<Point>, |
| 20 | #[cfg(debug_assertions)] |
| 21 | /// Custom panic location, set with [`Rect::set_location_for_panic_logging`] |
| 22 | constructor_location: Option<&'static std::panic::Location<'static>>, |
| 23 | } |
| 24 | |
| 25 | impl Default for Rect { |
| 26 | fn default() -> Self { |
| 27 | Self::new() |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | impl Rect { |
| 32 | #[cfg_attr(debug_assertions, track_caller)] |
| 33 | pub fn new() -> Self { |
| 34 | Self { |
| 35 | drop_shadow: None, |
| 36 | background: Fill::None, |
| 37 | border: Border::default(), |
| 38 | corner_radius: CornerRadius::default(), |
| 39 | size: None, |
| 40 | origin: None, |
| 41 | #[cfg(debug_assertions)] |
| 42 | constructor_location: Some(std::panic::Location::caller()), |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | pub fn with_corner_radius(mut self, radius: CornerRadius) -> Self { |
| 47 | self.corner_radius = radius; |
| 48 | self |
| 49 | } |
| 50 | |
| 51 | pub fn with_background<F>(mut self, fill: F) -> Self |
| 52 | where |
| 53 | F: Into<Fill>, |
| 54 | { |
| 55 | self.background = fill.into(); |
| 56 | self |
| 57 | } |
| 58 | |
| 59 | pub fn with_background_color(mut self, color: ColorU) -> Self { |
| 60 | self.background = Fill::Solid(color); |
| 61 | self |
| 62 | } |
| 63 | |
| 64 | pub fn with_drop_shadow(mut self, drop_shadow: DropShadow) -> Self { |
| 65 | self.drop_shadow = Some(drop_shadow); |
| 66 | self |
| 67 | } |
| 68 | |
| 69 | pub fn with_horizontal_background_gradient( |
| 70 | mut self, |
| 71 | start_color: ColorU, |
| 72 | end_color: ColorU, |
| 73 | ) -> Self { |
| 74 | self.background = Fill::Gradient { |
| 75 | start: vec2f(0.0, 0.0), |
| 76 | end: vec2f(1.0, 0.0), |
| 77 | start_color, |
| 78 | end_color, |
| 79 | }; |
| 80 | self |
| 81 | } |
| 82 | |
| 83 | pub fn with_background_gradient( |
| 84 | mut self, |
| 85 | start: Vector2F, |
| 86 | end: Vector2F, |
| 87 | start_color: ColorU, |
| 88 | end_color: ColorU, |
| 89 | ) -> Self { |
| 90 | self.background = Fill::Gradient { |
| 91 | start, |
| 92 | end, |
| 93 | start_color, |
| 94 | end_color, |
| 95 | }; |
| 96 | self |
| 97 | } |
| 98 | |
| 99 | pub fn with_border(mut self, border: Border) -> Self { |
| 100 | self.border = border; |
| 101 | self |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | impl Element for Rect { |
| 106 | fn layout( |
| 107 | &mut self, |
| 108 | constraint: SizeConstraint, |
| 109 | _ctx: &mut LayoutContext, |
| 110 | _app: &AppContext, |
| 111 | ) -> Vector2F { |
| 112 | let size = constraint.max; |
| 113 | self.size = Some(size); |
| 114 | size |
| 115 | } |
| 116 | |
| 117 | fn after_layout(&mut self, _ctx: &mut AfterLayoutContext, _app: &AppContext) {} |
| 118 | |
| 119 | fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, _app: &AppContext) { |
| 120 | self.origin = Some(Point::from_vec2f(origin, ctx.scene.z_index())); |
| 121 | let size = self.size.unwrap(); |
| 122 | |
| 123 | #[cfg(debug_assertions)] |
| 124 | ctx.scene |
| 125 | .set_location_for_panic_logging(self.constructor_location); |
| 126 | |
| 127 | let rect = ctx |
| 128 | .scene |
| 129 | .draw_rect_with_hit_recording(RectF::new(origin, size)) |
| 130 | .with_background(self.background) |
| 131 | .with_border(self.border) |
| 132 | .with_corner_radius(self.corner_radius); |
| 133 | if let Some(drop_shadow) = self.drop_shadow { |
| 134 | rect.with_drop_shadow(drop_shadow); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | fn dispatch_event( |
| 139 | &mut self, |
| 140 | _event: &DispatchedEvent, |
| 141 | _ctx: &mut EventContext, |
| 142 | _app: &AppContext, |
| 143 | ) -> bool { |
| 144 | false |
| 145 | } |
| 146 | |
| 147 | fn size(&self) -> Option<Vector2F> { |
| 148 | self.size |
| 149 | } |
| 150 | |
| 151 | fn origin(&self) -> Option<Point> { |
| 152 | self.origin |
| 153 | } |
| 154 | |
| 155 | #[cfg_attr(debug_assertions, track_caller)] |
| 156 | fn finish(mut self) -> Box<dyn Element> |
| 157 | where |
| 158 | Self: 'static + Sized, |
| 159 | { |
| 160 | #[cfg(debug_assertions)] |
| 161 | { |
| 162 | self.constructor_location = Some(std::panic::Location::caller()); |
| 163 | } |
| 164 | Box::new(self) |
| 165 | } |
| 166 | } |
| 167 |