i_slint_core/items/
image.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4/*!
5This module contains the builtin image related items.
6
7When adding an item or a property, it needs to be kept in sync with different place.
8Lookup the [`crate::items`] module documentation.
9*/
10use super::{
11    ImageFit, ImageHorizontalAlignment, ImageRendering, ImageTiling, ImageVerticalAlignment, Item,
12    ItemConsts, ItemRc, RenderingResult,
13};
14use crate::input::{
15    FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
16    KeyEventResult, MouseEvent,
17};
18use crate::item_rendering::ItemRenderer;
19use crate::item_rendering::{CachedRenderingData, RenderImage};
20use crate::layout::{LayoutInfo, Orientation};
21use crate::lengths::{LogicalLength, LogicalRect, LogicalSize};
22#[cfg(feature = "rtti")]
23use crate::rtti::*;
24use crate::window::WindowAdapter;
25use crate::{Brush, Coord, Property};
26use alloc::rc::Rc;
27use const_field_offset::FieldOffsets;
28use core::pin::Pin;
29use i_slint_core_macros::*;
30
31#[repr(C)]
32#[derive(FieldOffsets, Default, SlintElement)]
33#[pin]
34/// The implementation of the `Image` element
35pub struct ImageItem {
36    pub source: Property<crate::graphics::Image>,
37    pub width: Property<LogicalLength>,
38    pub height: Property<LogicalLength>,
39    pub image_fit: Property<ImageFit>,
40    pub image_rendering: Property<ImageRendering>,
41    pub colorize: Property<Brush>,
42    pub cached_rendering_data: CachedRenderingData,
43}
44
45impl Item for ImageItem {
46    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
47
48    fn layout_info(
49        self: Pin<&Self>,
50        orientation: Orientation,
51        _window_adapter: &Rc<dyn WindowAdapter>,
52        _self_rc: &ItemRc,
53    ) -> LayoutInfo {
54        let natural_size = self.source().size();
55        LayoutInfo {
56            preferred: match orientation {
57                _ if natural_size.width == 0 || natural_size.height == 0 => 0 as Coord,
58                Orientation::Horizontal => natural_size.width as Coord,
59                Orientation::Vertical => {
60                    natural_size.height as Coord * self.width().get() / natural_size.width as Coord
61                }
62            },
63            ..Default::default()
64        }
65    }
66
67    fn input_event_filter_before_children(
68        self: Pin<&Self>,
69        _: MouseEvent,
70        _window_adapter: &Rc<dyn WindowAdapter>,
71        _self_rc: &ItemRc,
72    ) -> InputEventFilterResult {
73        InputEventFilterResult::ForwardAndIgnore
74    }
75
76    fn input_event(
77        self: Pin<&Self>,
78        _: MouseEvent,
79        _window_adapter: &Rc<dyn WindowAdapter>,
80        _self_rc: &ItemRc,
81    ) -> InputEventResult {
82        InputEventResult::EventIgnored
83    }
84
85    fn key_event(
86        self: Pin<&Self>,
87        _: &KeyEvent,
88        _window_adapter: &Rc<dyn WindowAdapter>,
89        _self_rc: &ItemRc,
90    ) -> KeyEventResult {
91        KeyEventResult::EventIgnored
92    }
93
94    fn focus_event(
95        self: Pin<&Self>,
96        _: &FocusEvent,
97        _window_adapter: &Rc<dyn WindowAdapter>,
98        _self_rc: &ItemRc,
99    ) -> FocusEventResult {
100        FocusEventResult::FocusIgnored
101    }
102
103    fn render(
104        self: Pin<&Self>,
105        backend: &mut &mut dyn ItemRenderer,
106        self_rc: &ItemRc,
107        size: LogicalSize,
108    ) -> RenderingResult {
109        (*backend).draw_image(self, self_rc, size, &self.cached_rendering_data);
110        RenderingResult::ContinueRenderingChildren
111    }
112
113    fn bounding_rect(
114        self: core::pin::Pin<&Self>,
115        _window_adapter: &Rc<dyn WindowAdapter>,
116        _self_rc: &ItemRc,
117        geometry: LogicalRect,
118    ) -> LogicalRect {
119        geometry
120    }
121
122    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
123        false
124    }
125}
126
127impl RenderImage for ImageItem {
128    fn target_size(self: Pin<&Self>) -> LogicalSize {
129        LogicalSize::from_lengths(self.width(), self.height())
130    }
131
132    fn source(self: Pin<&Self>) -> crate::graphics::Image {
133        self.source()
134    }
135
136    fn source_clip(self: Pin<&Self>) -> Option<crate::graphics::IntRect> {
137        None
138    }
139
140    fn image_fit(self: Pin<&Self>) -> ImageFit {
141        self.image_fit()
142    }
143
144    fn rendering(self: Pin<&Self>) -> ImageRendering {
145        self.image_rendering()
146    }
147
148    fn colorize(self: Pin<&Self>) -> Brush {
149        self.colorize()
150    }
151
152    fn alignment(self: Pin<&Self>) -> (ImageHorizontalAlignment, ImageVerticalAlignment) {
153        Default::default()
154    }
155
156    fn tiling(self: Pin<&Self>) -> (ImageTiling, ImageTiling) {
157        Default::default()
158    }
159}
160
161impl ItemConsts for ImageItem {
162    const cached_rendering_data_offset: const_field_offset::FieldOffset<
163        ImageItem,
164        CachedRenderingData,
165    > = ImageItem::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
166}
167
168#[repr(C)]
169#[derive(FieldOffsets, Default, SlintElement)]
170#[pin]
171/// The implementation of the `ClippedImage` element
172pub struct ClippedImage {
173    pub source: Property<crate::graphics::Image>,
174    pub width: Property<LogicalLength>,
175    pub height: Property<LogicalLength>,
176    pub image_fit: Property<ImageFit>,
177    pub image_rendering: Property<ImageRendering>,
178    pub colorize: Property<Brush>,
179    pub source_clip_x: Property<i32>,
180    pub source_clip_y: Property<i32>,
181    pub source_clip_width: Property<i32>,
182    pub source_clip_height: Property<i32>,
183
184    pub horizontal_alignment: Property<ImageHorizontalAlignment>,
185    pub vertical_alignment: Property<ImageVerticalAlignment>,
186    pub horizontal_tiling: Property<ImageTiling>,
187    pub vertical_tiling: Property<ImageTiling>,
188
189    pub cached_rendering_data: CachedRenderingData,
190}
191
192impl Item for ClippedImage {
193    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
194
195    fn layout_info(
196        self: Pin<&Self>,
197        orientation: Orientation,
198        _window_adapter: &Rc<dyn WindowAdapter>,
199        _self_rc: &ItemRc,
200    ) -> LayoutInfo {
201        LayoutInfo {
202            preferred: match orientation {
203                Orientation::Horizontal => self.source_clip_width() as Coord,
204                Orientation::Vertical => {
205                    let source_clip_width = self.source_clip_width();
206                    if source_clip_width == 0 {
207                        0 as Coord
208                    } else {
209                        self.source_clip_height() as Coord * self.width().get()
210                            / source_clip_width as Coord
211                    }
212                }
213            },
214            ..Default::default()
215        }
216    }
217
218    fn input_event_filter_before_children(
219        self: Pin<&Self>,
220        _: MouseEvent,
221        _window_adapter: &Rc<dyn WindowAdapter>,
222        _self_rc: &ItemRc,
223    ) -> InputEventFilterResult {
224        InputEventFilterResult::ForwardAndIgnore
225    }
226
227    fn input_event(
228        self: Pin<&Self>,
229        _: MouseEvent,
230        _window_adapter: &Rc<dyn WindowAdapter>,
231        _self_rc: &ItemRc,
232    ) -> InputEventResult {
233        InputEventResult::EventIgnored
234    }
235
236    fn key_event(
237        self: Pin<&Self>,
238        _: &KeyEvent,
239        _window_adapter: &Rc<dyn WindowAdapter>,
240        _self_rc: &ItemRc,
241    ) -> KeyEventResult {
242        KeyEventResult::EventIgnored
243    }
244
245    fn focus_event(
246        self: Pin<&Self>,
247        _: &FocusEvent,
248        _window_adapter: &Rc<dyn WindowAdapter>,
249        _self_rc: &ItemRc,
250    ) -> FocusEventResult {
251        FocusEventResult::FocusIgnored
252    }
253
254    fn render(
255        self: Pin<&Self>,
256        backend: &mut &mut dyn ItemRenderer,
257        self_rc: &ItemRc,
258        size: LogicalSize,
259    ) -> RenderingResult {
260        (*backend).draw_image(self, self_rc, size, &self.cached_rendering_data);
261        RenderingResult::ContinueRenderingChildren
262    }
263
264    fn bounding_rect(
265        self: core::pin::Pin<&Self>,
266        _window_adapter: &Rc<dyn WindowAdapter>,
267        _self_rc: &ItemRc,
268        geometry: LogicalRect,
269    ) -> LogicalRect {
270        geometry
271    }
272
273    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
274        false
275    }
276}
277
278impl RenderImage for ClippedImage {
279    fn target_size(self: Pin<&Self>) -> LogicalSize {
280        LogicalSize::from_lengths(self.width(), self.height())
281    }
282
283    fn source(self: Pin<&Self>) -> crate::graphics::Image {
284        self.source()
285    }
286
287    fn source_clip(self: Pin<&Self>) -> Option<crate::graphics::IntRect> {
288        Some(euclid::rect(
289            self.source_clip_x(),
290            self.source_clip_y(),
291            self.source_clip_width(),
292            self.source_clip_height(),
293        ))
294    }
295
296    fn image_fit(self: Pin<&Self>) -> ImageFit {
297        self.image_fit()
298    }
299
300    fn rendering(self: Pin<&Self>) -> ImageRendering {
301        self.image_rendering()
302    }
303
304    fn colorize(self: Pin<&Self>) -> Brush {
305        self.colorize()
306    }
307
308    fn alignment(self: Pin<&Self>) -> (ImageHorizontalAlignment, ImageVerticalAlignment) {
309        (self.horizontal_alignment(), self.vertical_alignment())
310    }
311
312    fn tiling(self: Pin<&Self>) -> (ImageTiling, ImageTiling) {
313        (self.horizontal_tiling(), self.vertical_tiling())
314    }
315}
316
317impl ItemConsts for ClippedImage {
318    const cached_rendering_data_offset: const_field_offset::FieldOffset<
319        ClippedImage,
320        CachedRenderingData,
321    > = ClippedImage::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
322}