ui_base.ts 55 KB

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