ui_base.ts 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711
  1. let ui_base_show: bool = true;
  2. let ui_base_ui: zui_t;
  3. let ui_base_border_started: i32 = 0;
  4. let ui_base_border_handle_ptr: i32 = 0;
  5. let ui_base_action_paint_remap: string = "";
  6. let ui_base_operator_search_offset: i32 = 0;
  7. let ui_base_undo_tap_time: f32 = 0.0;
  8. let ui_base_redo_tap_time: f32 = 0.0;
  9. let ui_base_hwnds: zui_handle_t[] = ui_base_init_hwnds();
  10. let ui_base_htabs: zui_handle_t[] = ui_base_init_htabs();
  11. let ui_base_hwnd_tabs: any[] = ui_base_init_hwnd_tabs();
  12. ///if (is_paint || is_sculpt)
  13. let ui_base_default_sidebar_mini_w: i32 = 56;
  14. let ui_base_default_sidebar_full_w: i32 = 280;
  15. ///if (krom_android || krom_ios)
  16. let ui_base_default_sidebar_w: i32 = ui_base_default_sidebar_mini_w;
  17. ///else
  18. let ui_base_default_sidebar_w: i32 = ui_base_default_sidebar_full_w;
  19. ///end
  20. let ui_base_tabx: i32 = 0;
  21. let ui_base_hminimized: zui_handle_t = zui_handle_create();
  22. let ui_base_sidebar_mini_w: i32 = ui_base_default_sidebar_mini_w;
  23. ///end
  24. function ui_base_init_hwnds(): zui_handle_t[] {
  25. ///if is_paint
  26. return [zui_handle_create(), zui_handle_create(), zui_handle_create()];
  27. ///end
  28. ///if is_sculpt
  29. return [zui_handle_create(), zui_handle_create(), zui_handle_create()];
  30. ///end
  31. ///if is_lab
  32. return [zui_handle_create()];
  33. ///end
  34. }
  35. function ui_base_init_htabs(): zui_handle_t[] {
  36. ///if is_paint
  37. return [zui_handle_create(), zui_handle_create(), zui_handle_create()];
  38. ///end
  39. ///if is_sculpt
  40. return [zui_handle_create(), zui_handle_create(), zui_handle_create()];
  41. ///end
  42. ///if is_lab
  43. return [zui_handle_create()];
  44. ///end
  45. }
  46. function ui_base_init_hwnd_tabs(): any[] {
  47. ///if is_paint
  48. return [
  49. [tab_layers_draw, tab_history_draw, tab_plugins_draw
  50. ///if is_forge
  51. , tab_objects_draw
  52. ///end
  53. ],
  54. [tab_materials_draw, tab_brushes_draw, tab_particles_draw],
  55. [tab_browser_draw, tab_textures_draw, tab_meshes_draw, tab_fonts_draw, tab_swatches_draw, tab_script_draw, tab_console_draw, ui_status_draw_version_tab]
  56. ];
  57. ///end
  58. ///if is_sculpt
  59. return [
  60. [tab_layers_draw, tab_history_draw, tab_plugins_draw
  61. ///if is_forge
  62. , tab_objects_draw
  63. ///end
  64. ],
  65. [tab_materials_draw, tab_brushes_draw, tab_particles_draw],
  66. [tab_browser_draw, tab_textures_draw, tab_meshes_draw, tab_fonts_draw, tab_script_draw, tab_console_draw, ui_status_draw_version_tab]
  67. ];
  68. ///end
  69. ///if is_lab
  70. return [
  71. [tab_browser_draw, tab_textures_draw, tab_meshes_draw, tab_swatches_draw, tab_plugins_draw, tab_script_draw, tab_console_draw, ui_status_draw_version_tab]
  72. ];
  73. ///end
  74. }
  75. function ui_base_init() {
  76. ///if (is_paint || is_sculpt)
  77. ui_toolbar_init();
  78. ui_toolbar_w = math_floor(ui_toolbar_default_w * config_raw.window_scale);
  79. context_raw.text_tool_text = tr("Text");
  80. ///end
  81. ui_header_init();
  82. ui_status_init();
  83. ui_menubar_init();
  84. ui_header_h = math_floor(ui_header_default_h * config_raw.window_scale);
  85. ui_menubar_w = math_floor(ui_menubar_default_w * config_raw.window_scale);
  86. ///if (is_paint || is_sculpt)
  87. if (project_materials == null) {
  88. project_materials = [];
  89. let m: material_data_t = data_get_material("Scene", "Material");
  90. array_push(project_materials, slot_material_create(m));
  91. context_raw.material = project_materials[0];
  92. }
  93. if (project_brushes == null) {
  94. project_brushes = [];
  95. array_push(project_brushes, slot_brush_create());
  96. context_raw.brush = project_brushes[0];
  97. make_material_parse_brush();
  98. }
  99. if (project_fonts == null) {
  100. project_fonts = [];
  101. array_push(project_fonts, slot_font_create("default.ttf", base_font));
  102. context_raw.font = project_fonts[0];
  103. }
  104. if (project_layers == null) {
  105. project_layers = [];
  106. array_push(project_layers, slot_layer_create());
  107. context_raw.layer = project_layers[0];
  108. }
  109. ///end
  110. ///if is_lab
  111. if (project_material_data == null) {
  112. let m: material_data_t = data_get_material("Scene", "Material");
  113. project_material_data = m;
  114. }
  115. if (project_default_canvas == null) { // Synchronous
  116. let b: buffer_t = data_get_blob("default_brush.arm");
  117. project_default_canvas = b;
  118. }
  119. project_nodes = zui_nodes_create();
  120. project_canvas = armpack_decode(project_default_canvas);
  121. project_canvas.name = "Brush 1";
  122. context_parse_brush_inputs();
  123. parser_logic_parse(project_canvas);
  124. ///end
  125. if (project_raw.swatches == null) {
  126. project_set_default_swatches();
  127. context_raw.swatch = project_raw.swatches[0];
  128. }
  129. if (context_raw.empty_envmap == null) {
  130. let b: u8_array_t = u8_array_create(4);
  131. b[0] = 8;
  132. b[1] = 8;
  133. b[2] = 8;
  134. b[3] = 255;
  135. context_raw.empty_envmap = image_from_bytes(b.buffer, 1, 1);
  136. }
  137. if (context_raw.preview_envmap == null) {
  138. let b: u8_array_t = u8_array_create(4);
  139. b[0] = 0;
  140. b[1] = 0;
  141. b[2] = 0;
  142. b[3] = 255;
  143. context_raw.preview_envmap = image_from_bytes(b.buffer, 1, 1);
  144. }
  145. let world: world_data_t = scene_world;
  146. if (context_raw.saved_envmap == null) {
  147. // raw.savedEnvmap = world._envmap;
  148. context_raw.default_irradiance = world._.irradiance;
  149. context_raw.default_radiance = world._.radiance;
  150. context_raw.default_radiance_mipmaps = world._.radiance_mipmaps;
  151. }
  152. world._.envmap = context_raw.show_envmap ? context_raw.saved_envmap : context_raw.empty_envmap;
  153. context_raw.ddirty = 1;
  154. history_reset();
  155. let scale: f32 = config_raw.window_scale;
  156. let ops: zui_options_t = {
  157. theme: base_theme,
  158. font: base_font,
  159. scale_factor: scale,
  160. color_wheel: base_color_wheel,
  161. black_white_gradient: base_color_wheel_gradient
  162. };
  163. ui_base_ui = zui_create(ops);
  164. zui_set_on_border_hover(ui_base_on_border_hover);
  165. zui_set_on_text_hover(ui_base_on_text_hover);
  166. zui_set_on_deselect_text(ui_base_on_deselect_text);
  167. zui_set_on_tab_drop(ui_base_on_tab_drop);
  168. ///if (is_paint || is_sculpt)
  169. let resources: string[] = ["cursor.k", "icons.k"];
  170. ///end
  171. ///if is_lab
  172. let resources: string[] = ["cursor.k", "icons.k", "placeholder.k"];
  173. ///end
  174. ///if (is_paint || is_sculpt)
  175. context_raw.gizmo = scene_get_child(".Gizmo");
  176. context_raw.gizmo_translate_x = object_get_child(context_raw.gizmo, ".TranslateX");
  177. context_raw.gizmo_translate_y = object_get_child(context_raw.gizmo, ".TranslateY");
  178. context_raw.gizmo_translate_z = object_get_child(context_raw.gizmo, ".TranslateZ");
  179. context_raw.gizmo_scale_x = object_get_child(context_raw.gizmo, ".ScaleX");
  180. context_raw.gizmo_scale_y = object_get_child(context_raw.gizmo, ".ScaleY");
  181. context_raw.gizmo_scale_z = object_get_child(context_raw.gizmo, ".ScaleZ");
  182. context_raw.gizmo_rotate_x = object_get_child(context_raw.gizmo, ".RotateX");
  183. context_raw.gizmo_rotate_y = object_get_child(context_raw.gizmo, ".RotateY");
  184. context_raw.gizmo_rotate_z = object_get_child(context_raw.gizmo, ".RotateZ");
  185. ///end
  186. resource_load(resources);
  187. if (zui_SCALE(ui_base_ui) > 1) {
  188. ui_base_set_icon_scale();
  189. }
  190. context_raw.paint_object = scene_get_child(".Cube").ext;
  191. project_paint_objects = [context_raw.paint_object];
  192. if (project_filepath == "") {
  193. app_notify_on_init(base_init_layers);
  194. }
  195. context_raw.project_objects = [];
  196. for (let i: i32 = 0; i < scene_meshes.length; ++i) {
  197. let m: mesh_object_t = scene_meshes[i];
  198. array_push(context_raw.project_objects, m);
  199. }
  200. operator_register("view_top", ui_base_view_top);
  201. }
  202. function ui_base_update() {
  203. ui_base_update_ui();
  204. operator_update();
  205. let keys: string[] = map_keys(plugin_map);
  206. for (let i: i32 = 0; i < keys.length; ++i) {
  207. let p: plugin_t = map_get(plugin_map, keys[i]);
  208. if (p.update != null) {
  209. p.update();
  210. }
  211. }
  212. if (!base_ui_enabled) {
  213. return;
  214. }
  215. if (!ui_nodes_ui.is_typing && !ui_base_ui.is_typing) {
  216. if (operator_shortcut(map_get(config_keymap, "toggle_node_editor"))) {
  217. ///if (is_paint || is_sculpt)
  218. ui_nodes_canvas_type == canvas_type_t.MATERIAL ? ui_base_show_material_nodes() : ui_base_show_brush_nodes();
  219. ///end
  220. ///if is_lab
  221. ui_base_show_material_nodes();
  222. ///end
  223. }
  224. else if (operator_shortcut(map_get(config_keymap, "toggle_browser"))) {
  225. ui_base_toggle_browser();
  226. }
  227. else if (operator_shortcut(map_get(config_keymap, "toggle_2d_view"))) {
  228. ///if (is_paint || is_sculpt)
  229. ui_base_show_2d_view(view_2d_type_t.LAYER);
  230. ///else
  231. ui_base_show_2d_view(view_2d_type_t.ASSET);
  232. ///end
  233. }
  234. }
  235. if (operator_shortcut(map_get(config_keymap, "file_save_as"))) {
  236. project_save_as();
  237. }
  238. else if (operator_shortcut(map_get(config_keymap, "file_save"))) {
  239. project_save();
  240. }
  241. else if (operator_shortcut(map_get(config_keymap, "file_open"))) {
  242. project_open();
  243. }
  244. else if (operator_shortcut(map_get(config_keymap, "file_open_recent"))) {
  245. box_projects_show();
  246. }
  247. else if (operator_shortcut(map_get(config_keymap, "file_reimport_mesh"))) {
  248. project_reimport_mesh();
  249. }
  250. else if (operator_shortcut(map_get(config_keymap, "file_reimport_textures"))) {
  251. project_reimport_textures();
  252. }
  253. else if (operator_shortcut(map_get(config_keymap, "file_new"))) {
  254. project_new_box();
  255. }
  256. ///if (is_paint || is_lab)
  257. else if (operator_shortcut(map_get(config_keymap, "file_export_textures"))) {
  258. if (context_raw.texture_export_path == "") { // First export, ask for path
  259. ///if is_paint
  260. context_raw.layers_export = export_mode_t.VISIBLE;
  261. ///end
  262. box_export_show_textures();
  263. }
  264. else {
  265. app_notify_on_init(function () {
  266. export_texture_run(context_raw.texture_export_path);
  267. });
  268. }
  269. }
  270. else if (operator_shortcut(map_get(config_keymap, "file_export_textures_as"))) {
  271. ///if (is_paint || is_sculpt)
  272. context_raw.layers_export = export_mode_t.VISIBLE;
  273. ///end
  274. box_export_show_textures();
  275. }
  276. ///end
  277. else if (operator_shortcut(map_get(config_keymap, "file_import_assets"))) {
  278. project_import_asset();
  279. }
  280. else if (operator_shortcut(map_get(config_keymap, "edit_prefs"))) {
  281. box_preferences_show();
  282. }
  283. if (keyboard_started(map_get(config_keymap, "view_distract_free")) || (keyboard_started("escape") && !ui_base_show && !ui_box_show)) {
  284. ui_base_toggle_distract_free();
  285. }
  286. ///if krom_linux
  287. if (operator_shortcut("alt+enter", shortcut_type_t.STARTED)) {
  288. base_toggle_fullscreen();
  289. }
  290. ///end
  291. ///if (is_paint || is_sculpt)
  292. let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
  293. let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.DOWN);
  294. if ((context_raw.brush_can_lock || context_raw.brush_locked) && mouse_moved) {
  295. if (operator_shortcut(map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN) ||
  296. operator_shortcut(map_get(config_keymap, "brush_opacity"), shortcut_type_t.DOWN) ||
  297. operator_shortcut(map_get(config_keymap, "brush_angle"), shortcut_type_t.DOWN) ||
  298. (decal_mask && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN))) {
  299. if (context_raw.brush_locked) {
  300. if (operator_shortcut(map_get(config_keymap, "brush_opacity"), shortcut_type_t.DOWN)) {
  301. context_raw.brush_opacity += mouse_movement_x / 500;
  302. context_raw.brush_opacity = math_max(0.0, math_min(1.0, context_raw.brush_opacity));
  303. context_raw.brush_opacity = math_round(context_raw.brush_opacity * 100) / 100;
  304. context_raw.brush_opacity_handle.value = context_raw.brush_opacity;
  305. }
  306. else if (operator_shortcut(map_get(config_keymap, "brush_angle"), shortcut_type_t.DOWN)) {
  307. context_raw.brush_angle += mouse_movement_x / 5;
  308. context_raw.brush_angle = math_floor(context_raw.brush_angle) % 360;
  309. if (context_raw.brush_angle < 0) context_raw.brush_angle += 360;
  310. context_raw.brush_angle_handle.value = context_raw.brush_angle;
  311. make_material_parse_paint_material();
  312. }
  313. else if (decal_mask && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN)) {
  314. context_raw.brush_decal_mask_radius += mouse_movement_x / 150;
  315. context_raw.brush_decal_mask_radius = math_max(0.01, math_min(4.0, context_raw.brush_decal_mask_radius));
  316. context_raw.brush_decal_mask_radius = math_round(context_raw.brush_decal_mask_radius * 100) / 100;
  317. context_raw.brush_decal_mask_radius_handle.value = context_raw.brush_decal_mask_radius;
  318. }
  319. else {
  320. context_raw.brush_radius += mouse_movement_x / 150;
  321. context_raw.brush_radius = math_max(0.01, math_min(4.0, context_raw.brush_radius));
  322. context_raw.brush_radius = math_round(context_raw.brush_radius * 100) / 100;
  323. context_raw.brush_radius_handle.value = context_raw.brush_radius;
  324. }
  325. ui_header_handle.redraws = 2;
  326. }
  327. else if (context_raw.brush_can_lock) {
  328. context_raw.brush_can_lock = false;
  329. context_raw.brush_locked = true;
  330. }
  331. }
  332. }
  333. ///end
  334. ///if is_lab
  335. if ((context_raw.brush_can_lock || context_raw.brush_locked) && mouse_moved) {
  336. if (operator_shortcut(map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN)) {
  337. if (context_raw.brush_locked) {
  338. context_raw.brush_radius += mouse_movement_x / 150;
  339. context_raw.brush_radius = math_max(0.01, math_min(4.0, context_raw.brush_radius));
  340. context_raw.brush_radius = math_round(context_raw.brush_radius * 100) / 100;
  341. context_raw.brush_radius_handle.value = context_raw.brush_radius;
  342. ui_header_handle.redraws = 2;
  343. }
  344. else if (context_raw.brush_can_lock) {
  345. context_raw.brush_can_lock = false;
  346. context_raw.brush_locked = true;
  347. }
  348. }
  349. }
  350. ///end
  351. let is_typing: bool = ui_base_ui.is_typing || ui_view2d_ui.is_typing || ui_nodes_ui.is_typing;
  352. ///if (is_paint || is_sculpt)
  353. if (!is_typing) {
  354. if (operator_shortcut(map_get(config_keymap, "select_material"), shortcut_type_t.DOWN)) {
  355. ui_base_hwnds[tab_area_t.SIDEBAR1].redraws = 2;
  356. for (let i: i32 = 1; i < 10; ++i) {
  357. if (keyboard_started(i + "")) {
  358. context_select_material(i - 1);
  359. }
  360. }
  361. }
  362. else if (operator_shortcut(map_get(config_keymap, "select_layer"), shortcut_type_t.DOWN)) {
  363. ui_base_hwnds[tab_area_t.SIDEBAR0].redraws = 2;
  364. for (let i: i32 = 1; i < 10; ++i) {
  365. if (keyboard_started(i + "")) {
  366. context_select_layer(i - 1);
  367. }
  368. }
  369. }
  370. }
  371. ///end
  372. // Viewport shortcuts
  373. if (context_in_paint_area() && !is_typing) {
  374. ///if is_paint
  375. if (!mouse_down("right")) { // Fly mode off
  376. if (operator_shortcut(map_get(config_keymap, "tool_brush"))) {
  377. context_select_tool(workspace_tool_t.BRUSH);
  378. }
  379. else if (operator_shortcut(map_get(config_keymap, "tool_eraser"))) {
  380. context_select_tool(workspace_tool_t.ERASER);
  381. }
  382. else if (operator_shortcut(map_get(config_keymap, "tool_fill"))) {
  383. context_select_tool(workspace_tool_t.FILL);
  384. }
  385. else if (operator_shortcut(map_get(config_keymap, "tool_colorid"))) {
  386. context_select_tool(workspace_tool_t.COLORID);
  387. }
  388. else if (operator_shortcut(map_get(config_keymap, "tool_decal"))) {
  389. context_select_tool(workspace_tool_t.DECAL);
  390. }
  391. else if (operator_shortcut(map_get(config_keymap, "tool_text"))) {
  392. context_select_tool(workspace_tool_t.TEXT);
  393. }
  394. else if (operator_shortcut(map_get(config_keymap, "tool_clone"))) {
  395. context_select_tool(workspace_tool_t.CLONE);
  396. }
  397. else if (operator_shortcut(map_get(config_keymap, "tool_blur"))) {
  398. context_select_tool(workspace_tool_t.BLUR);
  399. }
  400. else if (operator_shortcut(map_get(config_keymap, "tool_smudge"))) {
  401. context_select_tool(workspace_tool_t.SMUDGE);
  402. }
  403. else if (operator_shortcut(map_get(config_keymap, "tool_particle"))) {
  404. context_select_tool(workspace_tool_t.PARTICLE);
  405. }
  406. else if (operator_shortcut(map_get(config_keymap, "tool_picker"))) {
  407. context_select_tool(workspace_tool_t.PICKER);
  408. }
  409. else if (operator_shortcut(map_get(config_keymap, "tool_bake"))) {
  410. context_select_tool(workspace_tool_t.BAKE);
  411. }
  412. else if (operator_shortcut(map_get(config_keymap, "tool_gizmo"))) {
  413. context_select_tool(workspace_tool_t.GIZMO);
  414. }
  415. else if (operator_shortcut(map_get(config_keymap, "tool_material"))) {
  416. context_select_tool(workspace_tool_t.MATERIAL);
  417. }
  418. else if (operator_shortcut(map_get(config_keymap, "swap_brush_eraser"))) {
  419. context_select_tool(context_raw.tool == workspace_tool_t.BRUSH ? workspace_tool_t.ERASER : workspace_tool_t.BRUSH);
  420. }
  421. }
  422. // Radius
  423. if (context_raw.tool == workspace_tool_t.BRUSH ||
  424. context_raw.tool == workspace_tool_t.ERASER ||
  425. context_raw.tool == workspace_tool_t.DECAL ||
  426. context_raw.tool == workspace_tool_t.TEXT ||
  427. context_raw.tool == workspace_tool_t.CLONE ||
  428. context_raw.tool == workspace_tool_t.BLUR ||
  429. context_raw.tool == workspace_tool_t.SMUDGE ||
  430. context_raw.tool == workspace_tool_t.PARTICLE) {
  431. if (operator_shortcut(map_get(config_keymap, "brush_radius")) ||
  432. operator_shortcut(map_get(config_keymap, "brush_opacity")) ||
  433. operator_shortcut(map_get(config_keymap, "brush_angle")) ||
  434. (decal_mask && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius")))) {
  435. context_raw.brush_can_lock = true;
  436. if (!pen_connected) {
  437. mouse_lock();
  438. }
  439. context_raw.lock_started_x = mouse_x;
  440. context_raw.lock_started_y = mouse_y;
  441. }
  442. else if (operator_shortcut(map_get(config_keymap, "brush_radius_decrease"), shortcut_type_t.REPEAT)) {
  443. context_raw.brush_radius -= ui_base_get_radius_increment();
  444. context_raw.brush_radius = math_max(math_round(context_raw.brush_radius * 100) / 100, 0.01);
  445. context_raw.brush_radius_handle.value = context_raw.brush_radius;
  446. ui_header_handle.redraws = 2;
  447. }
  448. else if (operator_shortcut(map_get(config_keymap, "brush_radius_increase"), shortcut_type_t.REPEAT)) {
  449. context_raw.brush_radius += ui_base_get_radius_increment();
  450. context_raw.brush_radius = math_round(context_raw.brush_radius * 100) / 100;
  451. context_raw.brush_radius_handle.value = context_raw.brush_radius;
  452. ui_header_handle.redraws = 2;
  453. }
  454. else if (decal_mask) {
  455. if (operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius_decrease"), shortcut_type_t.REPEAT)) {
  456. context_raw.brush_decal_mask_radius -= ui_base_get_radius_increment();
  457. context_raw.brush_decal_mask_radius = math_max(math_round(context_raw.brush_decal_mask_radius * 100) / 100, 0.01);
  458. context_raw.brush_decal_mask_radius_handle.value = context_raw.brush_decal_mask_radius;
  459. ui_header_handle.redraws = 2;
  460. }
  461. else if (operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius_increase"), shortcut_type_t.REPEAT)) {
  462. context_raw.brush_decal_mask_radius += ui_base_get_radius_increment();
  463. context_raw.brush_decal_mask_radius = math_round(context_raw.brush_decal_mask_radius * 100) / 100;
  464. context_raw.brush_decal_mask_radius_handle.value = context_raw.brush_decal_mask_radius;
  465. ui_header_handle.redraws = 2;
  466. }
  467. }
  468. }
  469. if (decal_mask && (operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.STARTED) || operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.RELEASED))) {
  470. ui_header_handle.redraws = 2;
  471. }
  472. ///end
  473. ///if is_lab
  474. if (ui_header_worktab.position == space_type_t.SPACE3D) {
  475. // Radius
  476. if (context_raw.tool == workspace_tool_t.ERASER ||
  477. context_raw.tool == workspace_tool_t.CLONE ||
  478. context_raw.tool == workspace_tool_t.BLUR ||
  479. context_raw.tool == workspace_tool_t.SMUDGE) {
  480. if (operator_shortcut(map_get(config_keymap, "brush_radius"))) {
  481. context_raw.brush_can_lock = true;
  482. if (!pen_connected) {
  483. mouse_lock();
  484. }
  485. context_raw.lock_started_x = mouse_x;
  486. context_raw.lock_started_y = mouse_y;
  487. }
  488. else if (operator_shortcut(map_get(config_keymap, "brush_radius_decrease"), shortcut_type_t.REPEAT)) {
  489. context_raw.brush_radius -= ui_base_get_radius_increment();
  490. context_raw.brush_radius = math_max(math_round(context_raw.brush_radius * 100) / 100, 0.01);
  491. context_raw.brush_radius_handle.value = context_raw.brush_radius;
  492. ui_header_handle.redraws = 2;
  493. }
  494. else if (operator_shortcut(map_get(config_keymap, "brush_radius_increase"), shortcut_type_t.REPEAT)) {
  495. context_raw.brush_radius += ui_base_get_radius_increment();
  496. context_raw.brush_radius = math_round(context_raw.brush_radius * 100) / 100;
  497. context_raw.brush_radius_handle.value = context_raw.brush_radius;
  498. ui_header_handle.redraws = 2;
  499. }
  500. }
  501. }
  502. ///end
  503. // Viewpoint
  504. if (mouse_view_x() < app_w()) {
  505. if (operator_shortcut(map_get(config_keymap, "view_reset"))) {
  506. viewport_reset();
  507. viewport_scale_to_bounds();
  508. }
  509. else if (operator_shortcut(map_get(config_keymap, "view_back"))) {
  510. viewport_set_view(0, 1, 0, math_pi() / 2, 0, math_pi());
  511. }
  512. else if (operator_shortcut(map_get(config_keymap, "view_front"))) {
  513. viewport_set_view(0, -1, 0, math_pi() / 2, 0, 0);
  514. }
  515. else if (operator_shortcut(map_get(config_keymap, "view_left"))) {
  516. viewport_set_view(-1, 0, 0, math_pi() / 2, 0, -math_pi() / 2);
  517. }
  518. else if (operator_shortcut(map_get(config_keymap, "view_right"))) {
  519. viewport_set_view(1, 0, 0, math_pi() / 2, 0, math_pi() / 2);
  520. }
  521. else if (operator_shortcut(map_get(config_keymap, "view_bottom"))) {
  522. viewport_set_view(0, 0, -1, math_pi(), 0, math_pi());
  523. }
  524. else if (operator_shortcut(map_get(config_keymap, "view_camera_type"))) {
  525. context_raw.camera_type = context_raw.camera_type == camera_type_t.PERSPECTIVE ? camera_type_t.ORTHOGRAPHIC : camera_type_t.PERSPECTIVE;
  526. context_raw.cam_handle.position = context_raw.camera_type;
  527. viewport_update_camera_type(context_raw.camera_type);
  528. }
  529. else if (operator_shortcut(map_get(config_keymap, "view_orbit_left"), shortcut_type_t.REPEAT)) {
  530. viewport_orbit(-math_pi() / 12, 0);
  531. }
  532. else if (operator_shortcut(map_get(config_keymap, "view_orbit_right"), shortcut_type_t.REPEAT)) {
  533. viewport_orbit(math_pi() / 12, 0);
  534. }
  535. else if (operator_shortcut(map_get(config_keymap, "view_orbit_up"), shortcut_type_t.REPEAT)) {
  536. viewport_orbit(0, -math_pi() / 12);
  537. }
  538. else if (operator_shortcut(map_get(config_keymap, "view_orbit_down"), shortcut_type_t.REPEAT)) {
  539. viewport_orbit(0, math_pi() / 12);
  540. }
  541. else if (operator_shortcut(map_get(config_keymap, "view_orbit_opposite"))) {
  542. viewport_orbit_opposite();
  543. }
  544. else if (operator_shortcut(map_get(config_keymap, "view_zoom_in"), shortcut_type_t.REPEAT)) {
  545. viewport_zoom(0.2);
  546. }
  547. else if (operator_shortcut(map_get(config_keymap, "view_zoom_out"), shortcut_type_t.REPEAT)) {
  548. viewport_zoom(-0.2);
  549. }
  550. else if (operator_shortcut(map_get(config_keymap, "viewport_mode"))) {
  551. let count: i32;
  552. ///if (is_paint || is_sculpt)
  553. count = 16;
  554. ///if (krom_direct3d12 || krom_vulkan || krom_metal)
  555. count += 1;
  556. ///end
  557. ///end
  558. ///if is_lab
  559. count = 9;
  560. ///if (krom_direct3d12 || krom_vulkan || krom_metal)
  561. count += 1;
  562. ///end
  563. ///end
  564. ui_menu_draw(function (ui: zui_t) {
  565. let mode_handle: zui_handle_t = zui_handle(__ID__);
  566. mode_handle.position = context_raw.viewport_mode;
  567. zui_text(tr("Viewport Mode"), zui_align_t.RIGHT, ui.ops.theme.HIGHLIGHT_COL);
  568. let modes: string[] = [
  569. tr("Lit"),
  570. tr("Base Color"),
  571. tr("Normal"),
  572. tr("Occlusion"),
  573. tr("Roughness"),
  574. tr("Metallic"),
  575. tr("Opacity"),
  576. tr("Height"),
  577. ///if (is_paint || is_sculpt)
  578. tr("Emission"),
  579. tr("Subsurface"),
  580. tr("TexCoord"),
  581. tr("Object Normal"),
  582. tr("Material ID"),
  583. tr("Object ID"),
  584. tr("Mask")
  585. ///end
  586. ];
  587. let shortcuts: string[] = ["l", "b", "n", "o", "r", "m", "a", "h", "e", "s", "t", "1", "2", "3", "4"];
  588. ///if (krom_direct3d12 || krom_vulkan || krom_metal)
  589. if (krom_raytrace_supported()) {
  590. array_push(modes, tr("Path Traced"));
  591. array_push(shortcuts, "p");
  592. }
  593. ///end
  594. for (let i: i32 = 0; i < modes.length; ++i) {
  595. zui_radio(mode_handle, i, modes[i], shortcuts[i]);
  596. }
  597. let index: i32 = array_index_of(shortcuts, keyboard_key_code(ui.key));
  598. if (ui.is_key_pressed && index != -1) {
  599. mode_handle.position = index;
  600. ui.changed = true;
  601. context_set_viewport_mode(mode_handle.position);
  602. }
  603. else if (mode_handle.changed) {
  604. context_set_viewport_mode(mode_handle.position);
  605. ui.changed = true;
  606. }
  607. }, count);
  608. }
  609. }
  610. if (operator_shortcut(map_get(config_keymap, "operator_search"))) ui_base_operator_search();
  611. }
  612. if (context_raw.brush_can_lock || context_raw.brush_locked) {
  613. if (mouse_moved && context_raw.brush_can_unlock) {
  614. context_raw.brush_locked = false;
  615. context_raw.brush_can_unlock = false;
  616. }
  617. ///if (is_paint || is_sculpt)
  618. let b: bool = (context_raw.brush_can_lock || context_raw.brush_locked) &&
  619. !operator_shortcut(map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN) &&
  620. !operator_shortcut(map_get(config_keymap, "brush_opacity"), shortcut_type_t.DOWN) &&
  621. !operator_shortcut(map_get(config_keymap, "brush_angle"), shortcut_type_t.DOWN) &&
  622. !(decal_mask && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN));
  623. ///end
  624. ///if is_lab
  625. let b: bool = (context_raw.brush_can_lock || context_raw.brush_locked) &&
  626. !operator_shortcut(map_get(config_keymap, "brush_radius"), shortcut_type_t.DOWN);
  627. ///end
  628. if (b) {
  629. mouse_unlock();
  630. context_raw.last_paint_x = -1;
  631. context_raw.last_paint_y = -1;
  632. if (context_raw.brush_can_lock) {
  633. context_raw.brush_can_lock = false;
  634. context_raw.brush_can_unlock = false;
  635. context_raw.brush_locked = false;
  636. }
  637. else {
  638. context_raw.brush_can_unlock = true;
  639. }
  640. }
  641. }
  642. ///if (is_paint || is_sculpt)
  643. if (ui_base_border_handle_ptr != 0) {
  644. if (ui_base_border_handle_ptr == ui_nodes_hwnd.ptr || ui_base_border_handle_ptr == ui_view2d_hwnd.ptr) {
  645. if (ui_base_border_started == border_side_t.LEFT) {
  646. config_raw.layout[layout_size_t.NODES_W] -= math_floor(mouse_movement_x);
  647. if (config_raw.layout[layout_size_t.NODES_W] < 32) {
  648. config_raw.layout[layout_size_t.NODES_W] = 32;
  649. }
  650. else if (config_raw.layout[layout_size_t.NODES_W] > sys_width() * 0.7) {
  651. config_raw.layout[layout_size_t.NODES_W] = math_floor(sys_width() * 0.7);
  652. }
  653. }
  654. else { // UINodes / UIView2D ratio
  655. config_raw.layout[layout_size_t.NODES_H] -= math_floor(mouse_movement_y);
  656. if (config_raw.layout[layout_size_t.NODES_H] < 32) {
  657. config_raw.layout[layout_size_t.NODES_H] = 32;
  658. }
  659. else if (config_raw.layout[layout_size_t.NODES_H] > app_h() * 0.95) {
  660. config_raw.layout[layout_size_t.NODES_H] = math_floor(app_h() * 0.95);
  661. }
  662. }
  663. }
  664. else if (ui_base_border_handle_ptr == ui_base_hwnds[tab_area_t.STATUS].ptr) {
  665. let my: i32 = math_floor(mouse_movement_y);
  666. if (config_raw.layout[layout_size_t.STATUS_H] - my >= ui_status_default_status_h * config_raw.window_scale && config_raw.layout[layout_size_t.STATUS_H] - my < sys_height() * 0.7) {
  667. config_raw.layout[layout_size_t.STATUS_H] -= my;
  668. }
  669. }
  670. else {
  671. if (ui_base_border_started == border_side_t.LEFT) {
  672. config_raw.layout[layout_size_t.SIDEBAR_W] -= math_floor(mouse_movement_x);
  673. if (config_raw.layout[layout_size_t.SIDEBAR_W] < ui_base_sidebar_mini_w) {
  674. config_raw.layout[layout_size_t.SIDEBAR_W] = ui_base_sidebar_mini_w;
  675. }
  676. else if (config_raw.layout[layout_size_t.SIDEBAR_W] > sys_width() - ui_base_sidebar_mini_w) {
  677. config_raw.layout[layout_size_t.SIDEBAR_W] = sys_width() - ui_base_sidebar_mini_w;
  678. }
  679. }
  680. else {
  681. let my: i32 = math_floor(mouse_movement_y);
  682. if (ui_base_border_handle_ptr == ui_base_hwnds[tab_area_t.SIDEBAR1].ptr && ui_base_border_started == border_side_t.TOP) {
  683. if (config_raw.layout[layout_size_t.SIDEBAR_H0] + my > 32 && config_raw.layout[layout_size_t.SIDEBAR_H1] - my > 32) {
  684. config_raw.layout[layout_size_t.SIDEBAR_H0] += my;
  685. config_raw.layout[layout_size_t.SIDEBAR_H1] -= my;
  686. }
  687. }
  688. }
  689. }
  690. }
  691. ///end
  692. ///if is_lab
  693. if (ui_base_border_handle_ptr != 0) {
  694. if (ui_base_border_handle_ptr == ui_nodes_hwnd.ptr || ui_base_border_handle_ptr == ui_view2d_hwnd.ptr) {
  695. if (ui_base_border_started == border_side_t.LEFT) {
  696. config_raw.layout[layout_size_t.NODES_W] -= math_floor(mouse_movement_x);
  697. if (config_raw.layout[layout_size_t.NODES_W] < 32) {
  698. config_raw.layout[layout_size_t.NODES_W] = 32;
  699. }
  700. else if (config_raw.layout[layout_size_t.NODES_W] > sys_width() * 0.7) {
  701. config_raw.layout[layout_size_t.NODES_W] = math_floor(sys_width() * 0.7);
  702. }
  703. }
  704. else { // UINodes / UIView2D ratio
  705. config_raw.layout[layout_size_t.NODES_H] -= math_floor(mouse_movement_y);
  706. if (config_raw.layout[layout_size_t.NODES_H] < 32) {
  707. config_raw.layout[layout_size_t.NODES_H] = 32;
  708. }
  709. else if (config_raw.layout[layout_size_t.NODES_H] > app_h() * 0.95) {
  710. config_raw.layout[layout_size_t.NODES_H] = math_floor(app_h() * 0.95);
  711. }
  712. }
  713. }
  714. else if (ui_base_border_handle_ptr == ui_base_hwnds[tab_area_t.STATUS].ptr) {
  715. let my: i32 = math_floor(mouse_movement_y);
  716. if (config_raw.layout[layout_size_t.STATUS_H] - my >= ui_status_default_status_h * config_raw.window_scale && config_raw.layout[layout_size_t.STATUS_H] - my < sys_height() * 0.7) {
  717. config_raw.layout[layout_size_t.STATUS_H] -= my;
  718. }
  719. }
  720. }
  721. ///end
  722. if (!mouse_down()) {
  723. ui_base_border_handle_ptr = 0;
  724. base_is_resizing = false;
  725. }
  726. ///if arm_physics
  727. if (context_raw.tool == workspace_tool_t.PARTICLE && context_raw.particle_physics && context_in_paint_area() && !context_raw.paint2d) {
  728. util_particle_init_physics();
  729. let world: physics_world_t = physics_world_active;
  730. physics_world_late_update(world);
  731. context_raw.ddirty = 2;
  732. context_raw.rdirty = 2;
  733. if (mouse_started()) {
  734. if (context_raw.particle_timer != null) {
  735. tween_stop(context_raw.particle_timer);
  736. context_raw.particle_timer.done();
  737. context_raw.particle_timer = null;
  738. }
  739. history_push_undo = true;
  740. context_raw.particle_hit_x = context_raw.particle_hit_y = context_raw.particle_hit_z = 0;
  741. let o: object_t = scene_spawn_object(".Sphere");
  742. let md: material_data_t = data_get_material("Scene", ".Gizmo");
  743. let mo: mesh_object_t = o.ext;
  744. mo.base.name = ".Bullet";
  745. mo.materials[0] = md;
  746. mo.base.visible = true;
  747. let camera: camera_object_t = scene_camera;
  748. let ct: transform_t = camera.base.transform;
  749. vec4_set(mo.base.transform.loc, transform_world_x(ct), transform_world_y(ct), transform_world_z(ct));
  750. vec4_set(mo.base.transform.scale, context_raw.brush_radius * 0.2, context_raw.brush_radius * 0.2, context_raw.brush_radius * 0.2);
  751. transform_build_matrix(mo.base.transform);
  752. let body: physics_body_t = physics_body_create();
  753. body.shape = shape_type_t.SPHERE;
  754. physics_body_set_mass(body, 1.0);
  755. body.ccd = true;
  756. mo.base.transform.radius /= 10; // Lower ccd radius
  757. physics_body_init(body, mo.base);
  758. map_set(physics_body_object_map, mo.base, body);
  759. mo.base.transform.radius *= 10;
  760. let ray: ray_t = raycast_get_ray(mouse_view_x(), mouse_view_y(), camera);
  761. physics_body_apply_impulse(body, vec4_mult(ray.dir, 0.15));
  762. context_raw.particle_timer = tween_timer(5, function () {
  763. mesh_object_remove(mo);
  764. });
  765. }
  766. let pairs: pair_t[] = physics_world_get_contact_pairs(world, context_raw.paint_body);
  767. if (pairs != null) {
  768. for (let i: i32 = 0; i < pairs.length; ++i) {
  769. let p: pair_t = pairs[i];
  770. context_raw.last_particle_hit_x = context_raw.particle_hit_x != 0 ? context_raw.particle_hit_x : p.pos_a.x;
  771. context_raw.last_particle_hit_y = context_raw.particle_hit_y != 0 ? context_raw.particle_hit_y : p.pos_a.y;
  772. context_raw.last_particle_hit_z = context_raw.particle_hit_z != 0 ? context_raw.particle_hit_z : p.pos_a.z;
  773. context_raw.particle_hit_x = p.pos_a.x;
  774. context_raw.particle_hit_y = p.pos_a.y;
  775. context_raw.particle_hit_z = p.pos_a.z;
  776. context_raw.pdirty = 1;
  777. break; // 1 pair for now
  778. }
  779. }
  780. }
  781. ///end
  782. }
  783. function ui_base_view_top() {
  784. let is_typing: bool = ui_base_ui.is_typing || ui_view2d_ui.is_typing || ui_nodes_ui.is_typing;
  785. if (context_in_paint_area() && !is_typing) {
  786. if (mouse_view_x() < app_w()) {
  787. viewport_set_view(0, 0, 1, 0, 0, 0);
  788. }
  789. }
  790. }
  791. let _ui_base_operator_search_first: bool;
  792. function ui_base_operator_search() {
  793. _ui_base_operator_search_first = true;
  794. ui_menu_draw(function (ui: zui_t) {
  795. let search_handle: zui_handle_t = zui_handle(__ID__);
  796. zui_fill(0, 0, ui._w / zui_SCALE(ui), ui.ops.theme.ELEMENT_H * 8, ui.ops.theme.SEPARATOR_COL);
  797. let search: string = zui_text_input(search_handle, "", zui_align_t.LEFT, true, true);
  798. ui.changed = false;
  799. if (_ui_base_operator_search_first) {
  800. _ui_base_operator_search_first = false;
  801. search_handle.text = "";
  802. zui_start_text_edit(search_handle); // Focus search bar
  803. }
  804. if (search_handle.changed) {
  805. ui_base_operator_search_offset = 0;
  806. }
  807. if (ui.is_key_pressed) { // Move selection
  808. if (ui.key == key_code_t.DOWN && ui_base_operator_search_offset < 6) {
  809. ui_base_operator_search_offset++;
  810. }
  811. if (ui.key == key_code_t.UP && ui_base_operator_search_offset > 0) {
  812. ui_base_operator_search_offset--;
  813. }
  814. }
  815. let enter: bool = keyboard_down("enter");
  816. let count: i32 = 0;
  817. let BUTTON_COL: i32 = ui.ops.theme.BUTTON_COL;
  818. let keys: string[] = map_keys(config_keymap);
  819. for (let i: i32 = 0; i < keys.length; ++i) {
  820. let n: string = keys[i];
  821. if (string_index_of(n, search) >= 0) {
  822. ui.ops.theme.BUTTON_COL = count == ui_base_operator_search_offset ? ui.ops.theme.HIGHLIGHT_COL : ui.ops.theme.SEPARATOR_COL;
  823. if (zui_button(n, zui_align_t.LEFT, map_get(config_keymap, n)) || (enter && count == ui_base_operator_search_offset)) {
  824. if (enter) {
  825. ui.changed = true;
  826. count = 6; // Trigger break
  827. }
  828. operator_run(n);
  829. }
  830. if (++count > 6) {
  831. break;
  832. }
  833. }
  834. }
  835. if (enter && count == 0) { // Hide popup on enter when command is not found
  836. ui.changed = true;
  837. search_handle.text = "";
  838. }
  839. ui.ops.theme.BUTTON_COL = BUTTON_COL;
  840. }, 8, -1, -1);
  841. }
  842. function ui_base_toggle_distract_free() {
  843. ui_base_show = !ui_base_show;
  844. base_resize();
  845. }
  846. function ui_base_get_radius_increment(): f32 {
  847. return 0.1;
  848. }
  849. function ui_base_hit_rect(mx: f32, my: f32, x: i32, y: i32, w: i32, h: i32) {
  850. return mx > x && mx < x + w && my > y && my < y + h;
  851. }
  852. ///if (is_paint || is_sculpt)
  853. function ui_base_get_brush_stencil_rect(): rect_t {
  854. let w: i32 = math_floor(context_raw.brush_stencil_image.width * (base_h() / context_raw.brush_stencil_image.height) * context_raw.brush_stencil_scale);
  855. let h: i32 = math_floor(base_h() * context_raw.brush_stencil_scale);
  856. let x: i32 = math_floor(base_x() + context_raw.brush_stencil_x * base_w());
  857. let y: i32 = math_floor(base_y() + context_raw.brush_stencil_y * base_h());
  858. return { w: w, h: h, x: x, y: y };
  859. }
  860. ///end
  861. function ui_base_update_ui() {
  862. if (console_message_timer > 0) {
  863. console_message_timer -= time_delta();
  864. if (console_message_timer <= 0) {
  865. ui_base_hwnds[tab_area_t.STATUS].redraws = 2;
  866. }
  867. }
  868. ///if (is_paint || is_sculpt)
  869. ui_base_sidebar_mini_w = math_floor(ui_base_default_sidebar_mini_w * zui_SCALE(ui_base_ui));
  870. ///end
  871. if (!base_ui_enabled) {
  872. return;
  873. }
  874. ///if (is_paint || is_sculpt)
  875. // Same mapping for paint and rotate (predefined in touch keymap)
  876. if (context_in_viewport()) {
  877. if (mouse_started() && map_get(config_keymap, "action_paint") == map_get(config_keymap, "action_rotate")) {
  878. ui_base_action_paint_remap = map_get(config_keymap, "action_paint");
  879. util_render_pick_pos_nor_tex();
  880. let is_mesh: bool = math_abs(context_raw.posx_picked) < 50 && math_abs(context_raw.posy_picked) < 50 && math_abs(context_raw.posz_picked) < 50;
  881. ///if krom_android
  882. // Allow rotating with both pen and touch, because hovering a pen prevents touch input on android
  883. let pen_only: bool = false;
  884. ///else
  885. let pen_only: bool = context_raw.pen_painting_only;
  886. ///end
  887. let is_pen: bool = pen_only && pen_down();
  888. // Mesh picked - disable rotate
  889. // Pen painting only - rotate with touch, paint with pen
  890. if ((is_mesh && !pen_only) || is_pen) {
  891. map_set(config_keymap, "action_rotate", "");
  892. map_set(config_keymap, "action_paint", ui_base_action_paint_remap);
  893. }
  894. // World sphere picked - disable paint
  895. else {
  896. map_set(config_keymap, "action_paint", "");
  897. map_set(config_keymap, "action_rotate", ui_base_action_paint_remap);
  898. }
  899. }
  900. else if (!mouse_down() && ui_base_action_paint_remap != "") {
  901. map_set(config_keymap, "action_rotate", ui_base_action_paint_remap);
  902. map_set(config_keymap, "action_paint", ui_base_action_paint_remap);
  903. ui_base_action_paint_remap = "";
  904. }
  905. }
  906. if (context_raw.brush_stencil_image != null && operator_shortcut(map_get(config_keymap, "stencil_transform"), shortcut_type_t.DOWN)) {
  907. let r: rect_t = ui_base_get_brush_stencil_rect();
  908. if (mouse_started("left")) {
  909. context_raw.brush_stencil_scaling =
  910. ui_base_hit_rect(mouse_x, mouse_y, r.x - 8, r.y - 8, 16, 16) ||
  911. ui_base_hit_rect(mouse_x, mouse_y, r.x - 8, r.h + r.y - 8, 16, 16) ||
  912. ui_base_hit_rect(mouse_x, mouse_y, r.w + r.x - 8, r.y - 8, 16, 16) ||
  913. ui_base_hit_rect(mouse_x, mouse_y, r.w + r.x - 8, r.h + r.y - 8, 16, 16);
  914. let cosa: f32 = math_cos(-context_raw.brush_stencil_angle);
  915. let sina: f32 = math_sin(-context_raw.brush_stencil_angle);
  916. let ox: f32 = 0;
  917. let oy: f32 = -r.h / 2;
  918. let x: f32 = ox * cosa - oy * sina;
  919. let y: f32 = ox * sina + oy * cosa;
  920. x += r.x + r.w / 2;
  921. y += r.y + r.h / 2;
  922. context_raw.brush_stencil_rotating =
  923. ui_base_hit_rect(mouse_x, mouse_y, math_floor(x - 16), math_floor(y - 16), 32, 32);
  924. }
  925. let _scale: f32 = context_raw.brush_stencil_scale;
  926. if (mouse_down("left")) {
  927. if (context_raw.brush_stencil_scaling) {
  928. let mult: i32 = mouse_x > r.x + r.w / 2 ? 1 : -1;
  929. context_raw.brush_stencil_scale += mouse_movement_x / 400 * mult;
  930. }
  931. else if (context_raw.brush_stencil_rotating) {
  932. let gizmo_x: f32 = r.x + r.w / 2;
  933. let gizmo_y: f32 = r.y + r.h / 2;
  934. context_raw.brush_stencil_angle = -math_atan2(mouse_y - gizmo_y, mouse_x - gizmo_x) - math_pi() / 2;
  935. }
  936. else {
  937. context_raw.brush_stencil_x += mouse_movement_x / base_w();
  938. context_raw.brush_stencil_y += mouse_movement_y / base_h();
  939. }
  940. }
  941. else {
  942. context_raw.brush_stencil_scaling = false;
  943. }
  944. if (mouse_wheel_delta != 0) {
  945. context_raw.brush_stencil_scale -= mouse_wheel_delta / 10;
  946. }
  947. // Center after scale
  948. let ratio: f32 = base_h() / context_raw.brush_stencil_image.height;
  949. let old_w: f32 = _scale * context_raw.brush_stencil_image.width * ratio;
  950. let new_w: f32 = context_raw.brush_stencil_scale * context_raw.brush_stencil_image.width * ratio;
  951. let old_h: f32 = _scale * base_h();
  952. let new_h: f32 = context_raw.brush_stencil_scale * base_h();
  953. context_raw.brush_stencil_x += (old_w - new_w) / base_w() / 2;
  954. context_raw.brush_stencil_y += (old_h - new_h) / base_h() / 2;
  955. }
  956. ///end
  957. let set_clone_source: bool = context_raw.tool == workspace_tool_t.CLONE && operator_shortcut(map_get(config_keymap, "set_clone_source") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
  958. ///if (is_paint || is_sculpt)
  959. let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
  960. let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
  961. let down: bool = operator_shortcut(map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
  962. decal_mask ||
  963. set_clone_source ||
  964. operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
  965. (pen_down() && !keyboard_down("alt"));
  966. ///end
  967. ///if is_lab
  968. let down: bool = operator_shortcut(map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
  969. set_clone_source ||
  970. operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
  971. (pen_down() && !keyboard_down("alt"));
  972. ///end
  973. if (config_raw.touch_ui) {
  974. if (pen_down()) {
  975. context_raw.pen_painting_only = true;
  976. }
  977. else if (context_raw.pen_painting_only) {
  978. down = false;
  979. }
  980. }
  981. ///if arm_physics
  982. if (context_raw.tool == workspace_tool_t.PARTICLE && context_raw.particle_physics) {
  983. down = false;
  984. }
  985. ///end
  986. ///if (is_paint || is_sculpt)
  987. ///if krom_ios
  988. // No hover on iPad, decals are painted by pen release
  989. if (decal) {
  990. down = pen_released();
  991. if (!context_raw.pen_painting_only) {
  992. down = down || mouse_released();
  993. }
  994. }
  995. ///end
  996. ///end
  997. if (down) {
  998. let mx: i32 = mouse_view_x();
  999. let my: i32 = mouse_view_y();
  1000. let ww: i32 = app_w();
  1001. ///if (is_paint || is_sculpt)
  1002. if (context_raw.paint2d) {
  1003. mx -= app_w();
  1004. ww = ui_view2d_ww;
  1005. }
  1006. ///end
  1007. if (mx < ww &&
  1008. mx > app_x() &&
  1009. my < app_h() &&
  1010. my > app_y()) {
  1011. if (set_clone_source) {
  1012. context_raw.clone_start_x = mx;
  1013. context_raw.clone_start_y = my;
  1014. }
  1015. else {
  1016. if (context_raw.brush_time == 0 &&
  1017. !base_is_dragging &&
  1018. !base_is_resizing &&
  1019. !base_is_combo_selected()) { // Paint started
  1020. // Draw line
  1021. if (operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN)) {
  1022. context_raw.last_paint_vec_x = context_raw.last_paint_x;
  1023. context_raw.last_paint_vec_y = context_raw.last_paint_y;
  1024. }
  1025. ///if (is_paint || is_sculpt)
  1026. history_push_undo = true;
  1027. if (context_raw.tool == workspace_tool_t.CLONE && context_raw.clone_start_x >= 0.0) { // Clone delta
  1028. context_raw.clone_delta_x = (context_raw.clone_start_x - mx) / ww;
  1029. context_raw.clone_delta_y = (context_raw.clone_start_y - my) / app_h();
  1030. context_raw.clone_start_x = -1;
  1031. }
  1032. else if (context_raw.tool == workspace_tool_t.PARTICLE) {
  1033. // Reset particles
  1034. ///if arm_particles
  1035. let emitter: mesh_object_t = scene_get_child(".ParticleEmitter").ext;
  1036. let psys: particle_sys_t = emitter.particle_systems[0];
  1037. psys.time = 0;
  1038. // psys.time = psys.seed * psys.animtime;
  1039. // psys.seed++;
  1040. ///end
  1041. }
  1042. else if (context_raw.tool == workspace_tool_t.FILL && context_raw.fill_type_handle.position == fill_type_t.UV_ISLAND) {
  1043. util_uv_uvislandmap_cached = false;
  1044. }
  1045. ///end
  1046. }
  1047. context_raw.brush_time += time_delta();
  1048. ///if (is_paint || is_sculpt)
  1049. if (context_raw.run_brush != null) {
  1050. context_raw.run_brush(context_raw.brush_output_node_inst, 0);
  1051. }
  1052. ///end
  1053. ///if is_lab
  1054. if (context_run_brush != null) {
  1055. // context_run_brush(context_raw.brush_output_node_inst, 0);
  1056. context_run_brush(0);
  1057. }
  1058. ///end
  1059. }
  1060. }
  1061. }
  1062. else if (context_raw.brush_time > 0) { // Brush released
  1063. context_raw.brush_time = 0;
  1064. context_raw.prev_paint_vec_x = -1;
  1065. context_raw.prev_paint_vec_y = -1;
  1066. ///if (!krom_direct3d12 && !krom_vulkan && !krom_metal) // Keep accumulated samples for D3D12
  1067. context_raw.ddirty = 3;
  1068. ///end
  1069. context_raw.brush_blend_dirty = true; // Update brush mask
  1070. ///if (is_paint || is_sculpt)
  1071. context_raw.layer_preview_dirty = true; // Update layer preview
  1072. ///end
  1073. ///if is_paint
  1074. // New color id picked, update fill layer
  1075. if (context_raw.tool == workspace_tool_t.COLORID && context_raw.layer.fill_layer != null) {
  1076. app_notify_on_next_frame(function () {
  1077. base_update_fill_layer();
  1078. make_material_parse_paint_material(false);
  1079. });
  1080. }
  1081. ///end
  1082. }
  1083. ///if is_paint
  1084. if (context_raw.layers_preview_dirty) {
  1085. context_raw.layers_preview_dirty = false;
  1086. context_raw.layer_preview_dirty = false;
  1087. context_raw.mask_preview_last = null;
  1088. if (base_pipe_merge == null) {
  1089. base_make_pipe();
  1090. }
  1091. // Update all layer previews
  1092. for (let i: i32 = 0; i < project_layers.length; ++i) {
  1093. let l: slot_layer_t = project_layers[i];
  1094. if (slot_layer_is_group(l)) {
  1095. continue;
  1096. }
  1097. let target: image_t = l.texpaint_preview;
  1098. let source: image_t = l.texpaint;
  1099. g2_begin(target);
  1100. g2_clear(0x00000000);
  1101. // g2_set_pipeline(l.isMask() ? base_pipe_copy8 : base_pipe_copy);
  1102. g2_set_pipeline(base_pipe_copy); // texpaint_preview is always RGBA32 for now
  1103. g2_draw_scaled_image(source, 0, 0, target.width, target.height);
  1104. g2_set_pipeline(null);
  1105. g2_end();
  1106. }
  1107. ui_base_hwnds[tab_area_t.SIDEBAR0].redraws = 2;
  1108. }
  1109. if (context_raw.layer_preview_dirty && !slot_layer_is_group(context_raw.layer)) {
  1110. context_raw.layer_preview_dirty = false;
  1111. context_raw.mask_preview_last = null;
  1112. if (base_pipe_merge == null) {
  1113. base_make_pipe();
  1114. }
  1115. // Update layer preview
  1116. let l: slot_layer_t = context_raw.layer;
  1117. let target: image_t = l.texpaint_preview;
  1118. let source: image_t = l.texpaint;
  1119. g2_begin(target);
  1120. g2_clear(0x00000000);
  1121. // g2_set_pipeline(raw.layer.isMask() ? base_pipe_copy8 : base_pipe_copy);
  1122. g2_set_pipeline(base_pipe_copy); // texpaint_preview is always RGBA32 for now
  1123. g2_draw_scaled_image(source, 0, 0, target.width, target.height);
  1124. g2_set_pipeline(null);
  1125. g2_end();
  1126. ui_base_hwnds[tab_area_t.SIDEBAR0].redraws = 2;
  1127. }
  1128. ///end
  1129. let undo_pressed: bool = operator_shortcut(map_get(config_keymap, "edit_undo"));
  1130. let redo_pressed: bool = operator_shortcut(map_get(config_keymap, "edit_redo")) ||
  1131. (keyboard_down("control") && keyboard_started("y"));
  1132. // Two-finger tap to undo, three-finger tap to redo
  1133. if (context_in_viewport() && config_raw.touch_ui) {
  1134. if (mouse_started("middle")) {
  1135. ui_base_redo_tap_time = time_time();
  1136. }
  1137. else if (mouse_started("right")) {
  1138. ui_base_undo_tap_time = time_time();
  1139. }
  1140. else if (mouse_released("middle") && time_time() - ui_base_redo_tap_time < 0.1) {
  1141. ui_base_redo_tap_time = ui_base_undo_tap_time = 0;
  1142. redo_pressed = true;
  1143. }
  1144. else if (mouse_released("right") && time_time() - ui_base_undo_tap_time < 0.1) {
  1145. ui_base_redo_tap_time = ui_base_undo_tap_time = 0;
  1146. undo_pressed = true;
  1147. }
  1148. }
  1149. if (undo_pressed) {
  1150. history_undo();
  1151. }
  1152. else if (redo_pressed) {
  1153. history_redo();
  1154. }
  1155. ///if (is_paint || is_sculpt)
  1156. gizmo_update();
  1157. ///end
  1158. }
  1159. function ui_base_render() {
  1160. if (!ui_base_show && config_raw.touch_ui) {
  1161. ui_base_ui.input_enabled = true;
  1162. g2_end();
  1163. zui_begin(ui_base_ui);
  1164. if (zui_window(zui_handle(__ID__), 0, 0, 150, math_floor(zui_ELEMENT_H(ui_base_ui) + zui_ELEMENT_OFFSET(ui_base_ui) + 1))) {
  1165. if (zui_button(tr("Close"))) {
  1166. ui_base_toggle_distract_free();
  1167. }
  1168. }
  1169. zui_end();
  1170. g2_begin(null);
  1171. }
  1172. if (!ui_base_show || sys_width() == 0 || sys_height() == 0) {
  1173. return;
  1174. }
  1175. ui_base_ui.input_enabled = base_ui_enabled;
  1176. // Remember last tab positions
  1177. for (let i: i32 = 0; i < ui_base_htabs.length; ++i) {
  1178. if (ui_base_htabs[i].changed) {
  1179. config_raw.layout_tabs[i] = ui_base_htabs[i].position;
  1180. config_save();
  1181. }
  1182. }
  1183. // Set tab positions
  1184. for (let i: i32 = 0; i < ui_base_htabs.length; ++i) {
  1185. ui_base_htabs[i].position = config_raw.layout_tabs[i];
  1186. }
  1187. g2_end();
  1188. zui_begin(ui_base_ui);
  1189. ///if (is_paint || is_sculpt)
  1190. ui_toolbar_render_ui();
  1191. ///end
  1192. ui_menubar_render_ui();
  1193. ui_header_render_ui();
  1194. ui_status_render_ui();
  1195. ///if (is_paint || is_sculpt)
  1196. ui_base_draw_sidebar();
  1197. ///end
  1198. zui_end();
  1199. g2_begin(null);
  1200. }
  1201. ///if (is_paint || is_sculpt)
  1202. function ui_base_draw_sidebar() {
  1203. // Tabs
  1204. let mini: bool = config_raw.layout[layout_size_t.SIDEBAR_W] <= ui_base_sidebar_mini_w;
  1205. let expand_button_offset: i32 = config_raw.touch_ui ? math_floor(zui_ELEMENT_H(ui_base_ui) + zui_ELEMENT_OFFSET(ui_base_ui)) : 0;
  1206. ui_base_tabx = sys_width() - config_raw.layout[layout_size_t.SIDEBAR_W];
  1207. let _SCROLL_W: i32 = ui_base_ui.ops.theme.SCROLL_W;
  1208. if (mini) {
  1209. ui_base_ui.ops.theme.SCROLL_W = ui_base_ui.ops.theme.SCROLL_MINI_W;
  1210. }
  1211. if (zui_window(ui_base_hwnds[tab_area_t.SIDEBAR0], ui_base_tabx, 0, config_raw.layout[layout_size_t.SIDEBAR_W], config_raw.layout[layout_size_t.SIDEBAR_H0])) {
  1212. for (let i: i32 = 0; i < (mini ? 1 : ui_base_hwnd_tabs[tab_area_t.SIDEBAR0].length); ++i) {
  1213. ui_base_hwnd_tabs[tab_area_t.SIDEBAR0][i](ui_base_htabs[tab_area_t.SIDEBAR0]);
  1214. }
  1215. }
  1216. if (zui_window(ui_base_hwnds[tab_area_t.SIDEBAR1], ui_base_tabx, config_raw.layout[layout_size_t.SIDEBAR_H0], config_raw.layout[layout_size_t.SIDEBAR_W], config_raw.layout[layout_size_t.SIDEBAR_H1] - expand_button_offset)) {
  1217. for (let i: i32 = 0; i < (mini ? 1 : ui_base_hwnd_tabs[tab_area_t.SIDEBAR1].length); ++i) {
  1218. ui_base_hwnd_tabs[tab_area_t.SIDEBAR1][i](ui_base_htabs[tab_area_t.SIDEBAR1]);
  1219. }
  1220. }
  1221. zui_end_window();
  1222. ui_base_ui.ops.theme.SCROLL_W = _SCROLL_W;
  1223. // Collapse / expand button for mini sidebar
  1224. if (config_raw.touch_ui) {
  1225. let width: i32 = config_raw.layout[layout_size_t.SIDEBAR_W];
  1226. let height: i32 = math_floor(zui_ELEMENT_H(ui_base_ui) + zui_ELEMENT_OFFSET(ui_base_ui));
  1227. if (zui_window(zui_handle(__ID__), sys_width() - width, sys_height() - height, width, height + 1)) {
  1228. ui_base_ui._w = width;
  1229. let _BUTTON_H: i32 = ui_base_ui.ops.theme.BUTTON_H;
  1230. let _BUTTON_COL: i32 = ui_base_ui.ops.theme.BUTTON_COL;
  1231. ui_base_ui.ops.theme.BUTTON_H = ui_base_ui.ops.theme.ELEMENT_H;
  1232. ui_base_ui.ops.theme.BUTTON_COL = ui_base_ui.ops.theme.WINDOW_BG_COL;
  1233. if (zui_button(mini ? "<<" : ">>")) {
  1234. config_raw.layout[layout_size_t.SIDEBAR_W] = mini ? ui_base_default_sidebar_full_w : ui_base_default_sidebar_mini_w;
  1235. config_raw.layout[layout_size_t.SIDEBAR_W] = math_floor(config_raw.layout[layout_size_t.SIDEBAR_W] * zui_SCALE(ui_base_ui));
  1236. }
  1237. ui_base_ui.ops.theme.BUTTON_H = _BUTTON_H;
  1238. ui_base_ui.ops.theme.BUTTON_COL = _BUTTON_COL;
  1239. }
  1240. }
  1241. // Expand button
  1242. if (config_raw.layout[layout_size_t.SIDEBAR_W] == 0) {
  1243. let width: i32 = math_floor(g2_font_width(ui_base_ui.ops.font, ui_base_ui.font_size, "<<") + 25 * zui_SCALE(ui_base_ui));
  1244. if (zui_window(ui_base_hminimized, sys_width() - width, 0, width, math_floor(zui_ELEMENT_H(ui_base_ui) + zui_ELEMENT_OFFSET(ui_base_ui) + 1))) {
  1245. ui_base_ui._w = width;
  1246. let _BUTTON_H: i32 = ui_base_ui.ops.theme.BUTTON_H;
  1247. let _BUTTON_COL: i32 = ui_base_ui.ops.theme.BUTTON_COL;
  1248. ui_base_ui.ops.theme.BUTTON_H = ui_base_ui.ops.theme.ELEMENT_H;
  1249. ui_base_ui.ops.theme.BUTTON_COL = ui_base_ui.ops.theme.SEPARATOR_COL;
  1250. if (zui_button("<<")) {
  1251. config_raw.layout[layout_size_t.SIDEBAR_W] = context_raw.maximized_sidebar_width != 0 ? context_raw.maximized_sidebar_width : math_floor(ui_base_default_sidebar_w * config_raw.window_scale);
  1252. }
  1253. ui_base_ui.ops.theme.BUTTON_H = _BUTTON_H;
  1254. ui_base_ui.ops.theme.BUTTON_COL = _BUTTON_COL;
  1255. }
  1256. }
  1257. else if (ui_base_htabs[tab_area_t.SIDEBAR0].changed && ui_base_htabs[tab_area_t.SIDEBAR0].position == context_raw.last_htab0_pos) {
  1258. if (time_time() - context_raw.select_time < 0.25) {
  1259. context_raw.maximized_sidebar_width = config_raw.layout[layout_size_t.SIDEBAR_W];
  1260. config_raw.layout[layout_size_t.SIDEBAR_W] = 0;
  1261. }
  1262. context_raw.select_time = time_time();
  1263. }
  1264. context_raw.last_htab0_pos = ui_base_htabs[tab_area_t.SIDEBAR0].position;
  1265. }
  1266. function ui_base_render_cursor() {
  1267. if (!base_ui_enabled) {
  1268. return;
  1269. }
  1270. ///if is_paint
  1271. if (context_raw.tool == workspace_tool_t.MATERIAL || context_raw.tool == workspace_tool_t.BAKE) {
  1272. return;
  1273. }
  1274. ///end
  1275. g2_set_color(0xffffffff);
  1276. context_raw.view_index = context_raw.view_index_last;
  1277. let mx: i32 = base_x() + context_raw.paint_vec.x * base_w();
  1278. let my: i32 = base_y() + context_raw.paint_vec.y * base_h();
  1279. context_raw.view_index = -1;
  1280. // Radius being scaled
  1281. if (context_raw.brush_locked) {
  1282. mx += context_raw.lock_started_x - sys_width() / 2;
  1283. my += context_raw.lock_started_y - sys_height() / 2;
  1284. }
  1285. ///if is_paint
  1286. if (context_raw.brush_stencil_image != null &&
  1287. // @ts-ignore
  1288. context_raw.tool != workspace_tool_t.BAKE &&
  1289. context_raw.tool != workspace_tool_t.PICKER &&
  1290. // @ts-ignore
  1291. context_raw.tool != workspace_tool_t.MATERIAL &&
  1292. context_raw.tool != workspace_tool_t.COLORID) {
  1293. let r: rect_t = ui_base_get_brush_stencil_rect();
  1294. if (!operator_shortcut(map_get(config_keymap, "stencil_hide"), shortcut_type_t.DOWN)) {
  1295. g2_set_color(0x88ffffff);
  1296. let angle: f32 = context_raw.brush_stencil_angle;
  1297. let cx: f32 = r.x + r.w / 2;
  1298. let cy: f32 = r.y + r.h / 2;
  1299. g2_set_transformation(mat3_multmat(mat3_multmat(mat3_translation(cx, cy), mat3_rotation(-angle)), mat3_translation(-cx, -cy)));
  1300. g2_draw_scaled_image(context_raw.brush_stencil_image, r.x, r.y, r.w, r.h);
  1301. g2_set_transformation(null);
  1302. g2_set_color(0xffffffff);
  1303. }
  1304. let transform: bool = operator_shortcut(map_get(config_keymap, "stencil_transform"), shortcut_type_t.DOWN);
  1305. if (transform) {
  1306. // Outline
  1307. g2_draw_rect(r.x, r.y, r.w, r.h);
  1308. // Scale
  1309. g2_draw_rect(r.x - 8, r.y - 8, 16, 16);
  1310. g2_draw_rect(r.x - 8 + r.w, r.y - 8, 16, 16);
  1311. g2_draw_rect(r.x - 8, r.y - 8 + r.h, 16, 16);
  1312. g2_draw_rect(r.x - 8 + r.w, r.y - 8 + r.h, 16, 16);
  1313. // Rotate
  1314. let angle: f32 = context_raw.brush_stencil_angle;
  1315. let cx: f32 = r.x + r.w / 2;
  1316. let cy: f32 = r.y + r.h / 2;
  1317. g2_set_transformation(mat3_multmat(mat3_multmat(mat3_translation(cx, cy), mat3_rotation(-angle)), mat3_translation(-cx, -cy)));
  1318. g2_fill_circle(r.x + r.w / 2, r.y - 4, 8);
  1319. g2_set_transformation(null);
  1320. }
  1321. }
  1322. ///end
  1323. // Show picked material next to cursor
  1324. if (context_raw.tool == workspace_tool_t.PICKER && context_raw.picker_select_material && context_raw.color_picker_callback == null) {
  1325. let img: image_t = context_raw.material.image_icon;
  1326. ///if krom_opengl
  1327. g2_draw_scaled_image(img, mx + 10, my + 10 + img.height, img.width, -img.height);
  1328. ///else
  1329. g2_draw_image(img, mx + 10, my + 10);
  1330. ///end
  1331. }
  1332. if (context_raw.tool == workspace_tool_t.PICKER && context_raw.color_picker_callback != null) {
  1333. let img: image_t = resource_get("icons.k");
  1334. let rect: rect_t = resource_tile50(img, workspace_tool_t.PICKER, 0);
  1335. g2_draw_sub_image(img, mx + 10, my + 10, rect.x, rect.y, rect.w, rect.h);
  1336. }
  1337. let cursor_img: image_t = resource_get("cursor.k");
  1338. let psize: i32 = math_floor(cursor_img.width * (context_raw.brush_radius * context_raw.brush_nodes_radius) * zui_SCALE(ui_base_ui));
  1339. // Clone source cursor
  1340. if (context_raw.tool == workspace_tool_t.CLONE && !keyboard_down("alt") && (mouse_down() || pen_down())) {
  1341. g2_set_color(0x66ffffff);
  1342. g2_draw_scaled_image(cursor_img, mx + context_raw.clone_delta_x * app_w() - psize / 2, my + context_raw.clone_delta_y * app_h() - psize / 2, psize, psize);
  1343. g2_set_color(0xffffffff);
  1344. }
  1345. let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
  1346. if (!config_raw.brush_3d || context_in_2d_view() || decal) {
  1347. let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask"), shortcut_type_t.DOWN);
  1348. if (decal && !context_in_nodes()) {
  1349. let decal_alpha: f32 = 0.5;
  1350. if (!decal_mask) {
  1351. context_raw.decal_x = context_raw.paint_vec.x;
  1352. context_raw.decal_y = context_raw.paint_vec.y;
  1353. decal_alpha = context_raw.brush_opacity;
  1354. // Radius being scaled
  1355. if (context_raw.brush_locked) {
  1356. context_raw.decal_x += (context_raw.lock_started_x - sys_width() / 2) / base_w();
  1357. context_raw.decal_y += (context_raw.lock_started_y - sys_height() / 2) / base_h();
  1358. }
  1359. }
  1360. if (!config_raw.brush_live) {
  1361. let psizex: i32 = math_floor(256 * zui_SCALE(ui_base_ui) * (context_raw.brush_radius * context_raw.brush_nodes_radius * context_raw.brush_scale_x));
  1362. let psizey: i32 = math_floor(256 * zui_SCALE(ui_base_ui) * (context_raw.brush_radius * context_raw.brush_nodes_radius));
  1363. context_raw.view_index = context_raw.view_index_last;
  1364. let decalx: f32 = base_x() + context_raw.decal_x * base_w() - psizex / 2;
  1365. let decaly: f32 = base_y() + context_raw.decal_y * base_h() - psizey / 2;
  1366. context_raw.view_index = -1;
  1367. g2_set_color(color_from_floats(1, 1, 1, decal_alpha));
  1368. let angle: f32 = (context_raw.brush_angle + context_raw.brush_nodes_angle) * (math_pi() / 180);
  1369. let cx: f32 = decalx + psizex / 2;
  1370. let cy: f32 = decaly + psizey / 2;
  1371. g2_set_transformation(mat3_multmat(mat3_multmat(mat3_translation(cx, cy), mat3_rotation(angle)), mat3_translation(-cx, -cy)));
  1372. ///if (krom_direct3d11 || krom_direct3d12 || krom_metal || krom_vulkan)
  1373. g2_draw_scaled_image(context_raw.decal_image, decalx, decaly, psizex, psizey);
  1374. ///else
  1375. g2_draw_scaled_image(context_raw.decal_image, decalx, decaly + psizey, psizex, -psizey);
  1376. ///end
  1377. g2_set_transformation(null);
  1378. g2_set_color(0xffffffff);
  1379. }
  1380. }
  1381. if (context_raw.tool == workspace_tool_t.BRUSH ||
  1382. context_raw.tool == workspace_tool_t.ERASER ||
  1383. context_raw.tool == workspace_tool_t.CLONE ||
  1384. context_raw.tool == workspace_tool_t.BLUR ||
  1385. context_raw.tool == workspace_tool_t.SMUDGE ||
  1386. context_raw.tool == workspace_tool_t.PARTICLE ||
  1387. (decal_mask && !config_raw.brush_3d) ||
  1388. (decal_mask && context_in_2d_view())) {
  1389. if (decal_mask) {
  1390. psize = math_floor(cursor_img.width * (context_raw.brush_decal_mask_radius * context_raw.brush_nodes_radius) * zui_SCALE(ui_base_ui));
  1391. }
  1392. if (config_raw.brush_3d && context_in_2d_view()) {
  1393. psize = math_floor(psize * ui_view2d_pan_scale);
  1394. }
  1395. g2_draw_scaled_image(cursor_img, mx - psize / 2, my - psize / 2, psize, psize);
  1396. }
  1397. }
  1398. if (context_raw.brush_lazy_radius > 0 && !context_raw.brush_locked &&
  1399. (context_raw.tool == workspace_tool_t.BRUSH ||
  1400. context_raw.tool == workspace_tool_t.ERASER ||
  1401. context_raw.tool == workspace_tool_t.DECAL ||
  1402. context_raw.tool == workspace_tool_t.TEXT ||
  1403. context_raw.tool == workspace_tool_t.CLONE ||
  1404. context_raw.tool == workspace_tool_t.BLUR ||
  1405. context_raw.tool == workspace_tool_t.SMUDGE ||
  1406. context_raw.tool == workspace_tool_t.PARTICLE)) {
  1407. g2_fill_rect(mx - 1, my - 1, 2, 2);
  1408. mx = context_raw.brush_lazy_x * base_w() + base_x();
  1409. my = context_raw.brush_lazy_y * base_h() + base_y();
  1410. let radius: f32 = context_raw.brush_lazy_radius * 180;
  1411. g2_set_color(0xff666666);
  1412. g2_draw_scaled_image(cursor_img, mx - radius / 2, my - radius / 2, radius, radius);
  1413. g2_set_color(0xffffffff);
  1414. }
  1415. }
  1416. ///end
  1417. function ui_base_show_material_nodes() {
  1418. // Clear input state as ui receives input events even when not drawn
  1419. zui_end_input();
  1420. ///if (is_paint || is_sculpt)
  1421. ui_nodes_show = !ui_nodes_show || ui_nodes_canvas_type != canvas_type_t.MATERIAL;
  1422. ui_nodes_canvas_type = canvas_type_t.MATERIAL;
  1423. ///end
  1424. ///if is_lab
  1425. ui_nodes_show = !ui_nodes_show;
  1426. ///end
  1427. base_resize();
  1428. }
  1429. ///if (is_paint || is_sculpt)
  1430. function ui_base_show_brush_nodes() {
  1431. // Clear input state as ui receives input events even when not drawn
  1432. zui_end_input();
  1433. ui_nodes_show = !ui_nodes_show || ui_nodes_canvas_type != canvas_type_t.BRUSH;
  1434. ui_nodes_canvas_type = canvas_type_t.BRUSH;
  1435. base_resize();
  1436. }
  1437. ///end
  1438. function ui_base_show_2d_view(type: view_2d_type_t) {
  1439. // Clear input state as ui receives input events even when not drawn
  1440. zui_end_input();
  1441. if (ui_view2d_type != type) {
  1442. ui_view2d_show = true;
  1443. }
  1444. else {
  1445. ui_view2d_show = !ui_view2d_show;
  1446. }
  1447. ui_view2d_type = type;
  1448. ui_view2d_hwnd.redraws = 2;
  1449. base_resize();
  1450. }
  1451. function ui_base_toggle_browser() {
  1452. let minimized: bool = config_raw.layout[layout_size_t.STATUS_H] <= (ui_status_default_status_h * config_raw.window_scale);
  1453. config_raw.layout[layout_size_t.STATUS_H] = minimized ? 240 : ui_status_default_status_h;
  1454. config_raw.layout[layout_size_t.STATUS_H] = math_floor(config_raw.layout[layout_size_t.STATUS_H] * config_raw.window_scale);
  1455. }
  1456. function ui_base_set_icon_scale() {
  1457. if (zui_SCALE(ui_base_ui) > 1) {
  1458. resource_load(["icons2x.k"]);
  1459. map_set(resource_bundled, "icons.k", resource_get("icons2x.k"));
  1460. }
  1461. else {
  1462. resource_load(["icons.k"]);
  1463. }
  1464. }
  1465. function ui_base_on_border_hover(handle_ptr: i32, side: i32) {
  1466. if (!base_ui_enabled) return;
  1467. ///if (is_paint || is_sculpt)
  1468. if (handle_ptr != ui_base_hwnds[tab_area_t.SIDEBAR0].ptr &&
  1469. handle_ptr != ui_base_hwnds[tab_area_t.SIDEBAR1].ptr &&
  1470. handle_ptr != ui_base_hwnds[tab_area_t.STATUS].ptr &&
  1471. handle_ptr != ui_nodes_hwnd.ptr &&
  1472. handle_ptr != ui_view2d_hwnd.ptr) {
  1473. return; // Scalable handles
  1474. }
  1475. if (handle_ptr == ui_view2d_hwnd.ptr && side != border_side_t.LEFT) {
  1476. return;
  1477. }
  1478. if (handle_ptr == ui_nodes_hwnd.ptr && side == border_side_t.TOP && !ui_view2d_show) {
  1479. return;
  1480. }
  1481. if (handle_ptr == ui_base_hwnds[tab_area_t.SIDEBAR0].ptr && side == border_side_t.TOP) {
  1482. return;
  1483. }
  1484. ///end
  1485. ///if is_lab
  1486. if (handle_ptr != ui_base_hwnds[tab_area_t.STATUS].ptr &&
  1487. handle_ptr != ui_nodes_hwnd.ptr &&
  1488. handle_ptr != ui_view2d_hwnd.ptr) return; // Scalable handles
  1489. if (handle_ptr == ui_view2d_hwnd.ptr && side != border_side_t.LEFT) {
  1490. return;
  1491. }
  1492. if (handle_ptr == ui_nodes_hwnd.ptr && side == border_side_t.TOP && !ui_view2d_show) {
  1493. return;
  1494. }
  1495. ///end
  1496. if (handle_ptr == ui_nodes_hwnd.ptr && side != border_side_t.LEFT && side != border_side_t.TOP) {
  1497. return;
  1498. }
  1499. if (handle_ptr == ui_base_hwnds[tab_area_t.STATUS].ptr && side != border_side_t.TOP) {
  1500. return;
  1501. }
  1502. if (side == border_side_t.RIGHT) {
  1503. return; // UI is snapped to the right side
  1504. }
  1505. side == border_side_t.LEFT || side == border_side_t.RIGHT ?
  1506. krom_set_mouse_cursor(3) : // Horizontal
  1507. krom_set_mouse_cursor(4); // Vertical
  1508. if (zui_current.input_started) {
  1509. ui_base_border_started = side;
  1510. ui_base_border_handle_ptr = handle_ptr;
  1511. base_is_resizing = true;
  1512. }
  1513. }
  1514. function ui_base_on_text_hover() {
  1515. krom_set_mouse_cursor(2); // I-cursor
  1516. }
  1517. function ui_base_on_deselect_text() {
  1518. ///if krom_ios
  1519. keyboard_up_listener(key_code_t.SHIFT);
  1520. ///end
  1521. }
  1522. function ui_base_on_tab_drop(to_ptr: i32, to_position: i32, from_ptr: i32, from_position: i32) {
  1523. let i: i32 = -1;
  1524. let j: i32 = -1;
  1525. for (let k: i32 = 0; k < ui_base_htabs.length; ++k) {
  1526. if (ui_base_htabs[k].ptr == to_ptr) {
  1527. i = k;
  1528. }
  1529. if (ui_base_htabs[k].ptr == from_ptr) {
  1530. j = k;
  1531. }
  1532. }
  1533. if (i > -1 && j > -1) {
  1534. let element: any = ui_base_hwnd_tabs[j][from_position];
  1535. array_splice(ui_base_hwnd_tabs[j], from_position, 1);
  1536. array_insert(ui_base_hwnd_tabs[i], to_position, element);
  1537. ui_base_hwnds[i].redraws = 2;
  1538. ui_base_hwnds[j].redraws = 2;
  1539. }
  1540. }
  1541. function ui_base_tag_ui_redraw() {
  1542. ui_header_handle.redraws = 2;
  1543. ui_base_hwnds[tab_area_t.STATUS].redraws = 2;
  1544. ui_menubar_workspace_handle.redraws = 2;
  1545. ui_menubar_menu_handle.redraws = 2;
  1546. ///if (is_paint || is_sculpt)
  1547. ui_base_hwnds[tab_area_t.SIDEBAR0].redraws = 2;
  1548. ui_base_hwnds[tab_area_t.SIDEBAR1].redraws = 2;
  1549. ui_toolbar_handle.redraws = 2;
  1550. ///end
  1551. }