Skip to main content

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, InternalKeyEvent,
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 deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
49
50    fn layout_info(
51        self: Pin<&Self>,
52        orientation: Orientation,
53        cross_axis_constraint: Coord,
54        _window_adapter: &Rc<dyn WindowAdapter>,
55        _self_rc: &ItemRc,
56    ) -> LayoutInfo {
57        let natural_size = self.source().size();
58        LayoutInfo {
59            preferred: match orientation {
60                _ if natural_size.width == 0 || natural_size.height == 0 => 0 as Coord,
61                Orientation::Horizontal => natural_size.width as Coord,
62                Orientation::Vertical => {
63                    let w = if cross_axis_constraint >= 0 as Coord {
64                        cross_axis_constraint
65                    } else {
66                        self.width().get()
67                    };
68                    natural_size.height as Coord * w / natural_size.width as Coord
69                }
70            },
71            // The compiler's single-cell box layout lowering relies on image items
72            // keeping the default stretch of 0 in their layout info.
73            ..Default::default()
74        }
75    }
76
77    fn input_event_filter_before_children(
78        self: Pin<&Self>,
79        _: &MouseEvent,
80        _window_adapter: &Rc<dyn WindowAdapter>,
81        _self_rc: &ItemRc,
82        _: &mut super::MouseCursorInner,
83    ) -> InputEventFilterResult {
84        InputEventFilterResult::ForwardAndIgnore
85    }
86
87    fn input_event(
88        self: Pin<&Self>,
89        _: &MouseEvent,
90        _window_adapter: &Rc<dyn WindowAdapter>,
91        _self_rc: &ItemRc,
92        _: &mut super::MouseCursorInner,
93    ) -> InputEventResult {
94        InputEventResult::EventIgnored
95    }
96
97    fn capture_key_event(
98        self: Pin<&Self>,
99        _: &InternalKeyEvent,
100        _window_adapter: &Rc<dyn WindowAdapter>,
101        _self_rc: &ItemRc,
102    ) -> KeyEventResult {
103        KeyEventResult::EventIgnored
104    }
105
106    fn key_event(
107        self: Pin<&Self>,
108        _: &InternalKeyEvent,
109        _window_adapter: &Rc<dyn WindowAdapter>,
110        _self_rc: &ItemRc,
111    ) -> KeyEventResult {
112        KeyEventResult::EventIgnored
113    }
114
115    fn focus_event(
116        self: Pin<&Self>,
117        _: &FocusEvent,
118        _window_adapter: &Rc<dyn WindowAdapter>,
119        _self_rc: &ItemRc,
120    ) -> FocusEventResult {
121        FocusEventResult::FocusIgnored
122    }
123
124    fn render(
125        self: Pin<&Self>,
126        backend: &mut &mut dyn ItemRenderer,
127        self_rc: &ItemRc,
128        size: LogicalSize,
129    ) -> RenderingResult {
130        (*backend).draw_image(self, self_rc, size, &self.cached_rendering_data);
131        RenderingResult::ContinueRenderingChildren
132    }
133
134    fn bounding_rect(
135        self: core::pin::Pin<&Self>,
136        _window_adapter: &Rc<dyn WindowAdapter>,
137        _self_rc: &ItemRc,
138        geometry: LogicalRect,
139    ) -> LogicalRect {
140        geometry
141    }
142
143    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
144        false
145    }
146}
147
148impl RenderImage for ImageItem {
149    fn target_size(self: Pin<&Self>) -> LogicalSize {
150        LogicalSize::from_lengths(self.width(), self.height())
151    }
152
153    fn source(self: Pin<&Self>) -> crate::graphics::Image {
154        self.source()
155    }
156
157    fn source_clip(self: Pin<&Self>) -> Option<crate::graphics::IntRect> {
158        None
159    }
160
161    fn image_fit(self: Pin<&Self>) -> ImageFit {
162        self.image_fit()
163    }
164
165    fn rendering(self: Pin<&Self>) -> ImageRendering {
166        self.image_rendering()
167    }
168
169    fn colorize(self: Pin<&Self>) -> Brush {
170        self.colorize()
171    }
172
173    fn alignment(self: Pin<&Self>) -> (ImageHorizontalAlignment, ImageVerticalAlignment) {
174        Default::default()
175    }
176
177    fn tiling(self: Pin<&Self>) -> (ImageTiling, ImageTiling) {
178        Default::default()
179    }
180}
181
182impl ItemConsts for ImageItem {
183    const cached_rendering_data_offset: const_field_offset::FieldOffset<
184        ImageItem,
185        CachedRenderingData,
186    > = ImageItem::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
187}
188
189#[repr(C)]
190#[derive(FieldOffsets, Default, SlintElement)]
191#[pin]
192/// The implementation of the `ClippedImage` element
193pub struct ClippedImage {
194    pub source: Property<crate::graphics::Image>,
195    pub width: Property<LogicalLength>,
196    pub height: Property<LogicalLength>,
197    pub image_fit: Property<ImageFit>,
198    pub image_rendering: Property<ImageRendering>,
199    pub colorize: Property<Brush>,
200    pub source_clip_x: Property<i32>,
201    pub source_clip_y: Property<i32>,
202    pub source_clip_width: Property<i32>,
203    pub source_clip_height: Property<i32>,
204
205    pub horizontal_alignment: Property<ImageHorizontalAlignment>,
206    pub vertical_alignment: Property<ImageVerticalAlignment>,
207    pub horizontal_tiling: Property<ImageTiling>,
208    pub vertical_tiling: Property<ImageTiling>,
209
210    pub cached_rendering_data: CachedRenderingData,
211}
212
213impl Item for ClippedImage {
214    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
215
216    fn deinit(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
217
218    fn layout_info(
219        self: Pin<&Self>,
220        orientation: Orientation,
221        cross_axis_constraint: Coord,
222        _window_adapter: &Rc<dyn WindowAdapter>,
223        _self_rc: &ItemRc,
224    ) -> LayoutInfo {
225        LayoutInfo {
226            preferred: match orientation {
227                Orientation::Horizontal => self.source_clip_width() as Coord,
228                Orientation::Vertical => {
229                    let source_clip_width = self.source_clip_width();
230                    if source_clip_width == 0 {
231                        0 as Coord
232                    } else {
233                        let w = if cross_axis_constraint >= 0 as Coord {
234                            cross_axis_constraint
235                        } else {
236                            self.width().get()
237                        };
238                        self.source_clip_height() as Coord * w / source_clip_width as Coord
239                    }
240                }
241            },
242            ..Default::default()
243        }
244    }
245
246    fn input_event_filter_before_children(
247        self: Pin<&Self>,
248        _: &MouseEvent,
249        _window_adapter: &Rc<dyn WindowAdapter>,
250        _self_rc: &ItemRc,
251        _: &mut super::MouseCursorInner,
252    ) -> InputEventFilterResult {
253        InputEventFilterResult::ForwardAndIgnore
254    }
255
256    fn input_event(
257        self: Pin<&Self>,
258        _: &MouseEvent,
259        _window_adapter: &Rc<dyn WindowAdapter>,
260        _self_rc: &ItemRc,
261        _: &mut super::MouseCursorInner,
262    ) -> InputEventResult {
263        InputEventResult::EventIgnored
264    }
265
266    fn capture_key_event(
267        self: Pin<&Self>,
268        _: &InternalKeyEvent,
269        _window_adapter: &Rc<dyn WindowAdapter>,
270        _self_rc: &ItemRc,
271    ) -> KeyEventResult {
272        KeyEventResult::EventIgnored
273    }
274
275    fn key_event(
276        self: Pin<&Self>,
277        _: &InternalKeyEvent,
278        _window_adapter: &Rc<dyn WindowAdapter>,
279        _self_rc: &ItemRc,
280    ) -> KeyEventResult {
281        KeyEventResult::EventIgnored
282    }
283
284    fn focus_event(
285        self: Pin<&Self>,
286        _: &FocusEvent,
287        _window_adapter: &Rc<dyn WindowAdapter>,
288        _self_rc: &ItemRc,
289    ) -> FocusEventResult {
290        FocusEventResult::FocusIgnored
291    }
292
293    fn render(
294        self: Pin<&Self>,
295        backend: &mut &mut dyn ItemRenderer,
296        self_rc: &ItemRc,
297        size: LogicalSize,
298    ) -> RenderingResult {
299        (*backend).draw_image(self, self_rc, size, &self.cached_rendering_data);
300        RenderingResult::ContinueRenderingChildren
301    }
302
303    fn bounding_rect(
304        self: core::pin::Pin<&Self>,
305        _window_adapter: &Rc<dyn WindowAdapter>,
306        _self_rc: &ItemRc,
307        geometry: LogicalRect,
308    ) -> LogicalRect {
309        geometry
310    }
311
312    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
313        false
314    }
315}
316
317impl RenderImage for ClippedImage {
318    fn target_size(self: Pin<&Self>) -> LogicalSize {
319        LogicalSize::from_lengths(self.width(), self.height())
320    }
321
322    fn source(self: Pin<&Self>) -> crate::graphics::Image {
323        self.source()
324    }
325
326    fn source_clip(self: Pin<&Self>) -> Option<crate::graphics::IntRect> {
327        Some(euclid::rect(
328            self.source_clip_x(),
329            self.source_clip_y(),
330            self.source_clip_width(),
331            self.source_clip_height(),
332        ))
333    }
334
335    fn image_fit(self: Pin<&Self>) -> ImageFit {
336        self.image_fit()
337    }
338
339    fn rendering(self: Pin<&Self>) -> ImageRendering {
340        self.image_rendering()
341    }
342
343    fn colorize(self: Pin<&Self>) -> Brush {
344        self.colorize()
345    }
346
347    fn alignment(self: Pin<&Self>) -> (ImageHorizontalAlignment, ImageVerticalAlignment) {
348        (self.horizontal_alignment(), self.vertical_alignment())
349    }
350
351    fn tiling(self: Pin<&Self>) -> (ImageTiling, ImageTiling) {
352        (self.horizontal_tiling(), self.vertical_tiling())
353    }
354}
355
356impl ItemConsts for ClippedImage {
357    const cached_rendering_data_offset: const_field_offset::FieldOffset<
358        ClippedImage,
359        CachedRenderingData,
360    > = ClippedImage::FIELD_OFFSETS.cached_rendering_data().as_unpinned_projection();
361}