2024-03-11 15:10:18 +01:00
2025-10-15 18:39:55 +02:00
let ui_menubar_default_w : i32 = 330 ;
let ui_menubar_hwnd : ui_handle_t = ui_handle_create ();
2024-09-03 15:52:05 +02:00
let ui_menubar_menu_handle : ui_handle_t = ui_handle_create ();
2025-10-15 18:39:55 +02:00
let ui_menubar_tab : ui_handle_t = ui_handle_create ();
let ui_menubar_w : i32 = ui_menubar_default_w ;
let ui_menubar_category : i32 = 0 ;
2024-03-11 15:10:18 +01:00
2024-10-19 19:44:38 +02:00
let _ui_menubar_saved_camera : mat4_t = mat4_nan ();
2024-03-11 15:10:18 +01:00
let _ui_menubar_plane : mesh_object_t = null ;
2025-08-08 14:08:39 +02:00
type update_info_t = {
2025-10-15 18:39:55 +02:00
version : i32 ; version_name : string ;
2025-08-08 14:08:39 +02:00
};
2024-03-11 15:10:18 +01:00
function ui_menubar_init() {
2025-10-15 18:39:55 +02:00
ui_menubar_hwnd . layout = ui_layout_t . HORIZONTAL ;
2024-09-03 15:52:05 +02:00
ui_menubar_menu_handle . layout = ui_layout_t . HORIZONTAL ;
2024-03-11 15:10:18 +01:00
}
2025-08-21 12:00:03 +02:00
function ui_menu_panel_x () : i32 {
2025-09-20 14:14:13 +02:00
let panel_x : i32 = base_x ();
2025-01-09 15:47:57 +01:00
if ( config_raw . layout [ layout_size_t . HEADER ] == 1 ) {
2025-08-21 12:00:03 +02:00
let item_w : i32 = ui_toolbar_w ();
2025-10-15 18:39:55 +02:00
panel_x = base_x () - item_w ;
2025-01-09 15:47:57 +01:00
}
2025-08-21 12:00:03 +02:00
else {
panel_x += 5 * UI_SCALE ();
}
return panel_x ;
}
function ui_menu_panel_y () : i32 {
let panel_y : i32 = 0 ;
if ( config_raw . layout [ layout_size_t . HEADER ] == 1 ) {
}
else {
panel_y += 5 * UI_SCALE ();
}
return panel_y ;
}
function ui_menubar_render_ui() {
2025-10-15 18:39:55 +02:00
let item_w : i32 = ui_toolbar_w ();
2025-08-21 12:00:03 +02:00
let panel_x : i32 = ui_menu_panel_x ();
let panel_y : i32 = ui_menu_panel_y ();
2024-03-11 15:10:18 +01:00
2025-08-21 12:00:03 +02:00
if ( ui_window ( ui_menubar_menu_handle , panel_x , panel_y , ui_menubar_w , ui_header_h )) {
2025-09-20 14:14:13 +02:00
if ( ! base_view3d_show ) {
ui . _x += 1 ;
}
2024-03-11 15:10:18 +01:00
2024-09-03 15:52:05 +02:00
ui_begin_menu ();
2024-03-11 15:10:18 +01:00
if ( config_raw . touch_ui ) {
2025-01-09 15:47:57 +01:00
ui . _w = item_w ;
2024-03-11 15:10:18 +01:00
2025-10-15 18:39:55 +02:00
if ( ui_menubar_icon_button ( 0 , 2 ))
box_preferences_show ();
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 0 , 3 )) {
2025-09-03 19:44:26 +02:00
// Save project icon in lit mode
2025-09-06 21:17:28 +02:00
context_set_viewport_mode ( viewport_mode_t . LIT );
2024-03-11 15:10:18 +01:00
console_toast ( tr ( "Saving project" ));
2025-10-15 18:39:55 +02:00
sys_notify_on_next_frame ( function () {
sys_notify_on_end_frame ( function () {
2025-09-06 21:17:28 +02:00
project_save ();
box_projects_show ();
});
2024-03-11 15:10:18 +01:00
});
}
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 4 , 2 )) {
2024-03-20 16:13:54 +01:00
project_import_asset ();
}
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 5 , 2 )) {
2024-03-20 16:13:54 +01:00
box_export_show_textures ();
}
2025-08-15 22:04:07 +02:00
let size : i32 = math_floor ( ui . _w / UI_SCALE ());
2025-08-08 14:08:39 +02:00
if ( ui_menu_show && ui_menubar_category == menubar_category_t . VIEWPORT ) {
2024-09-03 15:52:05 +02:00
ui_fill ( 0 , - 6 , size , size - 4 , ui . ops . theme . HIGHLIGHT_COL );
2024-03-20 16:13:54 +01:00
}
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 8 , 2 )) {
ui_menubar_show_menu ( menubar_category_t . VIEWPORT );
2024-03-20 16:13:54 +01:00
}
2025-08-08 14:08:39 +02:00
if ( ui_menu_show && ui_menubar_category == menubar_category_t . MODE ) {
2024-09-03 15:52:05 +02:00
ui_fill ( 0 , - 6 , size , size - 4 , ui . ops . theme . HIGHLIGHT_COL );
2024-03-20 16:13:54 +01:00
}
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 9 , 2 )) {
ui_menubar_show_menu ( menubar_category_t . MODE );
2024-03-20 16:13:54 +01:00
}
2025-08-08 14:08:39 +02:00
if ( ui_menu_show && ui_menubar_category == menubar_category_t . CAMERA ) {
2024-09-03 15:52:05 +02:00
ui_fill ( 0 , - 6 , size , size - 4 , ui . ops . theme . HIGHLIGHT_COL );
2024-03-20 16:13:54 +01:00
}
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 10 , 2 )) {
ui_menubar_show_menu ( menubar_category_t . CAMERA );
2024-03-20 16:13:54 +01:00
}
2025-08-08 14:08:39 +02:00
if ( ui_menu_show && ui_menubar_category == menubar_category_t . HELP ) {
2024-09-03 15:52:05 +02:00
ui_fill ( 0 , - 6 , size , size - 4 , ui . ops . theme . HIGHLIGHT_COL );
2024-03-20 16:13:54 +01:00
}
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 11 , 2 )) {
ui_menubar_show_menu ( menubar_category_t . HELP );
2024-03-20 16:13:54 +01:00
}
2024-03-11 15:10:18 +01:00
ui . enabled = history_undos > 0 ;
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 6 , 2 )) {
2024-03-20 16:13:54 +01:00
history_undo ();
}
2024-03-11 15:10:18 +01:00
ui . enabled = history_redos > 0 ;
2025-08-15 22:59:45 +02:00
if ( ui_menubar_icon_button ( 7 , 2 )) {
2024-03-20 16:13:54 +01:00
history_redo ();
}
2024-03-11 15:10:18 +01:00
ui . enabled = true ;
}
else {
2025-10-15 18:39:55 +02:00
let categories : string [] = [ tr ( "File" ), tr ( "Edit" ), tr ( "Viewport" ), tr ( "Mode" ), tr ( "Camera" ), tr ( "Help" ) ];
2024-03-11 15:10:18 +01:00
for ( let i : i32 = 0 ; i < categories . length ; ++ i ) {
2025-08-08 14:08:39 +02:00
if ( _ui_menu_button ( categories [ i ]) || ( ui_menu_show && ui_menu_commands == ui_menubar_draw_category_items && ui . is_hovered )) {
2025-08-15 22:59:45 +02:00
ui_menubar_show_menu ( i );
2024-03-11 15:10:18 +01:00
}
}
}
2025-09-17 22:52:15 +02:00
// Store real menubar w
2024-03-11 15:10:18 +01:00
if ( ui_menubar_w < ui . _x + 10 ) {
2025-10-15 18:39:55 +02:00
ui_menubar_w = math_floor ( ui . _x + 10 );
2024-03-11 15:10:18 +01:00
ui_toolbar_handle . redraws = 2 ;
}
2025-09-17 22:52:15 +02:00
// Crop menubar if sidebar + nodes are overlapping
let nodesw : i32 = ( ui_nodes_show || ui_view2d_show ) ? config_raw . layout [ layout_size_t . NODES_W ] : 0 ;
if ( ui_menubar_w > iron_window_width () - config_raw . layout [ layout_size_t . SIDEBAR_W ] - nodesw ) {
2025-10-15 18:39:55 +02:00
ui_menubar_w = iron_window_width () - config_raw . layout [ layout_size_t . SIDEBAR_W ] - nodesw ;
2025-09-17 22:52:15 +02:00
ui_toolbar_handle . redraws = 2 ;
}
2024-03-11 15:10:18 +01:00
2024-09-03 15:52:05 +02:00
ui_end_menu ();
2024-03-11 15:10:18 +01:00
}
2025-01-09 15:47:57 +01:00
if ( config_raw . layout [ layout_size_t . HEADER ] == 1 ) {
// Non-floating header
ui_menubar_draw_tab_header ();
}
}
function ui_menubar_draw_tab_header() {
2025-10-15 18:39:55 +02:00
let item_w : i32 = ui_toolbar_w ();
2025-09-20 14:14:13 +02:00
let panel_x : i32 = base_x ();
2025-01-09 15:47:57 +01:00
2024-03-11 15:10:18 +01:00
let nodesw : i32 = ( ui_nodes_show || ui_view2d_show ) ? config_raw . layout [ layout_size_t . NODES_W ] : 0 ;
2025-10-15 18:39:55 +02:00
let ww : i32 = iron_window_width () - config_raw . layout [ layout_size_t . SIDEBAR_W ] - ui_menubar_w - nodesw ;
panel_x = ( base_x () - item_w ) + ui_menubar_w ;
2025-09-20 14:14:13 +02:00
if ( ! base_view3d_show ) {
panel_x += 1 ;
}
2024-03-11 15:10:18 +01:00
2025-09-20 11:03:59 +02:00
if ( ui_window ( ui_menubar_hwnd , panel_x , 0 , ww , ui_header_h )) {
2025-09-19 21:24:11 +02:00
if ( config_raw . touch_ui ) {
ui_fill ( 0 , 0 , ui . _window_w , ui . _window_h + 4 , ui . ops . theme . SEPARATOR_COL );
2024-03-11 15:10:18 +01:00
}
else {
2025-09-20 14:14:13 +02:00
let a : bool = ui_tab ( ui_menubar_tab , tr ( "3D View" ));
let b : bool = ui_tab ( ui_menubar_tab , base_view3d_show ? ">" : "<" , false , - 2 );
if (( a && ! base_view3d_show ) || b ) {
2025-09-20 11:03:59 +02:00
base_view3d_show = ! base_view3d_show ;
2025-09-23 21:44:39 +02:00
ui_menubar_tab . i = base_view3d_show ? 0 : - 1 ;
2025-09-20 14:14:13 +02:00
base_resize ();
2025-09-19 21:24:11 +02:00
}
2024-03-11 15:10:18 +01:00
}
}
}
2025-08-15 22:59:45 +02:00
function ui_menubar_draw_category_items() {
2025-08-08 14:08:39 +02:00
if ( ui_menubar_category == menubar_category_t . FILE ) {
if ( ui_menu_button ( tr ( "New Project..." ), map_get ( config_keymap , "file_new" ))) {
project_new_box ();
}
if ( ui_menu_button ( tr ( "Open..." ), map_get ( config_keymap , "file_open" ))) {
project_open ();
}
if ( ui_menu_button ( tr ( "Open Recent..." ), map_get ( config_keymap , "file_open_recent" ))) {
box_projects_show ();
}
if ( ui_menu_button ( tr ( "Save" ), map_get ( config_keymap , "file_save" ))) {
project_save ();
}
if ( ui_menu_button ( tr ( "Save As..." ), map_get ( config_keymap , "file_save_as" ))) {
project_save_as ();
}
2025-08-15 22:59:45 +02:00
ui_menu_separator ();
2025-08-08 14:08:39 +02:00
if ( ui_menu_button ( tr ( "Import Texture..." ), map_get ( config_keymap , "file_import_assets" ))) {
project_import_asset ( string_array_join ( path_texture_formats , "," ), false );
}
if ( ui_menu_button ( tr ( "Import Envmap..." ))) {
2025-10-15 18:39:55 +02:00
ui_files_show ( "hdr" , false , false , function ( path : string ) {
2025-08-08 14:08:39 +02:00
if ( ! ends_with ( path , ".hdr" )) {
console_error ( tr ( "Error: .hdr file expected" ));
return ;
}
import_asset_run ( path );
});
}
if ( ui_menu_button ( tr ( "Import Font..." ))) {
project_import_asset ( "ttf,ttc,otf" );
}
if ( ui_menu_button ( tr ( "Import Material..." ))) {
project_import_material ();
}
if ( ui_menu_button ( tr ( "Import Brush..." ))) {
project_import_brush ();
}
if ( ui_menu_button ( tr ( "Import Swatches..." ))) {
project_import_swatches ();
}
if ( ui_menu_button ( tr ( "Import Mesh..." ))) {
project_import_mesh ();
}
if ( ui_menu_button ( tr ( "Reimport Mesh" ), map_get ( config_keymap , "file_reimport_mesh" ))) {
project_reimport_mesh ();
}
if ( ui_menu_button ( tr ( "Reimport Textures" ), map_get ( config_keymap , "file_reimport_textures" ))) {
project_reimport_textures ();
}
2025-08-15 22:59:45 +02:00
ui_menu_separator ();
2025-08-08 14:08:39 +02:00
if ( ui_menu_button ( tr ( "Export Textures..." ), map_get ( config_keymap , "file_export_textures_as" ))) {
context_raw . layers_export = export_mode_t . VISIBLE ;
box_export_show_textures ();
}
if ( ui_menu_button ( tr ( "Export Swatches..." ))) {
project_export_swatches ();
}
if ( ui_menu_button ( tr ( "Export Mesh..." ))) {
context_raw . export_mesh_index = 0 ; // All
box_export_show_mesh ();
}
if ( ui_menu_button ( tr ( "Bake Material..." ))) {
box_export_show_bake_material ();
}
2025-08-15 22:59:45 +02:00
ui_menu_separator ();
2025-08-08 14:08:39 +02:00
if ( ui_menu_button ( tr ( "Exit" ))) {
iron_stop ();
}
}
else if ( ui_menubar_category == menubar_category_t . EDIT ) {
let step_undo : string = "" ;
let step_redo : string = "" ;
if ( history_undos > 0 ) {
step_undo = history_steps [ history_steps . length - 1 - history_redos ]. name ;
}
if ( history_redos > 0 ) {
step_redo = history_steps [ history_steps . length - history_redos ]. name ;
}
2025-10-15 18:39:55 +02:00
ui . enabled = history_undos > 0 ;
2025-08-08 14:08:39 +02:00
let vars_undo : map_t < string , string > = map_create ();
map_set ( vars_undo , "step" , step_undo );
if ( ui_menu_button ( tr ( "Undo {step}" , vars_undo ), map_get ( config_keymap , "edit_undo" ))) {
history_undo ();
}
2025-10-15 18:39:55 +02:00
ui . enabled = history_redos > 0 ;
2025-08-08 14:08:39 +02:00
let vars_redo : map_t < string , string > = map_create ();
map_set ( vars_redo , "step" , step_redo );
if ( ui_menu_button ( tr ( "Redo {step}" , vars_redo ), map_get ( config_keymap , "edit_redo" ))) {
history_redo ();
}
ui . enabled = true ;
2025-08-15 22:59:45 +02:00
ui_menu_separator ();
2025-08-08 14:08:39 +02:00
if ( ui_menu_button ( tr ( "Preferences..." ), map_get ( config_keymap , "edit_prefs" ))) {
box_preferences_show ();
}
}
else if ( ui_menubar_category == menubar_category_t . VIEWPORT ) {
if ( ui_menu_button ( tr ( "Distract Free" ), map_get ( config_keymap , "view_distract_free" ))) {
ui_base_toggle_distract_free ();
2025-08-15 22:59:45 +02:00
ui . is_hovered = false ;
2025-08-08 14:08:39 +02:00
}
2025-10-15 18:39:55 +02:00
/// if !(arm_android || arm_ios)
2025-08-08 14:08:39 +02:00
if ( ui_menu_button ( tr ( "Toggle Fullscreen" ), "alt+enter" )) {
base_toggle_fullscreen ();
}
2025-10-15 18:39:55 +02:00
/// end
2025-08-08 14:08:39 +02:00
ui . changed = false ;
2025-10-15 18:39:55 +02:00
let p : world_data_t = scene_world ;
2025-08-08 14:08:39 +02:00
let env_handle : ui_handle_t = ui_handle ( __ID__ );
2025-10-15 18:39:55 +02:00
env_handle . f = p . strength ;
2025-08-15 22:59:45 +02:00
ui_menu_align ();
2025-08-08 14:08:39 +02:00
p . strength = ui_slider ( env_handle , tr ( "Environment" ), 0.0 , 8.0 , true );
if ( env_handle . changed ) {
context_raw . ddirty = 2 ;
}
let enva_handle : ui_handle_t = ui_handle ( __ID__ );
2025-10-15 18:39:55 +02:00
enva_handle . f = context_raw . envmap_angle / math_pi () * 180.0 ;
2025-09-23 21:44:39 +02:00
if ( enva_handle . f < 0 ) {
enva_handle . f += ( math_floor ( - enva_handle . f / 360 ) + 1 ) * 360 ;
2025-08-08 14:08:39 +02:00
}
2025-09-23 21:44:39 +02:00
else if ( enva_handle . f > 360 ) {
enva_handle . f -= math_floor ( enva_handle . f / 360 ) * 360 ;
2025-08-08 14:08:39 +02:00
}
2025-08-15 22:59:45 +02:00
ui_menu_align ();
2025-08-08 14:08:39 +02:00
context_raw . envmap_angle = ui_slider ( enva_handle , tr ( "Environment Angle" ), 0.0 , 360.0 , true , 1 ) / 180.0 * math_pi ();
if ( ui . is_hovered ) {
let vars : map_t < string , string > = map_create ();
map_set ( vars , "shortcut" , map_get ( config_keymap , "rotate_envmap" ));
ui_tooltip ( tr ( "{shortcut} and move mouse" , vars ));
}
if ( enva_handle . changed ) {
context_raw . ddirty = 2 ;
}
let split_view_handle : ui_handle_t = ui_handle ( __ID__ );
if ( split_view_handle . init ) {
2025-09-23 21:44:39 +02:00
split_view_handle . b = context_raw . split_view ;
2025-08-08 14:08:39 +02:00
}
context_raw . split_view = ui_check ( split_view_handle , " " + tr ( "Split View" ));
if ( split_view_handle . changed ) {
base_resize ();
}
let cull_handle : ui_handle_t = ui_handle ( __ID__ );
if ( cull_handle . init ) {
2025-09-23 21:44:39 +02:00
cull_handle . b = context_raw . cull_backfaces ;
2025-08-08 14:08:39 +02:00
}
context_raw . cull_backfaces = ui_check ( cull_handle , " " + tr ( "Cull Backfaces" ));
if ( cull_handle . changed ) {
make_material_parse_mesh_material ();
}
let filter_handle : ui_handle_t = ui_handle ( __ID__ );
if ( filter_handle . init ) {
2025-09-23 21:44:39 +02:00
filter_handle . b = context_raw . texture_filter ;
2025-08-08 14:08:39 +02:00
}
context_raw . texture_filter = ui_check ( filter_handle , " " + tr ( "Filter Textures" ));
if ( filter_handle . changed ) {
2025-08-09 21:20:37 +02:00
gpu_use_linear_sampling ( context_raw . texture_filter );
2025-08-08 14:08:39 +02:00
}
context_raw . draw_wireframe = ui_check ( context_raw . wireframe_handle , " " + tr ( "Wireframe" ));
if ( context_raw . wireframe_handle . changed ) {
let current : gpu_texture_t = _draw_current ;
draw_end ();
util_uv_cache_uv_map ();
draw_begin ( current );
make_material_parse_mesh_material ();
}
context_raw . draw_texels = ui_check ( context_raw . texels_handle , " " + tr ( "Texels" ));
if ( context_raw . texels_handle . changed ) {
make_material_parse_mesh_material ();
}
let compass_handle : ui_handle_t = ui_handle ( __ID__ );
if ( compass_handle . init ) {
2025-09-23 21:44:39 +02:00
compass_handle . b = context_raw . show_compass ;
2025-08-08 14:08:39 +02:00
}
context_raw . show_compass = ui_check ( compass_handle , " " + tr ( "Compass" ));
if ( compass_handle . changed ) {
context_raw . ddirty = 2 ;
}
2025-09-23 21:44:39 +02:00
context_raw . show_envmap_handle . b = context_raw . show_envmap ;
2025-10-15 18:39:55 +02:00
context_raw . show_envmap = ui_check ( context_raw . show_envmap_handle , " " + tr ( "Envmap" ));
2025-08-08 14:08:39 +02:00
if ( context_raw . show_envmap_handle . changed ) {
context_load_envmap ();
context_raw . ddirty = 2 ;
}
context_raw . show_envmap_blur = ui_check ( context_raw . show_envmap_blur_handle , " " + tr ( "Blur Envmap" ));
if ( context_raw . show_envmap_blur_handle . changed ) {
context_raw . ddirty = 2 ;
}
2025-09-04 11:54:56 +02:00
if ( ui_menu_button ( tr ( "Reset Envmap" ))) {
project_set_default_envmap ();
}
2025-10-17 18:04:21 +02:00
if ( ui_menu_button ( tr ( "Capture Screenshot" ))) {
sys_notify_on_next_frame ( function () {
viewport_capture_screenshot ();
});
ui . changed = false ; // Close menu
}
2025-08-08 14:08:39 +02:00
context_update_envmap ();
if ( ui . changed ) {
ui_menu_keep_open = true ;
}
}
else if ( ui_menubar_category == menubar_category_t . MODE ) {
let mode_handle : ui_handle_t = ui_handle ( __ID__ );
2025-10-15 18:39:55 +02:00
mode_handle . i = context_raw . viewport_mode ;
let modes : string [] = [
tr ( "Lit" ), tr ( "Base Color" ), tr ( "Normal" ), tr ( "Occlusion" ), tr ( "Roughness" ), tr ( "Metallic" ), tr ( "Opacity" ), tr ( "Height" ), tr ( "Emission" ),
tr ( "Subsurface" ), tr ( "TexCoord" ), tr ( "Object Normal" ), tr ( "Material ID" ), tr ( "Object ID" ), tr ( "Mask" )
2025-08-08 14:08:39 +02:00
];
2025-10-15 18:39:55 +02:00
let shortcuts : string [] = [ "l" , "b" , "n" , "o" , "r" , "m" , "a" , "h" , "e" , "s" , "t" , "1" , "2" , "3" , "4" ];
2025-08-08 14:08:39 +02:00
if ( gpu_raytrace_supported ()) {
array_push ( modes , tr ( "Path Traced" ));
array_push ( shortcuts , "p" );
}
for ( let i : i32 = 0 ; i < modes . length ; ++ i ) {
let shortcut : string = config_raw . touch_ui ? "" : map_get ( config_keymap , "viewport_mode" ) + ", " + shortcuts [ i ];
ui_radio ( mode_handle , i , modes [ i ], shortcut );
}
if ( mode_handle . changed ) {
2025-09-23 21:44:39 +02:00
context_set_viewport_mode ( mode_handle . i );
2025-08-08 14:08:39 +02:00
ui_menu_keep_open = true ;
}
}
else if ( ui_menubar_category == menubar_category_t . CAMERA ) {
if ( ui_menu_button ( tr ( "Reset" ), map_get ( config_keymap , "view_reset" ))) {
viewport_reset ();
viewport_scale_to_bounds ();
}
2025-08-15 22:59:45 +02:00
ui_menu_separator ();
2025-08-08 14:08:39 +02:00
if ( ui_menu_button ( tr ( "Front" ), map_get ( config_keymap , "view_front" ))) {
viewport_set_view ( 0 , - 1 , 0 , math_pi () / 2 , 0 , 0 );
}
if ( ui_menu_button ( tr ( "Back" ), map_get ( config_keymap , "view_back" ))) {
viewport_set_view ( 0 , 1 , 0 , math_pi () / 2 , 0 , math_pi ());
}
if ( ui_menu_button ( tr ( "Right" ), map_get ( config_keymap , "view_right" ))) {
viewport_set_view ( 1 , 0 , 0 , math_pi () / 2 , 0 , math_pi () / 2 );
}
if ( ui_menu_button ( tr ( "Left" ), map_get ( config_keymap , "view_left" ))) {
viewport_set_view ( - 1 , 0 , 0 , math_pi () / 2 , 0 , - math_pi () / 2 );
}
if ( ui_menu_button ( tr ( "Top" ), map_get ( config_keymap , "view_top" ))) {
viewport_set_view ( 0 , 0 , 1 , 0 , 0 , 0 );
}
if ( ui_menu_button ( tr ( "Bottom" ), map_get ( config_keymap , "view_bottom" ))) {
viewport_set_view ( 0 , 0 , - 1 , math_pi (), 0 , math_pi ());
}
2025-08-15 22:59:45 +02:00
ui_menu_separator ();
2025-08-08 14:08:39 +02:00
ui . changed = false ;
if ( ui_menu_button ( tr ( "Orbit Left" ), map_get ( config_keymap , "view_orbit_left" ))) {
viewport_orbit ( - math_pi () / 12 , 0 );
}
if ( ui_menu_button ( tr ( "Orbit Right" ), map_get ( config_keymap , "view_orbit_right" ))) {
viewport_orbit ( math_pi () / 12 , 0 );
}
if ( ui_menu_button ( tr ( "Orbit Up" ), map_get ( config_keymap , "view_orbit_up" ))) {
viewport_orbit ( 0 , - math_pi () / 12 );
}
if ( ui_menu_button ( tr ( "Orbit Down" ), map_get ( config_keymap , "view_orbit_down" ))) {
viewport_orbit ( 0 , math_pi () / 12 );
}
if ( ui_menu_button ( tr ( "Orbit Opposite" ), map_get ( config_keymap , "view_orbit_opposite" ))) {
viewport_orbit_opposite ();
}
if ( ui_menu_button ( tr ( "Zoom In" ), map_get ( config_keymap , "view_zoom_in" ))) {
viewport_zoom ( 0.2 );
}
if ( ui_menu_button ( tr ( "Zoom Out" ), map_get ( config_keymap , "view_zoom_out" ))) {
viewport_zoom ( - 0.2 );
}
let cam : camera_object_t = scene_camera ;
2025-10-15 18:39:55 +02:00
context_raw . fov_handle = ui_handle ( __ID__ );
2025-08-08 14:08:39 +02:00
if ( context_raw . fov_handle . init ) {
2025-09-23 21:44:39 +02:00
context_raw . fov_handle . f = math_floor ( cam . data . fov * 100 ) / 100 ;
2025-08-08 14:08:39 +02:00
}
2025-08-15 22:59:45 +02:00
ui_menu_align ();
2025-08-08 14:08:39 +02:00
cam . data . fov = ui_slider ( context_raw . fov_handle , tr ( "FoV" ), 0.3 , 1.4 , true );
if ( context_raw . fov_handle . changed ) {
viewport_update_camera_type ( context_raw . camera_type );
}
2025-08-15 22:59:45 +02:00
ui_menu_align ();
2025-08-08 14:08:39 +02:00
let camera_controls_handle : ui_handle_t = ui_handle ( __ID__ );
2025-10-15 18:39:55 +02:00
camera_controls_handle . i = context_raw . camera_controls ;
let camera_controls_items : string [] = [ tr ( "Orbit" ), tr ( "Rotate" ), tr ( "Fly" ) ];
context_raw . camera_controls = ui_inline_radio ( camera_controls_handle , camera_controls_items , ui_align_t . LEFT );
2025-08-08 14:08:39 +02:00
let vars : map_t < string , string > = map_create ();
map_set ( vars , "rotate_shortcut" , map_get ( config_keymap , "action_rotate" ));
map_set ( vars , "zoom_shortcut" , map_get ( config_keymap , "action_zoom" ));
map_set ( vars , "pan_shortcut" , map_get ( config_keymap , "action_pan" ));
2025-10-15 18:39:55 +02:00
let orbit_and_rotate_tooltip : string = tr (
"Orbit and Rotate mode:\n{rotate_shortcut} or move right mouse button to rotate.\n{zoom_shortcut} or scroll to zoom.\n{pan_shortcut} or move middle mouse to pan." ,
vars );
let fly_tooltip : string = tr (
"Fly mode:\nHold the right mouse button and one of the following commands:\nmove mouse to rotate.\nw, up or scroll up to move forward.\ns, down or scroll down to move backward.\na or left to move left.\nd or right to move right.\ne to move up.\nq to move down.\nHold shift to move faster or alt to move slower." );
2025-08-08 14:08:39 +02:00
if ( ui . is_hovered ) {
ui_tooltip ( orbit_and_rotate_tooltip + "\n\n" + fly_tooltip );
}
2025-08-15 22:59:45 +02:00
ui_menu_align ();
2025-10-15 18:39:55 +02:00
let camera_type_items : string [] = [ tr ( "Perspective" ), tr ( "Orthographic" ) ];
context_raw . camera_type = ui_inline_radio ( context_raw . cam_handle , camera_type_items , ui_align_t . LEFT );
2025-08-08 14:08:39 +02:00
if ( ui . is_hovered ) {
ui_tooltip ( tr ( "Camera Type" ) + " (" + map_get ( config_keymap , "view_camera_type" ) + ")" );
}
if ( context_raw . cam_handle . changed ) {
viewport_update_camera_type ( context_raw . camera_type );
}
if ( ui . changed ) {
ui_menu_keep_open = true ;
}
}
else if ( ui_menubar_category == menubar_category_t . HELP ) {
if ( ui_menu_button ( tr ( "Manual" ))) {
2025-09-01 13:44:44 +02:00
iron_load_url ( manifest_url + "/manual" );
2025-08-08 14:08:39 +02:00
}
if ( ui_menu_button ( tr ( "How To" ))) {
2025-09-01 13:44:44 +02:00
iron_load_url ( manifest_url + "/howto" );
2025-08-08 14:08:39 +02:00
}
if ( ui_menu_button ( tr ( "What's New" ))) {
2025-09-01 13:44:44 +02:00
iron_load_url ( manifest_url + "/notes" );
2025-08-08 14:08:39 +02:00
}
if ( ui_menu_button ( tr ( "Issue Tracker" ))) {
2025-09-01 13:44:44 +02:00
iron_load_url ( "https://github.com/armory3d/armortools/issues" );
2025-08-08 14:08:39 +02:00
}
if ( ui_menu_button ( tr ( "Report Bug" ))) {
2025-10-15 18:39:55 +02:00
/// if (arm_macos || arm_ios) // Limited url length
iron_load_url ( "https://github.com/armory3d/armortools/issues/new?labels=bug&template=bug_report.md&body=*" + manifest_title + "%20" +
manifest_version + "-" + config_get_sha () + ",%20" + iron_system_id ());
/// else
iron_load_url ( "https://github.com/armory3d/armortools/issues/new?labels=bug&template=bug_report.md&body=*" + manifest_title + "%20" +
manifest_version + "-" + config_get_sha () + ",%20" + iron_system_id () +
"*%0A%0A**Issue description:**%0A%0A**Steps to reproduce:**%0A%0A" );
/// end
2025-08-08 14:08:39 +02:00
}
if ( ui_menu_button ( tr ( "Request Feature" ))) {
2025-10-15 18:39:55 +02:00
/// if (arm_macos || arm_ios) // Limited url length
iron_load_url ( "https://github.com/armory3d/armortools/issues/new?labels=feature%20request&template=feature_request.md&body=*" + manifest_title +
"%20" + manifest_version + "-" + config_get_sha () + ",%20" + iron_system_id ());
/// else
iron_load_url ( "https://github.com/armory3d/armortools/issues/new?labels=feature%20request&template=feature_request.md&body=*" + manifest_title +
"%20" + manifest_version + "-" + config_get_sha () + ",%20" + iron_system_id () + "*%0A%0A**Feature description:**%0A%0A" );
/// end
2025-08-08 14:08:39 +02:00
}
2025-08-15 22:59:45 +02:00
ui_menu_separator ();
2025-08-08 14:08:39 +02:00
if ( ui_menu_button ( tr ( "Check for Updates..." ))) {
2025-10-15 18:39:55 +02:00
/// if arm_android
2025-09-01 13:44:44 +02:00
iron_load_url ( manifest_url_android );
2025-10-15 18:39:55 +02:00
/// elseif arm_ios
2025-09-01 13:44:44 +02:00
iron_load_url ( manifest_url_ios );
2025-10-15 18:39:55 +02:00
/// else
2025-08-08 14:08:39 +02:00
// Retrieve latest version number
2025-10-15 18:39:55 +02:00
iron_file_download ( "https://server.armorpaint.org/" + to_lower_case ( manifest_title ) + ".html" , function ( url : string , buffer : buffer_t ) {
if ( buffer != null ) {
2025-08-08 14:08:39 +02:00
// Compare versions
let update : update_info_t = json_parse ( sys_buffer_to_string ( buffer ));
2025-10-15 18:39:55 +02:00
let update_version : i32 = math_floor ( update . version );
2025-08-08 14:08:39 +02:00
if ( update_version > 0 ) {
2025-10-15 18:39:55 +02:00
let date : string = config_get_date (); // 2019 -> 19
date = substring ( date , 2 , date . length );
2025-08-08 14:08:39 +02:00
let date_int : i32 = parse_int ( string_replace_all ( date , "-" , "" ));
if ( update_version > date_int ) {
let vars : map_t < string , string > = map_create ();
map_set ( vars , "url" , manifest_url );
ui_box_show_message ( tr ( "Update" ), tr ( "Update is available!\nPlease visit {url}." , vars ));
}
else {
ui_box_show_message ( tr ( "Update" ), tr ( "You are up to date!" ));
}
}
}
else {
let vars : map_t < string , string > = map_create ();
map_set ( vars , "url" , manifest_url );
ui_box_show_message ( tr ( "Update" ), tr ( "Unable to check for updates.\nPlease visit {url}." , vars ));
}
});
2025-10-15 18:39:55 +02:00
/// end
2025-08-08 14:08:39 +02:00
}
if ( ui_menu_button ( tr ( "About..." ))) {
let msg : string = manifest_title + ".org - v" + manifest_version + " (" + config_get_date () + ") - " + config_get_sha () + "\n" ;
msg += iron_system_id () + " - " + strings_graphics_api ();
2025-08-24 10:30:41 +02:00
let gpu : string = gpu_device_name ();
2025-08-08 14:08:39 +02:00
msg += "\n" + gpu ;
_ui_menu_render_msg = msg ;
2025-10-15 18:39:55 +02:00
ui_box_show_custom ( function () {
2025-08-08 14:08:39 +02:00
let tab_vertical : bool = config_raw . touch_ui ;
if ( ui_tab ( ui_handle ( __ID__ ), tr ( "About" ), tab_vertical )) {
let img : gpu_texture_t = data_get_image ( "badge.k" );
ui_image ( img );
2025-08-15 22:04:07 +02:00
ui_end_element ();
2025-08-08 14:08:39 +02:00
let h : ui_handle_t = ui_handle ( __ID__ );
if ( h . init ) {
h . text = _ui_menu_render_msg ;
}
ui_text_area ( h , ui_align_t . LEFT , false );
ui_row3 ();
2025-10-15 18:39:55 +02:00
/// if (arm_windows || arm_linux || arm_macos)
2025-08-08 14:08:39 +02:00
if ( ui_button ( tr ( "Copy" ))) {
iron_copy_to_clipboard ( _ui_menu_render_msg );
}
2025-10-15 18:39:55 +02:00
/// else
2025-08-15 22:04:07 +02:00
ui_end_element ();
2025-10-15 18:39:55 +02:00
/// end
2025-08-08 14:08:39 +02:00
if ( ui_button ( tr ( "Contributors" ))) {
2025-09-01 13:44:44 +02:00
iron_load_url ( "https://github.com/armory3d/armortools/graphs/contributors" );
2025-08-08 14:08:39 +02:00
}
if ( ui_button ( tr ( "OK" ))) {
ui_box_hide ();
}
}
}, 400 , 320 );
}
}
}
2025-08-15 22:59:45 +02:00
function ui_menubar_show_menu ( category : i32 ) {
2025-08-08 14:08:39 +02:00
if ( ui_menu_show && ui_menubar_category == category ) {
2024-09-11 16:32:12 +02:00
return ;
}
2025-10-15 18:39:55 +02:00
ui_menu_show = true ;
ui_menu_show_first = true ;
ui_menu_commands = ui_menubar_draw_category_items ;
2025-08-08 14:08:39 +02:00
ui_menubar_category = category ;
2025-08-21 12:00:03 +02:00
let panel_x : i32 = ui_menu_panel_x ();
let panel_y : i32 = ui_menu_panel_y ();
2025-10-15 18:39:55 +02:00
ui_menu_x = math_floor ( ui . _x - ui . _w ) + panel_x ;
ui_menu_y = math_floor ( ui_MENUBAR_H ( ui )) + panel_y ;
2024-03-11 15:10:18 +01:00
if ( config_raw . touch_ui ) {
2025-08-15 22:04:07 +02:00
let menu_w : i32 = math_floor ( base_default_element_w * UI_SCALE () * 2.0 );
2025-01-02 16:32:43 +01:00
ui_menu_x -= math_floor (( menu_w - ui . _w ) / 2 ) + math_floor ( ui_header_h / 2 );
2025-08-15 22:04:07 +02:00
ui_menu_x += math_floor ( 2 * UI_SCALE ());
ui_menu_y -= math_floor ( 2 * UI_SCALE ());
2024-03-11 15:10:18 +01:00
ui_menu_keep_open = true ;
}
}
2025-08-15 22:59:45 +02:00
function ui_menubar_icon_button ( i : i32 , j : i32 ) : bool {
2025-10-15 18:39:55 +02:00
let col : u32 = ui . ops . theme . WINDOW_BG_COL ;
let light : bool = col > 0xff666666 ;
let icon_accent : i32 = light ? 0xff666666 : 0xffaaaaaa ;
2025-06-19 19:55:16 +02:00
let img : gpu_texture_t = resource_get ( "icons.k" );
2025-10-15 18:39:55 +02:00
let rect : rect_t = resource_tile50 ( img , i , j );
2025-08-04 19:34:53 +02:00
return ui_sub_image ( img , icon_accent , - 1.0 , rect . x , rect . y , rect . w , rect . h ) == ui_state_t . RELEASED ;
2024-03-11 15:10:18 +01:00
}