fyrox_ui/
utils.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::{
22    border::BorderBuilder,
23    button::ButtonBuilder,
24    core::{algebra::Vector2, color::Color, parking_lot::Mutex, pool::Handle},
25    decorator::DecoratorBuilder,
26    formatted_text::WrapMode,
27    grid::{Column, GridBuilder, Row},
28    image::ImageBuilder,
29    style::{resource::StyleResourceExt, Style},
30    text::TextBuilder,
31    vector_image::{Primitive, VectorImageBuilder},
32    widget::WidgetBuilder,
33    Brush, BuildContext, HorizontalAlignment, RcUiNodeHandle, Thickness, UiNode, VerticalAlignment,
34};
35use fyrox_core::Uuid;
36use fyrox_texture::{
37    CompressionOptions, TextureImportOptions, TextureMinificationFilter, TextureResource,
38    TextureResourceExtension,
39};
40use std::sync::Arc;
41
42pub enum ArrowDirection {
43    Top,
44    Bottom,
45    Left,
46    Right,
47}
48
49pub fn make_arrow_primitives_non_uniform_size(
50    orientation: ArrowDirection,
51    width: f32,
52    height: f32,
53) -> Vec<Primitive> {
54    vec![match orientation {
55        ArrowDirection::Top => Primitive::Triangle {
56            points: [
57                Vector2::new(width * 0.5, 0.0),
58                Vector2::new(width, height),
59                Vector2::new(0.0, height),
60            ],
61        },
62        ArrowDirection::Bottom => Primitive::Triangle {
63            points: [
64                Vector2::new(0.0, 0.0),
65                Vector2::new(width, 0.0),
66                Vector2::new(width * 0.5, height),
67            ],
68        },
69        ArrowDirection::Right => Primitive::Triangle {
70            points: [
71                Vector2::new(0.0, 0.0),
72                Vector2::new(width, height * 0.5),
73                Vector2::new(0.0, height),
74            ],
75        },
76        ArrowDirection::Left => Primitive::Triangle {
77            points: [
78                Vector2::new(0.0, height * 0.5),
79                Vector2::new(width, 0.0),
80                Vector2::new(width, height),
81            ],
82        },
83    }]
84}
85
86pub fn make_arrow_primitives(orientation: ArrowDirection, size: f32) -> Vec<Primitive> {
87    make_arrow_primitives_non_uniform_size(orientation, size, size)
88}
89
90pub fn make_arrow_non_uniform_size(
91    ctx: &mut BuildContext,
92    orientation: ArrowDirection,
93    width: f32,
94    height: f32,
95) -> Handle<UiNode> {
96    VectorImageBuilder::new(
97        WidgetBuilder::new()
98            .with_foreground(ctx.style.property(Style::BRUSH_BRIGHT))
99            .with_width(width)
100            .with_height(height)
101            .with_horizontal_alignment(HorizontalAlignment::Center)
102            .with_vertical_alignment(VerticalAlignment::Center),
103    )
104    .with_primitives(make_arrow_primitives_non_uniform_size(
105        orientation,
106        width,
107        height,
108    ))
109    .build(ctx)
110}
111
112pub fn make_arrow(
113    ctx: &mut BuildContext,
114    orientation: ArrowDirection,
115    size: f32,
116) -> Handle<UiNode> {
117    make_arrow_non_uniform_size(ctx, orientation, size, size)
118}
119
120pub fn make_cross_primitive(size: f32, thickness: f32) -> Vec<Primitive> {
121    vec![
122        Primitive::Line {
123            begin: Vector2::new(0.0, 0.0),
124            end: Vector2::new(size, size),
125            thickness,
126        },
127        Primitive::Line {
128            begin: Vector2::new(size, 0.0),
129            end: Vector2::new(0.0, size),
130            thickness,
131        },
132    ]
133}
134
135pub fn make_cross(ctx: &mut BuildContext, size: f32, thickness: f32) -> Handle<UiNode> {
136    VectorImageBuilder::new(
137        WidgetBuilder::new()
138            .with_horizontal_alignment(HorizontalAlignment::Center)
139            .with_vertical_alignment(VerticalAlignment::Center)
140            .with_width(size)
141            .with_height(size)
142            .with_foreground(ctx.style.property(Style::BRUSH_BRIGHT)),
143    )
144    .with_primitives(make_cross_primitive(size, thickness))
145    .build(ctx)
146}
147
148pub fn make_simple_tooltip(ctx: &mut BuildContext, text: &str) -> RcUiNodeHandle {
149    let handle = BorderBuilder::new(
150        WidgetBuilder::new()
151            .with_visibility(false)
152            .with_hit_test_visibility(false)
153            .with_foreground(ctx.style.property(Style::BRUSH_DARKEST))
154            .with_background(Brush::Solid(Color::opaque(230, 230, 230)).into())
155            .with_width(300.0)
156            .with_child(
157                TextBuilder::new(
158                    WidgetBuilder::new()
159                        .with_margin(Thickness::uniform(2.0))
160                        .with_foreground(ctx.style.property(Style::BRUSH_DARKER)),
161                )
162                .with_wrap(WrapMode::Word)
163                .with_text(text)
164                .build(ctx),
165            ),
166    )
167    .build(ctx);
168    RcUiNodeHandle::new(handle, ctx.sender())
169}
170
171pub fn make_asset_preview_tooltip(
172    texture: Option<TextureResource>,
173    ctx: &mut BuildContext,
174) -> (RcUiNodeHandle, Handle<UiNode>) {
175    let size = 120.0;
176    let image_preview;
177    let image_preview_tooltip = BorderBuilder::new(
178        WidgetBuilder::new()
179            .with_visibility(false)
180            .with_hit_test_visibility(false)
181            .with_foreground(ctx.style.property(Style::BRUSH_DARKEST))
182            .with_background(Brush::Solid(Color::opaque(230, 230, 230)).into())
183            .with_width(size + 2.0)
184            .with_height(size + 2.0)
185            .with_child({
186                image_preview = ImageBuilder::new(
187                    WidgetBuilder::new()
188                        .on_column(0)
189                        .with_width(size)
190                        .with_height(size)
191                        .with_margin(Thickness::uniform(1.0)),
192                )
193                .with_checkerboard_background(true)
194                .with_opt_texture(texture)
195                .with_sync_with_texture_size(false)
196                .build(ctx);
197                image_preview
198            }),
199    )
200    .build(ctx);
201    (
202        RcUiNodeHandle::new(image_preview_tooltip, ctx.sender()),
203        image_preview,
204    )
205}
206
207pub fn make_dropdown_list_option_universal<T: Send + 'static>(
208    ctx: &mut BuildContext,
209    name: &str,
210    height: f32,
211    user_data: T,
212) -> Handle<UiNode> {
213    DecoratorBuilder::new(
214        BorderBuilder::new(
215            WidgetBuilder::new()
216                .with_height(height)
217                .with_user_data(Arc::new(Mutex::new(user_data)))
218                .with_child(
219                    TextBuilder::new(WidgetBuilder::new())
220                        .with_vertical_text_alignment(VerticalAlignment::Center)
221                        .with_horizontal_text_alignment(HorizontalAlignment::Center)
222                        .with_text(name)
223                        .build(ctx),
224                ),
225        )
226        .with_corner_radius(4.0f32.into())
227        .with_pad_by_corner_radius(false),
228    )
229    .build(ctx)
230}
231
232pub fn make_dropdown_list_option(ctx: &mut BuildContext, name: &str) -> Handle<UiNode> {
233    DecoratorBuilder::new(
234        BorderBuilder::new(
235            WidgetBuilder::new().with_child(
236                TextBuilder::new(WidgetBuilder::new())
237                    .with_vertical_text_alignment(VerticalAlignment::Center)
238                    .with_horizontal_text_alignment(HorizontalAlignment::Center)
239                    .with_text(name)
240                    .build(ctx),
241            ),
242        )
243        .with_corner_radius(4.0f32.into())
244        .with_pad_by_corner_radius(false),
245    )
246    .build(ctx)
247}
248
249pub fn make_dropdown_list_option_with_height(
250    ctx: &mut BuildContext,
251    name: &str,
252    height: f32,
253) -> Handle<UiNode> {
254    DecoratorBuilder::new(
255        BorderBuilder::new(
256            WidgetBuilder::new().with_height(height).with_child(
257                TextBuilder::new(WidgetBuilder::new())
258                    .with_vertical_text_alignment(VerticalAlignment::Center)
259                    .with_horizontal_text_alignment(HorizontalAlignment::Center)
260                    .with_text(name)
261                    .build(ctx),
262            ),
263        )
264        .with_corner_radius(4.0f32.into())
265        .with_pad_by_corner_radius(false),
266    )
267    .build(ctx)
268}
269
270pub fn make_image_button_with_tooltip(
271    ctx: &mut BuildContext,
272    width: f32,
273    height: f32,
274    image: Option<TextureResource>,
275    tooltip: &str,
276    tab_index: Option<usize>,
277) -> Handle<UiNode> {
278    ButtonBuilder::new(
279        WidgetBuilder::new()
280            .with_tab_index(tab_index)
281            .with_tooltip(make_simple_tooltip(ctx, tooltip))
282            .with_margin(Thickness::uniform(1.0)),
283    )
284    .with_content(
285        ImageBuilder::new(
286            WidgetBuilder::new()
287                .with_background(ctx.style.property(Style::BRUSH_BRIGHTEST))
288                .with_margin(Thickness::uniform(2.0))
289                .with_width(width)
290                .with_height(height),
291        )
292        .with_opt_texture(image)
293        .build(ctx),
294    )
295    .build(ctx)
296}
297
298pub fn make_text_and_image_button_with_tooltip(
299    ctx: &mut BuildContext,
300    text: &str,
301    image_width: f32,
302    image_height: f32,
303    image: Option<TextureResource>,
304    tooltip: &str,
305    row: usize,
306    column: usize,
307    tab_index: Option<usize>,
308    color: Color,
309    font_size: f32,
310) -> Handle<UiNode> {
311    let margin = 2.0;
312    ButtonBuilder::new(
313        WidgetBuilder::new()
314            .on_row(row)
315            .on_column(column)
316            .with_tab_index(tab_index)
317            .with_tooltip(make_simple_tooltip(ctx, tooltip))
318            .with_margin(Thickness::uniform(1.0)),
319    )
320    .with_content(
321        GridBuilder::new(
322            WidgetBuilder::new()
323                .with_child(
324                    ImageBuilder::new(
325                        WidgetBuilder::new()
326                            .on_row(0)
327                            .on_column(0)
328                            .with_background(Brush::Solid(color).into())
329                            .with_margin(Thickness {
330                                left: 2.0 * margin,
331                                top: margin,
332                                right: margin,
333                                bottom: margin,
334                            })
335                            .with_width(image_width - 2.0 * margin)
336                            .with_height(image_height - 2.0 * margin),
337                    )
338                    .with_opt_texture(image)
339                    .build(ctx),
340                )
341                .with_child(
342                    TextBuilder::new(
343                        WidgetBuilder::new()
344                            .on_row(0)
345                            .on_column(1)
346                            .with_vertical_alignment(VerticalAlignment::Center)
347                            .with_horizontal_alignment(HorizontalAlignment::Center)
348                            .with_margin(Thickness {
349                                left: 4.0,
350                                top: margin,
351                                right: 8.0,
352                                bottom: margin,
353                            }),
354                    )
355                    .with_font_size(font_size.into())
356                    .with_text(text)
357                    .build(ctx),
358                ),
359        )
360        .add_column(Column::auto())
361        .add_column(Column::stretch())
362        .add_row(Row::stretch())
363        .build(ctx),
364    )
365    .build(ctx)
366}
367
368pub fn load_image(data: &[u8]) -> Option<TextureResource> {
369    TextureResource::load_from_memory(
370        Uuid::new_v4(),
371        Default::default(),
372        data,
373        TextureImportOptions::default()
374            .with_compression(CompressionOptions::NoCompression)
375            .with_minification_filter(TextureMinificationFilter::LinearMipMapLinear)
376            .with_lod_bias(-1.0),
377    )
378    .ok()
379}