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_texture::{
36    CompressionOptions, TextureImportOptions, TextureMinificationFilter, TextureResource,
37    TextureResourceExtension,
38};
39use std::sync::Arc;
40
41pub enum ArrowDirection {
42    Top,
43    Bottom,
44    Left,
45    Right,
46}
47
48pub fn make_arrow_primitives_non_uniform_size(
49    orientation: ArrowDirection,
50    width: f32,
51    height: f32,
52) -> Vec<Primitive> {
53    vec![match orientation {
54        ArrowDirection::Top => Primitive::Triangle {
55            points: [
56                Vector2::new(width * 0.5, 0.0),
57                Vector2::new(width, height),
58                Vector2::new(0.0, height),
59            ],
60        },
61        ArrowDirection::Bottom => Primitive::Triangle {
62            points: [
63                Vector2::new(0.0, 0.0),
64                Vector2::new(width, 0.0),
65                Vector2::new(width * 0.5, height),
66            ],
67        },
68        ArrowDirection::Right => Primitive::Triangle {
69            points: [
70                Vector2::new(0.0, 0.0),
71                Vector2::new(width, height * 0.5),
72                Vector2::new(0.0, height),
73            ],
74        },
75        ArrowDirection::Left => Primitive::Triangle {
76            points: [
77                Vector2::new(0.0, height * 0.5),
78                Vector2::new(width, 0.0),
79                Vector2::new(width, height),
80            ],
81        },
82    }]
83}
84
85pub fn make_arrow_primitives(orientation: ArrowDirection, size: f32) -> Vec<Primitive> {
86    make_arrow_primitives_non_uniform_size(orientation, size, size)
87}
88
89pub fn make_arrow_non_uniform_size(
90    ctx: &mut BuildContext,
91    orientation: ArrowDirection,
92    width: f32,
93    height: f32,
94) -> Handle<UiNode> {
95    VectorImageBuilder::new(
96        WidgetBuilder::new()
97            .with_foreground(ctx.style.property(Style::BRUSH_BRIGHT))
98            .with_width(width)
99            .with_height(height)
100            .with_horizontal_alignment(HorizontalAlignment::Center)
101            .with_vertical_alignment(VerticalAlignment::Center),
102    )
103    .with_primitives(make_arrow_primitives_non_uniform_size(
104        orientation,
105        width,
106        height,
107    ))
108    .build(ctx)
109}
110
111pub fn make_arrow(
112    ctx: &mut BuildContext,
113    orientation: ArrowDirection,
114    size: f32,
115) -> Handle<UiNode> {
116    make_arrow_non_uniform_size(ctx, orientation, size, size)
117}
118
119pub fn make_cross_primitive(size: f32, thickness: f32) -> Vec<Primitive> {
120    vec![
121        Primitive::Line {
122            begin: Vector2::new(0.0, 0.0),
123            end: Vector2::new(size, size),
124            thickness,
125        },
126        Primitive::Line {
127            begin: Vector2::new(size, 0.0),
128            end: Vector2::new(0.0, size),
129            thickness,
130        },
131    ]
132}
133
134pub fn make_cross(ctx: &mut BuildContext, size: f32, thickness: f32) -> Handle<UiNode> {
135    VectorImageBuilder::new(
136        WidgetBuilder::new()
137            .with_horizontal_alignment(HorizontalAlignment::Center)
138            .with_vertical_alignment(VerticalAlignment::Center)
139            .with_width(size)
140            .with_height(size)
141            .with_foreground(ctx.style.property(Style::BRUSH_BRIGHT)),
142    )
143    .with_primitives(make_cross_primitive(size, thickness))
144    .build(ctx)
145}
146
147pub fn make_simple_tooltip(ctx: &mut BuildContext, text: &str) -> RcUiNodeHandle {
148    let handle = BorderBuilder::new(
149        WidgetBuilder::new()
150            .with_visibility(false)
151            .with_hit_test_visibility(false)
152            .with_foreground(ctx.style.property(Style::BRUSH_DARKEST))
153            .with_background(Brush::Solid(Color::opaque(230, 230, 230)).into())
154            .with_max_size(Vector2::new(300.0, f32::INFINITY))
155            .with_child(
156                TextBuilder::new(
157                    WidgetBuilder::new()
158                        .with_margin(Thickness::uniform(2.0))
159                        .with_foreground(ctx.style.property(Style::BRUSH_DARKER)),
160                )
161                .with_wrap(WrapMode::Word)
162                .with_text(text)
163                .build(ctx),
164            ),
165    )
166    .build(ctx);
167    RcUiNodeHandle::new(handle, ctx.sender())
168}
169
170pub fn make_dropdown_list_option_universal<T: Send + 'static>(
171    ctx: &mut BuildContext,
172    name: &str,
173    height: f32,
174    user_data: T,
175) -> Handle<UiNode> {
176    DecoratorBuilder::new(
177        BorderBuilder::new(
178            WidgetBuilder::new()
179                .with_height(height)
180                .with_user_data(Arc::new(Mutex::new(user_data)))
181                .with_child(
182                    TextBuilder::new(WidgetBuilder::new())
183                        .with_vertical_text_alignment(VerticalAlignment::Center)
184                        .with_horizontal_text_alignment(HorizontalAlignment::Center)
185                        .with_text(name)
186                        .build(ctx),
187                ),
188        )
189        .with_corner_radius(4.0f32.into())
190        .with_pad_by_corner_radius(false),
191    )
192    .build(ctx)
193}
194
195pub fn make_dropdown_list_option(ctx: &mut BuildContext, name: &str) -> Handle<UiNode> {
196    DecoratorBuilder::new(
197        BorderBuilder::new(
198            WidgetBuilder::new().with_child(
199                TextBuilder::new(WidgetBuilder::new())
200                    .with_vertical_text_alignment(VerticalAlignment::Center)
201                    .with_horizontal_text_alignment(HorizontalAlignment::Center)
202                    .with_text(name)
203                    .build(ctx),
204            ),
205        )
206        .with_corner_radius(4.0f32.into())
207        .with_pad_by_corner_radius(false),
208    )
209    .build(ctx)
210}
211
212pub fn make_dropdown_list_option_with_height(
213    ctx: &mut BuildContext,
214    name: &str,
215    height: f32,
216) -> Handle<UiNode> {
217    DecoratorBuilder::new(
218        BorderBuilder::new(
219            WidgetBuilder::new().with_height(height).with_child(
220                TextBuilder::new(WidgetBuilder::new())
221                    .with_vertical_text_alignment(VerticalAlignment::Center)
222                    .with_horizontal_text_alignment(HorizontalAlignment::Center)
223                    .with_text(name)
224                    .build(ctx),
225            ),
226        )
227        .with_corner_radius(4.0f32.into())
228        .with_pad_by_corner_radius(false),
229    )
230    .build(ctx)
231}
232
233pub fn make_image_button_with_tooltip(
234    ctx: &mut BuildContext,
235    width: f32,
236    height: f32,
237    image: Option<TextureResource>,
238    tooltip: &str,
239    tab_index: Option<usize>,
240) -> Handle<UiNode> {
241    ButtonBuilder::new(
242        WidgetBuilder::new()
243            .with_tab_index(tab_index)
244            .with_tooltip(make_simple_tooltip(ctx, tooltip))
245            .with_margin(Thickness::uniform(1.0)),
246    )
247    .with_content(
248        ImageBuilder::new(
249            WidgetBuilder::new()
250                .with_background(ctx.style.property(Style::BRUSH_BRIGHTEST))
251                .with_margin(Thickness::uniform(2.0))
252                .with_width(width)
253                .with_height(height),
254        )
255        .with_opt_texture(image)
256        .build(ctx),
257    )
258    .build(ctx)
259}
260
261pub fn make_text_and_image_button_with_tooltip(
262    ctx: &mut BuildContext,
263    text: &str,
264    image_width: f32,
265    image_height: f32,
266    image: Option<TextureResource>,
267    tooltip: &str,
268    row: usize,
269    column: usize,
270    tab_index: Option<usize>,
271    color: Color,
272    font_size: f32,
273) -> Handle<UiNode> {
274    let margin = 2.0;
275    ButtonBuilder::new(
276        WidgetBuilder::new()
277            .on_row(row)
278            .on_column(column)
279            .with_tab_index(tab_index)
280            .with_tooltip(make_simple_tooltip(ctx, tooltip))
281            .with_margin(Thickness::uniform(1.0)),
282    )
283    .with_content(
284        GridBuilder::new(
285            WidgetBuilder::new()
286                .with_child(
287                    ImageBuilder::new(
288                        WidgetBuilder::new()
289                            .on_row(0)
290                            .on_column(0)
291                            .with_background(Brush::Solid(color).into())
292                            .with_margin(Thickness {
293                                left: 2.0 * margin,
294                                top: margin,
295                                right: margin,
296                                bottom: margin,
297                            })
298                            .with_width(image_width - 2.0 * margin)
299                            .with_height(image_height - 2.0 * margin),
300                    )
301                    .with_opt_texture(image)
302                    .build(ctx),
303                )
304                .with_child(
305                    TextBuilder::new(
306                        WidgetBuilder::new()
307                            .on_row(0)
308                            .on_column(1)
309                            .with_vertical_alignment(VerticalAlignment::Center)
310                            .with_horizontal_alignment(HorizontalAlignment::Center)
311                            .with_margin(Thickness {
312                                left: 4.0,
313                                top: margin,
314                                right: 8.0,
315                                bottom: margin,
316                            }),
317                    )
318                    .with_font_size(font_size.into())
319                    .with_text(text)
320                    .build(ctx),
321                ),
322        )
323        .add_column(Column::auto())
324        .add_column(Column::stretch())
325        .add_row(Row::stretch())
326        .build(ctx),
327    )
328    .build(ctx)
329}
330
331pub fn load_image(data: &[u8]) -> Option<TextureResource> {
332    TextureResource::load_from_memory(
333        Default::default(),
334        data,
335        TextureImportOptions::default()
336            .with_compression(CompressionOptions::NoCompression)
337            .with_minification_filter(TextureMinificationFilter::LinearMipMapLinear)
338            .with_lod_bias(-1.0),
339    )
340    .ok()
341}