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