1use 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}