ui_view2d.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. ///if (is_paint || is_sculpt)
  2. let ui_view2d_pipe: pipeline_t;
  3. let ui_view2d_channel_loc: kinc_const_loc_t;
  4. let ui_view2d_text_input_hover: bool = false;
  5. let ui_view2d_uvmap_show: bool = false;
  6. let ui_view2d_tex_type: paint_tex_t = paint_tex_t.BASE;
  7. let ui_view2d_layer_mode: view_2d_layer_mode_t = view_2d_layer_mode_t.SELECTED;
  8. ///end
  9. ///if (is_paint || is_sculpt)
  10. let ui_view2d_type: view_2d_type_t = view_2d_type_t.LAYER;
  11. ///else
  12. let ui_view2d_type: view_2d_type_t = view_2d_type_t.ASSET;
  13. ///end
  14. let ui_view2d_show: bool = false;
  15. let ui_view2d_wx: i32;
  16. let ui_view2d_wy: i32;
  17. let ui_view2d_ww: i32;
  18. let ui_view2d_wh: i32;
  19. let ui_view2d_ui: zui_t;
  20. let ui_view2d_hwnd: zui_handle_t = zui_handle_create();
  21. let ui_view2d_pan_x: f32 = 0.0;
  22. let ui_view2d_pan_y: f32 = 0.0;
  23. let ui_view2d_pan_scale: f32 = 1.0;
  24. let ui_view2d_tiled_show: bool = false;
  25. let ui_view2d_controls_down: bool = false;
  26. function ui_view2d_init() {
  27. ///if (is_paint || is_sculpt)
  28. ui_view2d_pipe = g4_pipeline_create();
  29. ui_view2d_pipe.vertex_shader = sys_get_shader("layer_view.vert");
  30. ui_view2d_pipe.fragment_shader = sys_get_shader("layer_view.frag");
  31. let vs: vertex_struct_t = g4_vertex_struct_create();
  32. g4_vertex_struct_add(vs, "pos", vertex_data_t.F32_3X);
  33. g4_vertex_struct_add(vs, "tex", vertex_data_t.F32_2X);
  34. g4_vertex_struct_add(vs, "col", vertex_data_t.U8_4X_NORM);
  35. ui_view2d_pipe.input_layout = [vs];
  36. ui_view2d_pipe.blend_source = blend_factor_t.BLEND_ONE;
  37. ui_view2d_pipe.blend_dest = blend_factor_t.BLEND_ZERO;
  38. ui_view2d_pipe.color_write_masks_alpha[0] = false;
  39. g4_pipeline_compile(ui_view2d_pipe);
  40. ui_view2d_channel_loc = g4_pipeline_get_const_loc(ui_view2d_pipe, "channel");
  41. ///end
  42. let scale: f32 = config_raw.window_scale;
  43. let ops: zui_options_t = {
  44. theme: base_theme,
  45. font: base_font,
  46. color_wheel: base_color_wheel,
  47. black_white_gradient: base_color_wheel_gradient,
  48. scale_factor: scale
  49. };
  50. ui_view2d_ui = zui_create(ops);
  51. ui_view2d_ui.scroll_enabled = false;
  52. }
  53. let _ui_view2d_render_tex: image_t;
  54. let _ui_view2d_render_x: f32;
  55. let _ui_view2d_render_y: f32;
  56. let _ui_view2d_render_tw: f32;
  57. let _ui_view2d_render_th: f32;
  58. function ui_view2d_render() {
  59. ui_view2d_ww = config_raw.layout[layout_size_t.NODES_W];
  60. ///if (is_paint || is_sculpt)
  61. ui_view2d_wx = math_floor(app_w()) + ui_toolbar_w;
  62. ///else
  63. ui_view2d_wx = math_floor(app_w());
  64. ///end
  65. ui_view2d_wy = 0;
  66. ///if (is_paint || is_sculpt)
  67. if (!ui_base_show) {
  68. ui_view2d_ww += config_raw.layout[layout_size_t.SIDEBAR_W] + ui_toolbar_w;
  69. ui_view2d_wx -= ui_toolbar_w;
  70. }
  71. ///end
  72. if (!ui_view2d_show) {
  73. return;
  74. }
  75. if (sys_width() == 0 || sys_height() == 0) {
  76. return;
  77. }
  78. if (context_raw.pdirty >= 0) {
  79. ui_view2d_hwnd.redraws = 2; // Paint was active
  80. }
  81. g2_end();
  82. // Cache grid
  83. if (ui_nodes_grid == null) {
  84. ui_nodes_draw_grid();
  85. }
  86. // Ensure UV map is drawn
  87. ///if (is_paint || is_sculpt)
  88. if (ui_view2d_uvmap_show) {
  89. util_uv_cache_uv_map();
  90. }
  91. ///end
  92. // Ensure font image is drawn
  93. ///if (is_paint || is_sculpt)
  94. if (context_raw.font.image == null) {
  95. util_render_make_font_preview();
  96. }
  97. ///end
  98. zui_begin(ui_view2d_ui);
  99. let headerh: i32 = config_raw.layout[layout_size_t.HEADER] == 1 ? ui_header_h * 2 : ui_header_h;
  100. let apph: i32 = sys_height() - config_raw.layout[layout_size_t.STATUS_H] + headerh;
  101. ui_view2d_wh = sys_height() - config_raw.layout[layout_size_t.STATUS_H];
  102. if (ui_nodes_show) {
  103. ui_view2d_wh -= config_raw.layout[layout_size_t.NODES_H];
  104. if (config_raw.touch_ui) ui_view2d_wh += ui_header_h;
  105. }
  106. if (zui_window(ui_view2d_hwnd, ui_view2d_wx, ui_view2d_wy, ui_view2d_ww, ui_view2d_wh)) {
  107. zui_tab(zui_handle(__ID__), tr("2D View"));
  108. // Grid
  109. g2_set_color(0xffffffff);
  110. g2_draw_image(ui_nodes_grid, (ui_view2d_pan_x * ui_view2d_pan_scale) % 100 - 100, (ui_view2d_pan_y * ui_view2d_pan_scale) % 100 - 100);
  111. // Texture
  112. let tex: image_t = null;
  113. ///if (is_paint || is_sculpt)
  114. let l: slot_layer_t = context_raw.layer;
  115. let channel: i32 = 0;
  116. ///end
  117. let tw: f32 = ui_view2d_ww * 0.95 * ui_view2d_pan_scale;
  118. let tx: f32 = ui_view2d_ww / 2 - tw / 2 + ui_view2d_pan_x;
  119. let ty: f32 = apph / 2 - tw / 2 + ui_view2d_pan_y;
  120. if (ui_view2d_type == view_2d_type_t.ASSET) {
  121. tex = project_get_image(context_raw.texture);
  122. }
  123. else if (ui_view2d_type == view_2d_type_t.NODE) {
  124. ///if (is_paint || is_sculpt)
  125. tex = context_raw.node_preview;
  126. ///else
  127. let nodes: zui_nodes_t = ui_nodes_get_nodes();
  128. if (nodes.nodes_selected_id.length > 0) {
  129. let sel: zui_node_t = zui_get_node(ui_nodes_get_canvas(true).nodes, nodes.nodes_selected_id[0]);
  130. let brush_node: logic_node_t = parser_logic_get_logic_node(sel);
  131. if (brush_node != null) {
  132. tex = logic_node_get_cached_image(brush_node);
  133. }
  134. }
  135. ///end
  136. }
  137. ///if is_paint
  138. else if (ui_view2d_type == view_2d_type_t.LAYER) {
  139. let layer: slot_layer_t = l;
  140. if (config_raw.brush_live && render_path_paint_live_layer_drawn > 0) {
  141. layer = render_path_paint_live_layer;
  142. }
  143. if (ui_view2d_layer_mode == view_2d_layer_mode_t.VISIBLE) {
  144. let current: image_t = _g2_current;
  145. let g2_in_use: bool = _g2_in_use;
  146. if (g2_in_use) g2_end();
  147. layer = base_flatten();
  148. if (g2_in_use) g2_begin(current);
  149. }
  150. else if (slot_layer_is_group(layer)) {
  151. let current: image_t = _g2_current;
  152. let g2_in_use: bool = _g2_in_use;
  153. if (g2_in_use) g2_end();
  154. layer = base_flatten(false, slot_layer_get_children(layer));
  155. if (g2_in_use) g2_begin(current);
  156. }
  157. tex =
  158. slot_layer_is_mask(context_raw.layer) ? layer.texpaint :
  159. ui_view2d_tex_type == paint_tex_t.BASE ? layer.texpaint :
  160. ui_view2d_tex_type == paint_tex_t.OPACITY ? layer.texpaint :
  161. ui_view2d_tex_type == paint_tex_t.NORMAL ? layer.texpaint_nor :
  162. layer.texpaint_pack;
  163. channel =
  164. slot_layer_is_mask(context_raw.layer) ? 1 :
  165. ui_view2d_tex_type == paint_tex_t.OCCLUSION ? 1 :
  166. ui_view2d_tex_type == paint_tex_t.ROUGHNESS ? 2 :
  167. ui_view2d_tex_type == paint_tex_t.METALLIC ? 3 :
  168. ui_view2d_tex_type == paint_tex_t.OPACITY ? 4 :
  169. ui_view2d_tex_type == paint_tex_t.HEIGHT ? 4 :
  170. ui_view2d_tex_type == paint_tex_t.NORMAL ? 5 :
  171. 0;
  172. }
  173. else if (ui_view2d_type == view_2d_type_t.FONT) {
  174. tex = context_raw.font.image;
  175. }
  176. ///end
  177. let th: f32 = tw;
  178. if (tex != null) {
  179. th = tw * (tex.height / tex.width);
  180. ty = apph / 2 - th / 2 + ui_view2d_pan_y;
  181. ///if (is_paint || is_sculpt)
  182. if (ui_view2d_type == view_2d_type_t.LAYER) {
  183. ///if (!krom_opengl)
  184. g2_set_pipeline(ui_view2d_pipe);
  185. ///end
  186. if (!context_raw.texture_filter) {
  187. g2_set_bilinear_filter(false);
  188. }
  189. ///if krom_opengl
  190. krom_g4_set_pipeline(ui_view2d_pipe.pipeline_);
  191. ///end
  192. krom_g4_set_int(ui_view2d_channel_loc, channel);
  193. }
  194. ///end
  195. g2_draw_scaled_image(tex, tx, ty, tw, th);
  196. if (ui_view2d_tiled_show) {
  197. g2_draw_scaled_image(tex, tx - tw, ty, tw, th);
  198. g2_draw_scaled_image(tex, tx - tw, ty - th, tw, th);
  199. g2_draw_scaled_image(tex, tx - tw, ty + th, tw, th);
  200. g2_draw_scaled_image(tex, tx + tw, ty, tw, th);
  201. g2_draw_scaled_image(tex, tx + tw, ty - th, tw, th);
  202. g2_draw_scaled_image(tex, tx + tw, ty + th, tw, th);
  203. g2_draw_scaled_image(tex, tx, ty - th, tw, th);
  204. g2_draw_scaled_image(tex, tx, ty + th, tw, th);
  205. }
  206. ///if (is_paint || is_sculpt)
  207. if (ui_view2d_type == view_2d_type_t.LAYER) {
  208. g2_set_pipeline(null);
  209. if (!context_raw.texture_filter) {
  210. g2_set_bilinear_filter(true);
  211. }
  212. }
  213. // Texture and node preview color picking
  214. if ((context_in_2d_view(view_2d_type_t.ASSET) || context_in_2d_view(view_2d_type_t.NODE)) && context_raw.tool == workspace_tool_t.PICKER && ui_view2d_ui.input_down) {
  215. _ui_view2d_render_tex = tex;
  216. _ui_view2d_render_x = ui_view2d_ui.input_x - tx - ui_view2d_wx;;
  217. _ui_view2d_render_y = ui_view2d_ui.input_y - ty - ui_view2d_wy;
  218. _ui_view2d_render_tw = tw;
  219. _ui_view2d_render_th = th;
  220. app_notify_on_next_frame(function () {
  221. let texpaint_picker: image_t = map_get(render_path_render_targets, "texpaint_picker")._image;
  222. g2_begin(texpaint_picker);
  223. g2_draw_scaled_image(_ui_view2d_render_tex, -_ui_view2d_render_x, -_ui_view2d_render_y, _ui_view2d_render_tw, _ui_view2d_render_th);
  224. g2_end();
  225. let a: buffer_view_t = buffer_view_create(image_get_pixels(texpaint_picker));
  226. ///if (krom_metal || krom_vulkan)
  227. let i0: i32 = 2;
  228. let i1: i32 = 1;
  229. let i2: i32 = 0;
  230. ///else
  231. let i0: i32 = 0;
  232. let i1: i32 = 1;
  233. let i2: i32 = 2;
  234. ///end
  235. context_raw.picked_color.base = color_set_rb(context_raw.picked_color.base, buffer_view_get_u8(a, i0));
  236. context_raw.picked_color.base = color_set_gb(context_raw.picked_color.base, buffer_view_get_u8(a, i1));
  237. context_raw.picked_color.base = color_set_bb(context_raw.picked_color.base, buffer_view_get_u8(a, i2));
  238. ui_header_handle.redraws = 2;
  239. });
  240. }
  241. ///end
  242. }
  243. ///if (is_paint || is_sculpt)
  244. // UV map
  245. if (ui_view2d_type == view_2d_type_t.LAYER && ui_view2d_uvmap_show) {
  246. g2_draw_scaled_image(util_uv_uvmap, tx, ty, tw, th);
  247. }
  248. ///end
  249. // Menu
  250. let ew: i32 = math_floor(zui_ELEMENT_W(ui_view2d_ui));
  251. g2_set_color(ui_view2d_ui.ops.theme.SEPARATOR_COL);
  252. g2_fill_rect(0, zui_ELEMENT_H(ui_view2d_ui), ui_view2d_ww, zui_ELEMENT_H(ui_view2d_ui) + zui_ELEMENT_OFFSET(ui_view2d_ui) * 2);
  253. g2_set_color(0xffffffff);
  254. let start_y: f32 = zui_ELEMENT_H(ui_view2d_ui) + zui_ELEMENT_OFFSET(ui_view2d_ui);
  255. ui_view2d_ui._x = 2;
  256. ui_view2d_ui._y = 2 + start_y;
  257. ui_view2d_ui._w = ew;
  258. // Editable layer name
  259. let h: zui_handle_t = zui_handle(__ID__);
  260. ///if (is_paint || is_sculpt)
  261. let text: string = ui_view2d_type == view_2d_type_t.NODE ? context_raw.node_preview_name : h.text;
  262. ///else
  263. let text: string = h.text;
  264. ///end
  265. ui_view2d_ui._w = math_floor(math_min(g2_font_width(ui_view2d_ui.ops.font, ui_view2d_ui.font_size, text) + 15 * zui_SCALE(ui_view2d_ui), 100 * zui_SCALE(ui_view2d_ui)));
  266. if (ui_view2d_type == view_2d_type_t.ASSET) {
  267. let asset: asset_t = context_raw.texture;
  268. if (asset != null) {
  269. let asset_names: string[] = project_asset_names;
  270. let i: i32 = array_index_of(asset_names, asset.name);
  271. h.text = asset.name;
  272. asset.name = zui_text_input(h, "");
  273. asset_names[i] = asset.name;
  274. }
  275. }
  276. else if (ui_view2d_type == view_2d_type_t.NODE) {
  277. ///if (is_paint || is_sculpt)
  278. zui_text(context_raw.node_preview_name);
  279. ///else
  280. let nodes: zui_nodes_t = ui_nodes_get_nodes();
  281. if (nodes.nodes_selected_id.length > 0) {
  282. zui_text(zui_get_node(ui_nodes_get_canvas(true).nodes, nodes.nodes_selected_id[0]).name);
  283. }
  284. ///end
  285. }
  286. ///if (is_paint || is_sculpt)
  287. else if (ui_view2d_type == view_2d_type_t.LAYER) {
  288. h.text = l.name;
  289. l.name = zui_text_input(h, "");
  290. ui_view2d_text_input_hover = ui_view2d_ui.is_hovered;
  291. }
  292. else if (ui_view2d_type == view_2d_type_t.FONT) {
  293. h.text = context_raw.font.name;
  294. context_raw.font.name = zui_text_input(h, "");
  295. }
  296. ///end
  297. if (h.changed) {
  298. ui_base_hwnds[0].redraws = 2;
  299. }
  300. ui_view2d_ui._x += ui_view2d_ui._w + 3;
  301. ui_view2d_ui._y = 2 + start_y;
  302. ui_view2d_ui._w = ew;
  303. ///if (is_paint || is_sculpt)
  304. if (ui_view2d_type == view_2d_type_t.LAYER) {
  305. let h_layer_mode: zui_handle_t = zui_handle(__ID__);
  306. if (h_layer_mode.init) {
  307. h_layer_mode.position = ui_view2d_layer_mode;
  308. }
  309. let layer_mode_combo: string[] = [tr("Visible"), tr("Selected")];
  310. ui_view2d_layer_mode = zui_combo(h_layer_mode, layer_mode_combo, tr("Layers"));
  311. ui_view2d_ui._x += ew + 3;
  312. ui_view2d_ui._y = 2 + start_y;
  313. if (!slot_layer_is_mask(context_raw.layer)) {
  314. let h_tex_type: zui_handle_t = zui_handle(__ID__);
  315. if (h_tex_type.init) {
  316. h_tex_type.position = ui_view2d_tex_type;
  317. }
  318. let tex_type_combo: string[] = [
  319. tr("Base Color"),
  320. tr("Normal Map"),
  321. tr("Occlusion"),
  322. tr("Roughness"),
  323. tr("Metallic"),
  324. tr("Opacity"),
  325. tr("Height"),
  326. ];
  327. ui_view2d_tex_type = zui_combo(h_tex_type, tex_type_combo, tr("Texture"));
  328. ui_view2d_ui._x += ew + 3;
  329. ui_view2d_ui._y = 2 + start_y;
  330. }
  331. ui_view2d_ui._w = math_floor(ew * 0.7 + 3);
  332. let h_uvmap_show: zui_handle_t = zui_handle(__ID__);
  333. if (h_uvmap_show.init) {
  334. h_uvmap_show.selected = ui_view2d_uvmap_show;
  335. }
  336. ui_view2d_uvmap_show = zui_check(h_uvmap_show, tr("UV Map"));
  337. ui_view2d_ui._x += ew * 0.7 + 3;
  338. ui_view2d_ui._y = 2 + start_y;
  339. }
  340. ///end
  341. let h_tiled_show: zui_handle_t = zui_handle(__ID__);
  342. if (h_tiled_show.init) {
  343. h_tiled_show.selected = ui_view2d_tiled_show;
  344. }
  345. ui_view2d_tiled_show = zui_check(h_tiled_show, tr("Tiled"));
  346. ui_view2d_ui._x += ew * 0.7 + 3;
  347. ui_view2d_ui._y = 2 + start_y;
  348. if (ui_view2d_type == view_2d_type_t.ASSET && tex != null) { // Texture resolution
  349. zui_text(tex.width + "x" + tex.height);
  350. }
  351. // Picked position
  352. ///if (is_paint || is_sculpt)
  353. if (context_raw.tool == workspace_tool_t.PICKER && (ui_view2d_type == view_2d_type_t.LAYER || ui_view2d_type == view_2d_type_t.ASSET)) {
  354. let cursor_img: image_t = resource_get("cursor.k");
  355. let hsize: f32 = 16 * zui_SCALE(ui_view2d_ui);
  356. let size: f32 = hsize * 2;
  357. g2_draw_scaled_image(cursor_img, tx + tw * context_raw.uvx_picked - hsize, ty + th * context_raw.uvy_picked - hsize, size, size);
  358. }
  359. ///end
  360. }
  361. zui_end();
  362. g2_begin(null);
  363. }
  364. function ui_view2d_update() {
  365. let headerh: f32 = zui_ELEMENT_H(ui_view2d_ui) * 1.4;
  366. ///if (is_paint || is_sculpt)
  367. context_raw.paint2d = false;
  368. ///end
  369. if (!base_ui_enabled ||
  370. !ui_view2d_show ||
  371. mouse_x < ui_view2d_wx ||
  372. mouse_x > ui_view2d_wx + ui_view2d_ww ||
  373. mouse_y < ui_view2d_wy + headerh ||
  374. mouse_y > ui_view2d_wy + ui_view2d_wh) {
  375. if (ui_view2d_controls_down) {
  376. let control: zui_canvas_control_t = ui_nodes_get_canvas_control(ui_view2d_ui, ui_view2d_controls_down);
  377. ui_view2d_controls_down = control.controls_down;
  378. }
  379. return;
  380. }
  381. let control: zui_canvas_control_t = ui_nodes_get_canvas_control(ui_view2d_ui, ui_view2d_controls_down);
  382. ui_view2d_pan_x += control.pan_x;
  383. ui_view2d_pan_y += control.pan_y;
  384. ui_view2d_controls_down = control.controls_down;
  385. if (control.zoom != 0) {
  386. let _pan_x: f32 = ui_view2d_pan_x / ui_view2d_pan_scale;
  387. let _pan_y: f32 = ui_view2d_pan_y / ui_view2d_pan_scale;
  388. ui_view2d_pan_scale += control.zoom;
  389. if (ui_view2d_pan_scale < 0.1) {
  390. ui_view2d_pan_scale = 0.1;
  391. }
  392. if (ui_view2d_pan_scale > 6.0) {
  393. ui_view2d_pan_scale = 6.0;
  394. }
  395. ui_view2d_pan_x = _pan_x * ui_view2d_pan_scale;
  396. ui_view2d_pan_y = _pan_y * ui_view2d_pan_scale;
  397. if (zui_touch_scroll()) {
  398. // Zoom to finger location
  399. ui_view2d_pan_x -= (ui_view2d_ui.input_x - ui_view2d_ui._window_x - ui_view2d_ui._window_w / 2) * control.zoom;
  400. ui_view2d_pan_y -= (ui_view2d_ui.input_y - ui_view2d_ui._window_y - ui_view2d_ui._window_h / 2) * control.zoom;
  401. }
  402. }
  403. ///if (is_paint || is_sculpt)
  404. let decal: bool = context_raw.tool == workspace_tool_t.DECAL || context_raw.tool == workspace_tool_t.TEXT;
  405. let decal_mask: bool = decal && operator_shortcut(map_get(config_keymap, "decal_mask") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN);
  406. 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);
  407. if (ui_view2d_type == view_2d_type_t.LAYER &&
  408. !ui_view2d_text_input_hover &&
  409. (operator_shortcut(map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
  410. operator_shortcut(map_get(config_keymap, "brush_ruler") + "+" + map_get(config_keymap, "action_paint"), shortcut_type_t.DOWN) ||
  411. decal_mask ||
  412. set_clone_source ||
  413. config_raw.brush_live)) {
  414. context_raw.paint2d = true;
  415. }
  416. ///end
  417. if (ui_view2d_ui.is_typing) {
  418. return;
  419. }
  420. if (keyboard_started("left")) {
  421. ui_view2d_pan_x -= 5;
  422. }
  423. else if (keyboard_started("right")) {
  424. ui_view2d_pan_x += 5;
  425. }
  426. if (keyboard_started("up")) {
  427. ui_view2d_pan_y -= 5;
  428. }
  429. else if (keyboard_started("down")) {
  430. ui_view2d_pan_y += 5;
  431. }
  432. // Limit panning to keep texture in viewport
  433. let border: i32 = 32;
  434. let tw: f32 = ui_view2d_ww * 0.95 * ui_view2d_pan_scale;
  435. let tx: f32 = ui_view2d_ww / 2 - tw / 2 + ui_view2d_pan_x;
  436. let hh: f32 = app_h();
  437. let ty: f32 = hh / 2 - tw / 2 + ui_view2d_pan_y;
  438. if (tx + border > ui_view2d_ww) {
  439. ui_view2d_pan_x = ui_view2d_ww / 2 + tw / 2 - border;
  440. }
  441. else if (tx - border < -tw) {
  442. ui_view2d_pan_x = -tw / 2 - ui_view2d_ww / 2 + border;
  443. }
  444. if (ty + border > hh) {
  445. ui_view2d_pan_y = hh / 2 + tw / 2 - border;
  446. }
  447. else if (ty - border < -tw) {
  448. ui_view2d_pan_y = -tw / 2 - hh / 2 + border;
  449. }
  450. if (operator_shortcut(map_get(config_keymap, "view_reset"))) {
  451. ui_view2d_pan_x = 0.0;
  452. ui_view2d_pan_y = 0.0;
  453. ui_view2d_pan_scale = 1.0;
  454. }
  455. }