1use crate::button::Button;
22use crate::image::Image;
23use crate::vector_image::VectorImage;
24use crate::{
25 border::BorderBuilder,
26 button::ButtonBuilder,
27 core::{algebra::Vector2, color::Color, parking_lot::Mutex, pool::Handle},
28 decorator::DecoratorBuilder,
29 formatted_text::WrapMode,
30 grid::{Column, GridBuilder, Row},
31 image::ImageBuilder,
32 style::{resource::StyleResourceExt, Style},
33 text::TextBuilder,
34 vector_image::{Primitive, VectorImageBuilder},
35 widget::WidgetBuilder,
36 Brush, BuildContext, HorizontalAlignment, RcUiNodeHandle, Thickness, UiNode, VerticalAlignment,
37};
38use fyrox_core::Uuid;
39use fyrox_texture::{
40 CompressionOptions, TextureImportOptions, TextureMinificationFilter, TextureResource,
41 TextureResourceExtension,
42};
43use std::sync::Arc;
44
45pub enum ArrowDirection {
46 Top,
47 Bottom,
48 Left,
49 Right,
50}
51
52pub fn make_arrow_primitives_non_uniform_size(
53 orientation: ArrowDirection,
54 width: f32,
55 height: f32,
56) -> Vec<Primitive> {
57 vec![match orientation {
58 ArrowDirection::Top => Primitive::Triangle {
59 points: [
60 Vector2::new(width * 0.5, 0.0),
61 Vector2::new(width, height),
62 Vector2::new(0.0, height),
63 ],
64 },
65 ArrowDirection::Bottom => Primitive::Triangle {
66 points: [
67 Vector2::new(0.0, 0.0),
68 Vector2::new(width, 0.0),
69 Vector2::new(width * 0.5, height),
70 ],
71 },
72 ArrowDirection::Right => Primitive::Triangle {
73 points: [
74 Vector2::new(0.0, 0.0),
75 Vector2::new(width, height * 0.5),
76 Vector2::new(0.0, height),
77 ],
78 },
79 ArrowDirection::Left => Primitive::Triangle {
80 points: [
81 Vector2::new(0.0, height * 0.5),
82 Vector2::new(width, 0.0),
83 Vector2::new(width, height),
84 ],
85 },
86 }]
87}
88
89pub fn make_arrow_primitives(orientation: ArrowDirection, size: f32) -> Vec<Primitive> {
90 make_arrow_primitives_non_uniform_size(orientation, size, size)
91}
92
93pub fn make_arrow_non_uniform_size(
94 ctx: &mut BuildContext,
95 orientation: ArrowDirection,
96 width: f32,
97 height: f32,
98) -> Handle<VectorImage> {
99 VectorImageBuilder::new(
100 WidgetBuilder::new()
101 .with_foreground(ctx.style.property(Style::BRUSH_BRIGHT))
102 .with_width(width)
103 .with_height(height)
104 .with_horizontal_alignment(HorizontalAlignment::Center)
105 .with_vertical_alignment(VerticalAlignment::Center),
106 )
107 .with_primitives(make_arrow_primitives_non_uniform_size(
108 orientation,
109 width,
110 height,
111 ))
112 .build(ctx)
113}
114
115pub fn make_arrow(
116 ctx: &mut BuildContext,
117 orientation: ArrowDirection,
118 size: f32,
119) -> Handle<VectorImage> {
120 make_arrow_non_uniform_size(ctx, orientation, size, size)
121}
122
123pub fn make_cross_primitive(size: f32, thickness: f32) -> Vec<Primitive> {
124 vec![
125 Primitive::Line {
126 begin: Vector2::new(0.0, 0.0),
127 end: Vector2::new(size, size),
128 thickness,
129 },
130 Primitive::Line {
131 begin: Vector2::new(size, 0.0),
132 end: Vector2::new(0.0, size),
133 thickness,
134 },
135 ]
136}
137
138pub fn make_cross(ctx: &mut BuildContext, size: f32, thickness: f32) -> Handle<VectorImage> {
139 VectorImageBuilder::new(
140 WidgetBuilder::new()
141 .with_horizontal_alignment(HorizontalAlignment::Center)
142 .with_vertical_alignment(VerticalAlignment::Center)
143 .with_width(size)
144 .with_height(size)
145 .with_foreground(ctx.style.property(Style::BRUSH_BRIGHT)),
146 )
147 .with_primitives(make_cross_primitive(size, thickness))
148 .build(ctx)
149}
150
151pub fn make_simple_tooltip(ctx: &mut BuildContext, text: &str) -> RcUiNodeHandle {
152 let handle = BorderBuilder::new(
153 WidgetBuilder::new()
154 .with_visibility(false)
155 .with_hit_test_visibility(false)
156 .with_foreground(ctx.style.property(Style::BRUSH_DARKEST))
157 .with_background(Brush::Solid(Color::opaque(230, 230, 230)).into())
158 .with_width(300.0)
159 .with_child(
160 TextBuilder::new(
161 WidgetBuilder::new()
162 .with_margin(Thickness::uniform(2.0))
163 .with_foreground(ctx.style.property(Style::BRUSH_DARKER)),
164 )
165 .with_wrap(WrapMode::Word)
166 .with_text(text)
167 .build(ctx),
168 ),
169 )
170 .build(ctx);
171 RcUiNodeHandle::new(handle, ctx.sender())
172}
173
174pub fn make_asset_preview_tooltip(
175 texture: Option<TextureResource>,
176 ctx: &mut BuildContext,
177) -> (RcUiNodeHandle, Handle<Image>) {
178 let size = 120.0;
179 let image_preview;
180 let image_preview_tooltip = BorderBuilder::new(
181 WidgetBuilder::new()
182 .with_visibility(false)
183 .with_hit_test_visibility(false)
184 .with_foreground(ctx.style.property(Style::BRUSH_DARKEST))
185 .with_background(Brush::Solid(Color::opaque(230, 230, 230)).into())
186 .with_width(size + 2.0)
187 .with_height(size + 2.0)
188 .with_child({
189 image_preview = ImageBuilder::new(
190 WidgetBuilder::new()
191 .on_column(0)
192 .with_width(size)
193 .with_height(size)
194 .with_margin(Thickness::uniform(1.0)),
195 )
196 .with_checkerboard_background(true)
197 .with_opt_texture(texture)
198 .with_sync_with_texture_size(false)
199 .build(ctx);
200 image_preview
201 }),
202 )
203 .build(ctx);
204 (
205 RcUiNodeHandle::new(image_preview_tooltip, ctx.sender()),
206 image_preview,
207 )
208}
209
210fn adjust_decorator(builder: DecoratorBuilder, ctx: &BuildContext) -> DecoratorBuilder {
211 builder
212 .with_normal_brush(ctx.style.property(Style::BRUSH_DARKER))
213 .with_hover_brush(ctx.style.property(Style::BRUSH_DARK))
214 .with_pressed_brush(ctx.style.property(Style::BRUSH_PRIMARY))
215 .with_selected_brush(ctx.style.property(Style::BRUSH_LIGHTER_PRIMARY))
216}
217
218fn text_margin() -> Thickness {
219 Thickness {
220 left: 5.0,
221 top: 2.0,
222 right: 2.0,
223 bottom: 2.0,
224 }
225}
226
227pub fn make_dropdown_list_option_universal<T: Send + 'static>(
228 ctx: &mut BuildContext,
229 name: &str,
230 height: f32,
231 user_data: T,
232) -> Handle<UiNode> {
233 adjust_decorator(
234 DecoratorBuilder::new(
235 BorderBuilder::new(
236 WidgetBuilder::new()
237 .with_height(height)
238 .with_user_data(Arc::new(Mutex::new(user_data)))
239 .with_child(
240 TextBuilder::new(WidgetBuilder::new().with_margin(text_margin()))
241 .with_vertical_text_alignment(VerticalAlignment::Center)
242 .with_horizontal_text_alignment(HorizontalAlignment::Left)
243 .with_text(name)
244 .build(ctx),
245 ),
246 )
247 .with_corner_radius(4.0f32.into())
248 .with_pad_by_corner_radius(false),
249 ),
250 ctx,
251 )
252 .build(ctx)
253 .to_base()
254}
255
256pub fn make_dropdown_list_option(ctx: &mut BuildContext, name: &str) -> Handle<UiNode> {
257 adjust_decorator(
258 DecoratorBuilder::new(
259 BorderBuilder::new(
260 WidgetBuilder::new().with_child(
261 TextBuilder::new(
262 WidgetBuilder::new()
263 .with_margin(text_margin())
264 .with_vertical_alignment(VerticalAlignment::Center),
265 )
266 .with_vertical_text_alignment(VerticalAlignment::Center)
267 .with_horizontal_text_alignment(HorizontalAlignment::Left)
268 .with_text(name)
269 .build(ctx),
270 ),
271 )
272 .with_corner_radius(4.0f32.into())
273 .with_pad_by_corner_radius(false),
274 ),
275 ctx,
276 )
277 .build(ctx)
278 .to_base()
279}
280
281pub fn make_dropdown_list_option_with_height(
282 ctx: &mut BuildContext,
283 name: &str,
284 height: f32,
285) -> Handle<UiNode> {
286 adjust_decorator(
287 DecoratorBuilder::new(
288 BorderBuilder::new(
289 WidgetBuilder::new().with_height(height).with_child(
290 TextBuilder::new(WidgetBuilder::new().with_margin(text_margin()))
291 .with_vertical_text_alignment(VerticalAlignment::Center)
292 .with_horizontal_text_alignment(HorizontalAlignment::Left)
293 .with_text(name)
294 .build(ctx),
295 ),
296 )
297 .with_corner_radius(4.0f32.into())
298 .with_pad_by_corner_radius(false),
299 ),
300 ctx,
301 )
302 .build(ctx)
303 .to_base()
304}
305
306pub fn make_image_button_with_tooltip(
307 ctx: &mut BuildContext,
308 width: f32,
309 height: f32,
310 image: Option<TextureResource>,
311 tooltip: &str,
312 tab_index: Option<usize>,
313) -> Handle<Button> {
314 ButtonBuilder::new(
315 WidgetBuilder::new()
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 ImageBuilder::new(
322 WidgetBuilder::new()
323 .with_background(ctx.style.property(Style::BRUSH_BRIGHTEST))
324 .with_margin(Thickness::uniform(3.0))
325 .with_width(width)
326 .with_height(height),
327 )
328 .with_opt_texture(image)
329 .build(ctx),
330 )
331 .build(ctx)
332}
333
334pub fn make_text_and_image_button_with_tooltip(
335 ctx: &mut BuildContext,
336 text: &str,
337 image_width: f32,
338 image_height: f32,
339 image: Option<TextureResource>,
340 tooltip: &str,
341 row: usize,
342 column: usize,
343 tab_index: Option<usize>,
344 color: Color,
345 font_size: f32,
346) -> Handle<Button> {
347 let margin = 3.0;
348 ButtonBuilder::new(
349 WidgetBuilder::new()
350 .on_row(row)
351 .on_column(column)
352 .with_tab_index(tab_index)
353 .with_tooltip(make_simple_tooltip(ctx, tooltip))
354 .with_margin(Thickness::uniform(1.0)),
355 )
356 .with_content(
357 GridBuilder::new(
358 WidgetBuilder::new()
359 .with_child(
360 ImageBuilder::new(
361 WidgetBuilder::new()
362 .on_row(0)
363 .on_column(0)
364 .with_background(Brush::Solid(color).into())
365 .with_margin(Thickness {
366 left: 2.0 * margin,
367 top: margin,
368 right: margin,
369 bottom: margin,
370 })
371 .with_width(image_width - 2.0 * margin)
372 .with_height(image_height - 2.0 * margin),
373 )
374 .with_opt_texture(image)
375 .build(ctx),
376 )
377 .with_child(
378 TextBuilder::new(
379 WidgetBuilder::new()
380 .on_row(0)
381 .on_column(1)
382 .with_vertical_alignment(VerticalAlignment::Center)
383 .with_horizontal_alignment(HorizontalAlignment::Center)
384 .with_margin(Thickness {
385 left: 4.0,
386 top: margin,
387 right: 8.0,
388 bottom: margin,
389 }),
390 )
391 .with_font_size(font_size.into())
392 .with_text(text)
393 .build(ctx),
394 ),
395 )
396 .add_column(Column::auto())
397 .add_column(Column::stretch())
398 .add_row(Row::stretch())
399 .build(ctx),
400 )
401 .build(ctx)
402}
403
404pub fn load_image(data: &[u8]) -> Option<TextureResource> {
405 TextureResource::load_from_memory(
406 Uuid::new_v4(),
407 Default::default(),
408 data,
409 TextureImportOptions::default()
410 .with_compression(CompressionOptions::NoCompression)
411 .with_minification_filter(TextureMinificationFilter::LinearMipMapLinear)
412 .with_lod_bias(-1.0),
413 )
414 .ok()
415}