1 #include "ImGuiWrapper.hpp"
2
3 #include <cstdio>
4 #include <vector>
5 #include <cmath>
6 #include <stdexcept>
7
8 #include <boost/format.hpp>
9 #include <boost/log/trivial.hpp>
10 #include <boost/filesystem.hpp>
11
12 #include <wx/string.h>
13 #include <wx/event.h>
14 #include <wx/clipbrd.h>
15 #include <wx/debug.h>
16
17 #include <GL/glew.h>
18
19 #ifndef IMGUI_DEFINE_MATH_OPERATORS
20 #define IMGUI_DEFINE_MATH_OPERATORS
21 #endif
22 #include <imgui/imgui_internal.h>
23
24 #include "libslic3r/libslic3r.h"
25 #include "libslic3r/Utils.hpp"
26 #include "3DScene.hpp"
27 #include "GUI.hpp"
28 #include "I18N.hpp"
29 #include "Search.hpp"
30
31 #include "../Utils/MacDarkMode.hpp"
32 #include "nanosvg/nanosvg.h"
33 #include "nanosvg/nanosvgrast.h"
34
35 namespace Slic3r {
36 namespace GUI {
37
38
39 static const std::map<const char, std::string> font_icons = {
40 {ImGui::PrintIconMarker , "cog" },
41 {ImGui::PrinterIconMarker , "printer" },
42 {ImGui::PrinterSlaIconMarker , "sla_printer" },
43 {ImGui::FilamentIconMarker , "spool" },
44 {ImGui::MaterialIconMarker , "resin" },
45 {ImGui::MinimalizeButton , "notification_minimalize" },
46 {ImGui::MinimalizeHoverButton , "notification_minimalize_hover" }
47 };
48 static const std::map<const char, std::string> font_icons_large = {
49 {ImGui::CloseNotifButton , "notification_close" },
50 {ImGui::CloseNotifHoverButton , "notification_close_hover" },
51 {ImGui::EjectButton , "notification_eject_sd" },
52 {ImGui::EjectHoverButton , "notification_eject_sd_hover" },
53 {ImGui::WarningMarker , "notification_warning" },
54 {ImGui::ErrorMarker , "notification_error" }
55 };
56
57 const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f };
58 const ImVec4 ImGuiWrapper::COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f };
59 const ImVec4 ImGuiWrapper::COL_ORANGE_DARK = { 0.757f, 0.404f, 0.216f, 1.0f };
60 const ImVec4 ImGuiWrapper::COL_ORANGE_LIGHT = { 1.0f, 0.49f, 0.216f, 1.0f };
61 const ImVec4 ImGuiWrapper::COL_WINDOW_BACKGROUND = { 0.133f, 0.133f, 0.133f, 0.8f };
62 const ImVec4 ImGuiWrapper::COL_BUTTON_BACKGROUND = COL_ORANGE_DARK;
63 const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT;
64 const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED;
65
ImGuiWrapper()66 ImGuiWrapper::ImGuiWrapper()
67 : m_glyph_ranges(nullptr)
68 , m_font_cjk(false)
69 , m_font_size(18.0)
70 , m_font_texture(0)
71 , m_style_scaling(1.0)
72 , m_mouse_buttons(0)
73 , m_disabled(false)
74 , m_new_frame_open(false)
75 {
76 ImGui::CreateContext();
77
78 init_input();
79 init_style();
80
81 ImGui::GetIO().IniFilename = nullptr;
82 }
83
~ImGuiWrapper()84 ImGuiWrapper::~ImGuiWrapper()
85 {
86 destroy_font();
87 ImGui::DestroyContext();
88 }
89
set_language(const std::string & language)90 void ImGuiWrapper::set_language(const std::string &language)
91 {
92 if (m_new_frame_open) {
93 // ImGUI internally locks the font between NewFrame() and EndFrame()
94 // NewFrame() might've been called here because of input from the 3D scene;
95 // call EndFrame()
96 ImGui::EndFrame();
97 m_new_frame_open = false;
98 }
99
100 const ImWchar *ranges = nullptr;
101 size_t idx = language.find('_');
102 std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx);
103 static const ImWchar ranges_latin2[] =
104 {
105 0x0020, 0x00FF, // Basic Latin + Latin Supplement
106 0x0100, 0x017F, // Latin Extended-A
107 0,
108 };
109 static const ImWchar ranges_turkish[] = {
110 0x0020, 0x01FF, // Basic Latin + Latin Supplement
111 0x0100, 0x017F, // Latin Extended-A
112 0x0180, 0x01FF, // Turkish
113 0,
114 };
115 static const ImWchar ranges_vietnamese[] =
116 {
117 0x0020, 0x00FF, // Basic Latin
118 0x0102, 0x0103,
119 0x0110, 0x0111,
120 0x0128, 0x0129,
121 0x0168, 0x0169,
122 0x01A0, 0x01A1,
123 0x01AF, 0x01B0,
124 0x1EA0, 0x1EF9,
125 0,
126 };
127 m_font_cjk = false;
128 if (lang == "cs" || lang == "pl") {
129 ranges = ranges_latin2;
130 } else if (lang == "ru" || lang == "uk") {
131 ranges = ImGui::GetIO().Fonts->GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters
132 } else if (lang == "tr") {
133 ranges = ranges_turkish;
134 } else if (lang == "vi") {
135 ranges = ranges_vietnamese;
136 } else if (lang == "ja") {
137 ranges = ImGui::GetIO().Fonts->GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs
138 m_font_cjk = true;
139 } else if (lang == "ko") {
140 ranges = ImGui::GetIO().Fonts->GetGlyphRangesKorean(); // Default + Korean characters
141 m_font_cjk = true;
142 } else if (lang == "zh") {
143 ranges = (language == "zh_TW") ?
144 // Traditional Chinese
145 // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs
146 ImGui::GetIO().Fonts->GetGlyphRangesChineseFull() :
147 // Simplified Chinese
148 // Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese
149 ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
150 m_font_cjk = true;
151 } else if (lang == "th") {
152 ranges = ImGui::GetIO().Fonts->GetGlyphRangesThai(); // Default + Thai characters
153 } else {
154 ranges = ImGui::GetIO().Fonts->GetGlyphRangesDefault(); // Basic Latin, Extended Latin
155 }
156
157 if (ranges != m_glyph_ranges) {
158 m_glyph_ranges = ranges;
159 destroy_font();
160 }
161 }
162
set_display_size(float w,float h)163 void ImGuiWrapper::set_display_size(float w, float h)
164 {
165 ImGuiIO& io = ImGui::GetIO();
166 io.DisplaySize = ImVec2(w, h);
167 io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
168 }
169
set_scaling(float font_size,float scale_style,float scale_both)170 void ImGuiWrapper::set_scaling(float font_size, float scale_style, float scale_both)
171 {
172 font_size *= scale_both;
173 scale_style *= scale_both;
174
175 if (m_font_size == font_size && m_style_scaling == scale_style) {
176 return;
177 }
178
179 m_font_size = font_size;
180
181 ImGui::GetStyle().ScaleAllSizes(scale_style / m_style_scaling);
182 m_style_scaling = scale_style;
183
184 destroy_font();
185 }
186
update_mouse_data(wxMouseEvent & evt)187 bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt)
188 {
189 if (! display_initialized()) {
190 return false;
191 }
192
193 ImGuiIO& io = ImGui::GetIO();
194 io.MousePos = ImVec2((float)evt.GetX(), (float)evt.GetY());
195 io.MouseDown[0] = evt.LeftIsDown();
196 io.MouseDown[1] = evt.RightIsDown();
197 io.MouseDown[2] = evt.MiddleIsDown();
198 float wheel_delta = static_cast<float>(evt.GetWheelDelta());
199 if (wheel_delta != 0.0f)
200 io.MouseWheel = static_cast<float>(evt.GetWheelRotation()) / wheel_delta;
201
202 unsigned buttons = (evt.LeftIsDown() ? 1 : 0) | (evt.RightIsDown() ? 2 : 0) | (evt.MiddleIsDown() ? 4 : 0);
203 m_mouse_buttons = buttons;
204
205 new_frame();
206 return want_mouse();
207 }
208
update_key_data(wxKeyEvent & evt)209 bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
210 {
211 if (! display_initialized()) {
212 return false;
213 }
214
215 ImGuiIO& io = ImGui::GetIO();
216
217 if (evt.GetEventType() == wxEVT_CHAR) {
218 // Char event
219 const auto key = evt.GetUnicodeKey();
220 if (key != 0) {
221 io.AddInputCharacter(key);
222 }
223
224 new_frame();
225 return want_keyboard() || want_text_input();
226 } else {
227 // Key up/down event
228 int key = evt.GetKeyCode();
229 wxCHECK_MSG(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown), false, "Received invalid key code");
230
231 io.KeysDown[key] = evt.GetEventType() == wxEVT_KEY_DOWN;
232 io.KeyShift = evt.ShiftDown();
233 io.KeyCtrl = evt.ControlDown();
234 io.KeyAlt = evt.AltDown();
235 io.KeySuper = evt.MetaDown();
236
237 new_frame();
238 return want_keyboard() || want_text_input();
239 }
240 }
241
new_frame()242 void ImGuiWrapper::new_frame()
243 {
244 if (m_new_frame_open) {
245 return;
246 }
247
248 if (m_font_texture == 0) {
249 init_font(true);
250 }
251
252 ImGui::NewFrame();
253 m_new_frame_open = true;
254 }
255
render()256 void ImGuiWrapper::render()
257 {
258 ImGui::Render();
259 render_draw_data(ImGui::GetDrawData());
260 m_new_frame_open = false;
261 }
262
calc_text_size(const wxString & text)263 ImVec2 ImGuiWrapper::calc_text_size(const wxString &text)
264 {
265 auto text_utf8 = into_u8(text);
266 ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str());
267
268 /*#ifdef __linux__
269 size.x *= m_style_scaling;
270 size.y *= m_style_scaling;
271 #endif*/
272
273 return size;
274 }
275
set_next_window_pos(float x,float y,int flag,float pivot_x,float pivot_y)276 void ImGuiWrapper::set_next_window_pos(float x, float y, int flag, float pivot_x, float pivot_y)
277 {
278 ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag, ImVec2(pivot_x, pivot_y));
279 ImGui::SetNextWindowSize(ImVec2(0.0, 0.0));
280 }
281
set_next_window_bg_alpha(float alpha)282 void ImGuiWrapper::set_next_window_bg_alpha(float alpha)
283 {
284 ImGui::SetNextWindowBgAlpha(alpha);
285 }
286
set_next_window_size(float x,float y,ImGuiCond cond)287 void ImGuiWrapper::set_next_window_size(float x, float y, ImGuiCond cond)
288 {
289 ImGui::SetNextWindowSize(ImVec2(x, y), cond);
290 }
291
begin(const std::string & name,int flags)292 bool ImGuiWrapper::begin(const std::string &name, int flags)
293 {
294 return ImGui::Begin(name.c_str(), nullptr, (ImGuiWindowFlags)flags);
295 }
296
begin(const wxString & name,int flags)297 bool ImGuiWrapper::begin(const wxString &name, int flags)
298 {
299 return begin(into_u8(name), flags);
300 }
301
begin(const std::string & name,bool * close,int flags)302 bool ImGuiWrapper::begin(const std::string& name, bool* close, int flags)
303 {
304 return ImGui::Begin(name.c_str(), close, (ImGuiWindowFlags)flags);
305 }
306
begin(const wxString & name,bool * close,int flags)307 bool ImGuiWrapper::begin(const wxString& name, bool* close, int flags)
308 {
309 return begin(into_u8(name), close, flags);
310 }
311
end()312 void ImGuiWrapper::end()
313 {
314 ImGui::End();
315 }
316
button(const wxString & label)317 bool ImGuiWrapper::button(const wxString &label)
318 {
319 auto label_utf8 = into_u8(label);
320 return ImGui::Button(label_utf8.c_str());
321 }
322
button(const wxString & label,float width,float height)323 bool ImGuiWrapper::button(const wxString& label, float width, float height)
324 {
325 auto label_utf8 = into_u8(label);
326 return ImGui::Button(label_utf8.c_str(), ImVec2(width, height));
327 }
328
radio_button(const wxString & label,bool active)329 bool ImGuiWrapper::radio_button(const wxString &label, bool active)
330 {
331 auto label_utf8 = into_u8(label);
332 return ImGui::RadioButton(label_utf8.c_str(), active);
333 }
334
image_button()335 bool ImGuiWrapper::image_button()
336 {
337 return false;
338 }
339
input_double(const std::string & label,const double & value,const std::string & format)340 bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
341 {
342 return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str());
343 }
344
input_double(const wxString & label,const double & value,const std::string & format)345 bool ImGuiWrapper::input_double(const wxString &label, const double &value, const std::string &format)
346 {
347 auto label_utf8 = into_u8(label);
348 return input_double(label_utf8, value, format);
349 }
350
input_vec3(const std::string & label,const Vec3d & value,float width,const std::string & format)351 bool ImGuiWrapper::input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format)
352 {
353 bool value_changed = false;
354
355 ImGui::BeginGroup();
356
357 for (int i = 0; i < 3; ++i)
358 {
359 std::string item_label = (i == 0) ? "X" : ((i == 1) ? "Y" : "Z");
360 ImGui::PushID(i);
361 ImGui::PushItemWidth(width);
362 value_changed |= ImGui::InputDouble(item_label.c_str(), const_cast<double*>(&value(i)), 0.0f, 0.0f, format.c_str());
363 ImGui::PopID();
364 }
365 ImGui::EndGroup();
366
367 return value_changed;
368 }
369
checkbox(const wxString & label,bool & value)370 bool ImGuiWrapper::checkbox(const wxString &label, bool &value)
371 {
372 auto label_utf8 = into_u8(label);
373 return ImGui::Checkbox(label_utf8.c_str(), &value);
374 }
375
text(const char * label)376 void ImGuiWrapper::text(const char *label)
377 {
378 ImGui::Text("%s", label);
379 }
380
text(const std::string & label)381 void ImGuiWrapper::text(const std::string &label)
382 {
383 this->text(label.c_str());
384 }
385
text(const wxString & label)386 void ImGuiWrapper::text(const wxString &label)
387 {
388 auto label_utf8 = into_u8(label);
389 this->text(label_utf8.c_str());
390 }
391
text_colored(const ImVec4 & color,const char * label)392 void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
393 {
394 ImGui::TextColored(color, "%s", label);
395 }
396
text_colored(const ImVec4 & color,const std::string & label)397 void ImGuiWrapper::text_colored(const ImVec4& color, const std::string& label)
398 {
399 this->text_colored(color, label.c_str());
400 }
401
text_colored(const ImVec4 & color,const wxString & label)402 void ImGuiWrapper::text_colored(const ImVec4& color, const wxString& label)
403 {
404 auto label_utf8 = into_u8(label);
405 this->text_colored(color, label_utf8.c_str());
406 }
407
slider_float(const char * label,float * v,float v_min,float v_max,const char * format,float power)408 bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/)
409 {
410 return ImGui::SliderFloat(label, v, v_min, v_max, format, power);
411 }
412
slider_float(const std::string & label,float * v,float v_min,float v_max,const char * format,float power)413 bool ImGuiWrapper::slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/)
414 {
415 return this->slider_float(label.c_str(), v, v_min, v_max, format, power);
416 }
417
slider_float(const wxString & label,float * v,float v_min,float v_max,const char * format,float power)418 bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/)
419 {
420 auto label_utf8 = into_u8(label);
421 return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power);
422 }
423
combo(const wxString & label,const std::vector<std::string> & options,int & selection)424 bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection)
425 {
426 // this is to force the label to the left of the widget:
427 text(label);
428 ImGui::SameLine();
429
430 int selection_out = selection;
431 bool res = false;
432
433 const char *selection_str = selection < int(options.size()) && selection >= 0 ? options[selection].c_str() : "";
434 if (ImGui::BeginCombo("", selection_str)) {
435 for (int i = 0; i < (int)options.size(); i++) {
436 if (ImGui::Selectable(options[i].c_str(), i == selection)) {
437 selection_out = i;
438 }
439 }
440
441 ImGui::EndCombo();
442 res = true;
443 }
444
445 selection = selection_out;
446 return res;
447 }
448
449 // Scroll up for one item
scroll_up()450 static void scroll_up()
451 {
452 ImGuiContext& g = *GImGui;
453 ImGuiWindow* window = g.CurrentWindow;
454
455 float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
456 float win_top = window->Scroll.y;
457
458 ImGui::SetScrollY(win_top - item_size_y);
459 }
460
461 // Scroll down for one item
scroll_down()462 static void scroll_down()
463 {
464 ImGuiContext& g = *GImGui;
465 ImGuiWindow* window = g.CurrentWindow;
466
467 float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
468 float win_top = window->Scroll.y;
469
470 ImGui::SetScrollY(win_top + item_size_y);
471 }
472
process_mouse_wheel(int & mouse_wheel)473 static void process_mouse_wheel(int& mouse_wheel)
474 {
475 if (mouse_wheel > 0)
476 scroll_up();
477 else if (mouse_wheel < 0)
478 scroll_down();
479 mouse_wheel = 0;
480 }
481
undo_redo_list(const ImVec2 & size,const bool is_undo,bool (* items_getter)(const bool,int,const char **),int & hovered,int & selected,int & mouse_wheel)482 bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected, int& mouse_wheel)
483 {
484 bool is_hovered = false;
485 ImGui::ListBoxHeader("", size);
486
487 int i=0;
488 const char* item_text;
489 while (items_getter(is_undo, i, &item_text))
490 {
491 ImGui::Selectable(item_text, i < hovered);
492
493 if (ImGui::IsItemHovered()) {
494 ImGui::SetTooltip("%s", item_text);
495 hovered = i;
496 is_hovered = true;
497 }
498
499 if (ImGui::IsItemClicked())
500 selected = i;
501 i++;
502 }
503
504 if (is_hovered)
505 process_mouse_wheel(mouse_wheel);
506
507 ImGui::ListBoxFooter();
508 return is_hovered;
509 }
510
511 // It's a copy of IMGui::Selactable function.
512 // But a little beat modified to change a label text.
513 // If item is hovered we should use another color for highlighted letters.
514 // To do that we push a ColorMarkerHovered symbol at the very beginning of the label
515 // This symbol will be used to a color selection for the highlighted letters.
516 // see imgui_draw.cpp, void ImFont::RenderText()
selectable(const char * label,bool selected,ImGuiSelectableFlags flags=0,const ImVec2 & size_arg=ImVec2 (0,0))517 static bool selectable(const char* label, bool selected, ImGuiSelectableFlags flags = 0, const ImVec2& size_arg = ImVec2(0, 0))
518 {
519 ImGuiWindow* window = ImGui::GetCurrentWindow();
520 if (window->SkipItems)
521 return false;
522
523 ImGuiContext& g = *GImGui;
524 const ImGuiStyle& style = g.Style;
525
526 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) // FIXME-OPT: Avoid if vertically clipped.
527 ImGui::PushColumnsBackground();
528
529 ImGuiID id = window->GetID(label);
530 ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
531 ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
532 ImVec2 pos = window->DC.CursorPos;
533 pos.y += window->DC.CurrLineTextBaseOffset;
534 ImRect bb_inner(pos, pos + size);
535 ImGui::ItemSize(size, 0.0f);
536
537 // Fill horizontal space.
538 ImVec2 window_padding = window->WindowPadding;
539 float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? ImGui::GetWindowContentRegionMax().x : ImGui::GetContentRegionMax().x;
540 float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - pos.x);
541 ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
542 ImRect bb(pos, pos + size_draw);
543 if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
544 bb.Max.x += window_padding.x;
545
546 // Selectables are tightly packed together so we extend the box to cover spacing between selectable.
547 const float spacing_x = style.ItemSpacing.x;
548 const float spacing_y = style.ItemSpacing.y;
549 const float spacing_L = IM_FLOOR(spacing_x * 0.50f);
550 const float spacing_U = IM_FLOOR(spacing_y * 0.50f);
551 bb.Min.x -= spacing_L;
552 bb.Min.y -= spacing_U;
553 bb.Max.x += (spacing_x - spacing_L);
554 bb.Max.y += (spacing_y - spacing_U);
555
556 bool item_add;
557 if (flags & ImGuiSelectableFlags_Disabled)
558 {
559 ImGuiItemFlags backup_item_flags = window->DC.ItemFlags;
560 window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus;
561 item_add = ImGui::ItemAdd(bb, id);
562 window->DC.ItemFlags = backup_item_flags;
563 }
564 else
565 {
566 item_add = ImGui::ItemAdd(bb, id);
567 }
568 if (!item_add)
569 {
570 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns)
571 ImGui::PopColumnsBackground();
572 return false;
573 }
574
575 // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
576 ImGuiButtonFlags button_flags = 0;
577 if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
578 if (flags & ImGuiSelectableFlags_PressedOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
579 if (flags & ImGuiSelectableFlags_PressedOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
580 if (flags & ImGuiSelectableFlags_Disabled) { button_flags |= ImGuiButtonFlags_Disabled; }
581 if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
582 if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; }
583
584 if (flags & ImGuiSelectableFlags_Disabled)
585 selected = false;
586
587 const bool was_selected = selected;
588 bool hovered, held;
589 bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, button_flags);
590
591 // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
592 if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
593 {
594 if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
595 {
596 g.NavDisableHighlight = true;
597 ImGui::SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent);
598 }
599 }
600 if (pressed)
601 ImGui::MarkItemEdited(id);
602
603 if (flags & ImGuiSelectableFlags_AllowItemOverlap)
604 ImGui::SetItemAllowOverlap();
605
606 // In this branch, Selectable() cannot toggle the selection so this will never trigger.
607 if (selected != was_selected) //-V547
608 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
609
610 // Render
611 if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld))
612 hovered = true;
613 if (hovered || selected)
614 {
615 const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
616 ImGui::RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
617 ImGui::RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
618 }
619
620 if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns)
621 {
622 ImGui::PopColumnsBackground();
623 bb.Max.x -= (ImGui::GetContentRegionMax().x - max_x);
624 }
625
626 // mark a label with a ImGui::ColorMarkerHovered, if item is hovered
627 char* marked_label = new char[512]; //255 symbols is not enough for translated string (e.t. to Russian)
628 if (hovered)
629 sprintf(marked_label, "%c%s", ImGui::ColorMarkerHovered, label);
630 else
631 strcpy(marked_label, label);
632
633 if (flags & ImGuiSelectableFlags_Disabled) ImGui::PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
634 ImGui::RenderTextClipped(bb_inner.Min, bb_inner.Max, marked_label, NULL, &label_size, style.SelectableTextAlign, &bb);
635 if (flags & ImGuiSelectableFlags_Disabled) ImGui::PopStyleColor();
636
637 delete[] marked_label;
638
639 // Automatically close popups
640 if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) ImGui::CloseCurrentPopup();
641
642 IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
643 return pressed;
644 }
645
646 // Scroll so that the hovered item is at the top of the window
scroll_y(int hover_id)647 static void scroll_y(int hover_id)
648 {
649 if (hover_id < 0)
650 return;
651 ImGuiContext& g = *GImGui;
652 ImGuiWindow* window = g.CurrentWindow;
653
654 float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
655 float item_delta = 0.5 * item_size_y;
656
657 float item_top = item_size_y * hover_id;
658 float item_bottom = item_top + item_size_y;
659
660 float win_top = window->Scroll.y;
661 float win_bottom = window->Scroll.y + window->Size.y;
662
663 if (item_bottom + item_delta >= win_bottom)
664 ImGui::SetScrollY(win_top + item_size_y);
665 else if (item_top - item_delta <= win_top)
666 ImGui::SetScrollY(win_top - item_size_y);
667 }
668
669 // Use this function instead of ImGui::IsKeyPressed.
670 // ImGui::IsKeyPressed is related for *GImGui.IO.KeysDownDuration[user_key_index]
671 // And after first key pressing IsKeyPressed() return "true" always even if key wasn't pressed
process_key_down(ImGuiKey imgui_key,std::function<void ()> f)672 static void process_key_down(ImGuiKey imgui_key, std::function<void()> f)
673 {
674 if (ImGui::IsKeyDown(ImGui::GetKeyIndex(imgui_key)))
675 {
676 f();
677 // set KeysDown to false to avoid redundant key down processing
678 ImGuiContext& g = *GImGui;
679 g.IO.KeysDown[ImGui::GetKeyIndex(imgui_key)] = false;
680 }
681 }
682
search_list(const ImVec2 & size_,bool (* items_getter)(int,const char ** label,const char ** tooltip),char * search_str,Search::OptionViewParameters & view_params,int & selected,bool & edited,int & mouse_wheel,bool is_localized)683 void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
684 Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized)
685 {
686 int& hovered_id = view_params.hovered_id;
687 // ImGui::ListBoxHeader("", size);
688 {
689 // rewrote part of function to add a TextInput instead of label Text
690 ImGuiContext& g = *GImGui;
691 ImGuiWindow* window = ImGui::GetCurrentWindow();
692 if (window->SkipItems)
693 return ;
694
695 const ImGuiStyle& style = g.Style;
696
697 // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
698 ImVec2 size = ImGui::CalcItemSize(size_, ImGui::CalcItemWidth(), ImGui::GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
699 ImRect frame_bb(window->DC.CursorPos, ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y + size.y));
700
701 ImRect bb(frame_bb.Min, frame_bb.Max);
702 window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy.
703 g.NextItemData.ClearFlags();
704
705 if (!ImGui::IsRectVisible(bb.Min, bb.Max))
706 {
707 ImGui::ItemSize(bb.GetSize(), style.FramePadding.y);
708 ImGui::ItemAdd(bb, 0, &frame_bb);
709 return ;
710 }
711
712 ImGui::BeginGroup();
713
714 const ImGuiID id = ImGui::GetID(search_str);
715 ImVec2 search_size = ImVec2(size.x, ImGui::GetTextLineHeightWithSpacing() + style.ItemSpacing.y);
716
717 if (!ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))
718 ImGui::SetKeyboardFocusHere(0);
719
720 // The press on Esc key invokes editing of InputText (removes last changes)
721 // So we should save previous value...
722 std::string str = search_str;
723 ImGui::InputTextEx("", NULL, search_str, 40, search_size, ImGuiInputTextFlags_AutoSelectAll, NULL, NULL);
724 edited = ImGui::IsItemEdited();
725 if (edited)
726 hovered_id = 0;
727
728 process_key_down(ImGuiKey_Escape, [&selected, search_str, str]() {
729 // use 9999 to mark selection as a Esc key
730 selected = 9999;
731 // ... and when Esc key was pressed, than revert search_str value
732 strcpy(search_str, str.c_str());
733 });
734
735 ImGui::BeginChildFrame(id, frame_bb.GetSize());
736 }
737
738 int i = 0;
739 const char* item_text;
740 const char* tooltip;
741 int mouse_hovered = -1;
742
743 while (items_getter(i, &item_text, &tooltip))
744 {
745 selectable(item_text, i == hovered_id);
746
747 if (ImGui::IsItemHovered()) {
748 ImGui::SetTooltip("%s", /*item_text*/tooltip);
749 hovered_id = -1;
750 mouse_hovered = i;
751 }
752
753 if (ImGui::IsItemClicked())
754 selected = i;
755 i++;
756 }
757
758 // Process mouse wheel
759 if (mouse_hovered > 0)
760 process_mouse_wheel(mouse_wheel);
761
762 // process Up/DownArrows and Enter
763 process_key_down(ImGuiKey_UpArrow, [&hovered_id, mouse_hovered]() {
764 if (mouse_hovered > 0)
765 scroll_up();
766 else {
767 if (hovered_id > 0)
768 --hovered_id;
769 scroll_y(hovered_id);
770 }
771 });
772
773 process_key_down(ImGuiKey_DownArrow, [&hovered_id, mouse_hovered, i]() {
774 if (mouse_hovered > 0)
775 scroll_down();
776 else {
777 if (hovered_id < 0)
778 hovered_id = 0;
779 else if (hovered_id < i - 1)
780 ++hovered_id;
781 scroll_y(hovered_id);
782 }
783 });
784
785 process_key_down(ImGuiKey_Enter, [&selected, hovered_id]() {
786 selected = hovered_id;
787 });
788
789 ImGui::ListBoxFooter();
790
791 auto check_box = [&edited, this](const wxString& label, bool& check) {
792 ImGui::SameLine();
793 bool ch = check;
794 checkbox(label, ch);
795 if (ImGui::IsItemClicked()) {
796 check = !check;
797 edited = true;
798 }
799 };
800
801 ImGui::AlignTextToFramePadding();
802
803 // add checkboxes for show/hide Categories and Groups
804 text(_L("Use for search")+":");
805 check_box(_L("Category"), view_params.category);
806 if (is_localized)
807 check_box(_L("Search in English"), view_params.english);
808 }
809
title(const std::string & str)810 void ImGuiWrapper::title(const std::string& str)
811 {
812 text(str);
813 ImGui::Separator();
814 }
815
disabled_begin(bool disabled)816 void ImGuiWrapper::disabled_begin(bool disabled)
817 {
818 wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
819
820 if (disabled) {
821 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
822 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
823 m_disabled = true;
824 }
825 }
826
disabled_end()827 void ImGuiWrapper::disabled_end()
828 {
829 if (m_disabled) {
830 ImGui::PopItemFlag();
831 ImGui::PopStyleVar();
832 m_disabled = false;
833 }
834 }
835
want_mouse() const836 bool ImGuiWrapper::want_mouse() const
837 {
838 return ImGui::GetIO().WantCaptureMouse;
839 }
840
want_keyboard() const841 bool ImGuiWrapper::want_keyboard() const
842 {
843 return ImGui::GetIO().WantCaptureKeyboard;
844 }
845
want_text_input() const846 bool ImGuiWrapper::want_text_input() const
847 {
848 return ImGui::GetIO().WantTextInput;
849 }
850
want_any_input() const851 bool ImGuiWrapper::want_any_input() const
852 {
853 const auto io = ImGui::GetIO();
854 return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
855 }
856
857 #ifdef __APPLE__
858 static const ImWchar ranges_keyboard_shortcuts[] =
859 {
860 0x21E7, 0x21E7, // OSX Shift Key symbol
861 0x2318, 0x2318, // OSX Command Key symbol
862 0x2325, 0x2325, // OSX Option Key symbol
863 0,
864 };
865 #endif // __APPLE__
866
867
load_svg(const std::string & bitmap_name,unsigned target_width,unsigned target_height)868 std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height)
869 {
870 std::vector<unsigned char> empty_vector;
871
872 #ifdef __WXMSW__
873 std::string folder = "white\\";
874 #else
875 std::string folder = "white/";
876 #endif
877 if (!boost::filesystem::exists(Slic3r::var(folder + bitmap_name + ".svg")))
878 folder.clear();
879
880 NSVGimage* image = ::nsvgParseFromFile(Slic3r::var(folder + bitmap_name + ".svg").c_str(), "px", 96.0f);
881 if (image == nullptr)
882 return empty_vector;
883
884 float svg_scale = target_height != 0 ?
885 (float)target_height / image->height : target_width != 0 ?
886 (float)target_width / image->width : 1;
887
888 int width = (int)(svg_scale * image->width + 0.5f);
889 int height = (int)(svg_scale * image->height + 0.5f);
890 int n_pixels = width * height;
891 if (n_pixels <= 0) {
892 ::nsvgDelete(image);
893 return empty_vector;
894 }
895
896 NSVGrasterizer* rast = ::nsvgCreateRasterizer();
897 if (rast == nullptr) {
898 ::nsvgDelete(image);
899 return empty_vector;
900 }
901
902 std::vector<unsigned char> data(n_pixels * 4, 0);
903 ::nsvgRasterize(rast, image, 0, 0, svg_scale, data.data(), width, height, width * 4);
904 ::nsvgDeleteRasterizer(rast);
905 ::nsvgDelete(image);
906
907 return data;
908 }
909
init_font(bool compress)910 void ImGuiWrapper::init_font(bool compress)
911 {
912 destroy_font();
913
914 ImGuiIO& io = ImGui::GetIO();
915 io.Fonts->Clear();
916
917 // Create ranges of characters from m_glyph_ranges, possibly adding some OS specific special characters.
918 ImVector<ImWchar> ranges;
919 ImFontAtlas::GlyphRangesBuilder builder;
920 builder.AddRanges(m_glyph_ranges);
921 #ifdef __APPLE__
922 if (m_font_cjk)
923 // Apple keyboard shortcuts are only contained in the CJK fonts.
924 builder.AddRanges(ranges_keyboard_shortcuts);
925 #endif
926 builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted)
927
928 //FIXME replace with io.Fonts->AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, m_font_size, nullptr, ranges.Data);
929 //https://github.com/ocornut/imgui/issues/220
930 ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + (m_font_cjk ? "NotoSansCJK-Regular.ttc" : "NotoSans-Regular.ttf")).c_str(), m_font_size, nullptr, ranges.Data);
931 if (font == nullptr) {
932 font = io.Fonts->AddFontDefault();
933 if (font == nullptr) {
934 throw Slic3r::RuntimeError("ImGui: Could not load deafult font");
935 }
936 }
937
938 #ifdef __APPLE__
939 ImFontConfig config;
940 config.MergeMode = true;
941 if (! m_font_cjk) {
942 // Apple keyboard shortcuts are only contained in the CJK fonts.
943 ImFont *font_cjk = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSansCJK-Regular.ttc").c_str(), m_font_size, &config, ranges_keyboard_shortcuts);
944 assert(font_cjk != nullptr);
945 }
946 #endif
947
948 float font_scale = m_font_size/15;
949 int icon_sz = lround(16 * font_scale); // default size of icon is 16 px
950
951 int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next
952 // add rectangles for the icons to the font atlas
953 for (auto& icon : font_icons)
954 io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
955 for (auto& icon : font_icons_large)
956 io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2);
957
958 // Build texture atlas
959 unsigned char* pixels;
960 int width, height;
961 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
962
963 // Fill rectangles from the SVG-icons
964 for (auto icon : font_icons) {
965 if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) {
966 assert(rect->Width == icon_sz);
967 assert(rect->Height == icon_sz);
968 std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz);
969 const ImU32* pIn = (ImU32*)raw_data.data();
970 for (int y = 0; y < icon_sz; y++) {
971 ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
972 for (int x = 0; x < icon_sz; x++)
973 *pOut++ = *pIn++;
974 }
975 }
976 rect_id++;
977 }
978
979 icon_sz *= 2; // default size of large icon is 32 px
980 for (auto icon : font_icons_large) {
981 if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) {
982 assert(rect->Width == icon_sz);
983 assert(rect->Height == icon_sz);
984 std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz);
985 const ImU32* pIn = (ImU32*)raw_data.data();
986 for (int y = 0; y < icon_sz; y++) {
987 ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
988 for (int x = 0; x < icon_sz; x++)
989 *pOut++ = *pIn++;
990 }
991 }
992 rect_id++;
993 }
994
995 // Upload texture to graphics system
996 GLint last_texture;
997 glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
998 glsafe(::glGenTextures(1, &m_font_texture));
999 glsafe(::glBindTexture(GL_TEXTURE_2D, m_font_texture));
1000 glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
1001 glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
1002 glsafe(::glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
1003 if (compress && GLEW_EXT_texture_compression_s3tc)
1004 glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
1005 else
1006 glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
1007
1008 // Store our identifier
1009 io.Fonts->TexID = (ImTextureID)(intptr_t)m_font_texture;
1010
1011 // Restore state
1012 glsafe(::glBindTexture(GL_TEXTURE_2D, last_texture));
1013 }
1014
init_input()1015 void ImGuiWrapper::init_input()
1016 {
1017 ImGuiIO& io = ImGui::GetIO();
1018
1019 // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
1020 io.KeyMap[ImGuiKey_Tab] = WXK_TAB;
1021 io.KeyMap[ImGuiKey_LeftArrow] = WXK_LEFT;
1022 io.KeyMap[ImGuiKey_RightArrow] = WXK_RIGHT;
1023 io.KeyMap[ImGuiKey_UpArrow] = WXK_UP;
1024 io.KeyMap[ImGuiKey_DownArrow] = WXK_DOWN;
1025 io.KeyMap[ImGuiKey_PageUp] = WXK_PAGEUP;
1026 io.KeyMap[ImGuiKey_PageDown] = WXK_PAGEDOWN;
1027 io.KeyMap[ImGuiKey_Home] = WXK_HOME;
1028 io.KeyMap[ImGuiKey_End] = WXK_END;
1029 io.KeyMap[ImGuiKey_Insert] = WXK_INSERT;
1030 io.KeyMap[ImGuiKey_Delete] = WXK_DELETE;
1031 io.KeyMap[ImGuiKey_Backspace] = WXK_BACK;
1032 io.KeyMap[ImGuiKey_Space] = WXK_SPACE;
1033 io.KeyMap[ImGuiKey_Enter] = WXK_RETURN;
1034 io.KeyMap[ImGuiKey_Escape] = WXK_ESCAPE;
1035 io.KeyMap[ImGuiKey_A] = 'A';
1036 io.KeyMap[ImGuiKey_C] = 'C';
1037 io.KeyMap[ImGuiKey_V] = 'V';
1038 io.KeyMap[ImGuiKey_X] = 'X';
1039 io.KeyMap[ImGuiKey_Y] = 'Y';
1040 io.KeyMap[ImGuiKey_Z] = 'Z';
1041
1042 // Don't let imgui special-case Mac, wxWidgets already do that
1043 io.ConfigMacOSXBehaviors = false;
1044
1045 // Setup clipboard interaction callbacks
1046 io.SetClipboardTextFn = clipboard_set;
1047 io.GetClipboardTextFn = clipboard_get;
1048 io.ClipboardUserData = this;
1049 }
1050
init_style()1051 void ImGuiWrapper::init_style()
1052 {
1053 ImGuiStyle &style = ImGui::GetStyle();
1054
1055 auto set_color = [&](ImGuiCol_ entity, ImVec4 color) {
1056 style.Colors[entity] = color;
1057 };
1058
1059 // Window
1060 style.WindowRounding = 4.0f;
1061 set_color(ImGuiCol_WindowBg, COL_WINDOW_BACKGROUND);
1062 set_color(ImGuiCol_TitleBgActive, COL_ORANGE_DARK);
1063
1064 // Generics
1065 set_color(ImGuiCol_FrameBg, COL_GREY_DARK);
1066 set_color(ImGuiCol_FrameBgHovered, COL_GREY_LIGHT);
1067 set_color(ImGuiCol_FrameBgActive, COL_GREY_LIGHT);
1068
1069 // Text selection
1070 set_color(ImGuiCol_TextSelectedBg, COL_ORANGE_DARK);
1071
1072 // Buttons
1073 set_color(ImGuiCol_Button, COL_BUTTON_BACKGROUND);
1074 set_color(ImGuiCol_ButtonHovered, COL_BUTTON_HOVERED);
1075 set_color(ImGuiCol_ButtonActive, COL_BUTTON_ACTIVE);
1076
1077 // Checkbox
1078 set_color(ImGuiCol_CheckMark, COL_ORANGE_LIGHT);
1079
1080 // ComboBox items
1081 set_color(ImGuiCol_Header, COL_ORANGE_DARK);
1082 set_color(ImGuiCol_HeaderHovered, COL_ORANGE_LIGHT);
1083 set_color(ImGuiCol_HeaderActive, COL_ORANGE_LIGHT);
1084
1085 // Slider
1086 set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK);
1087 set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT);
1088
1089 // Separator
1090 set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT);
1091
1092 // Tabs
1093 set_color(ImGuiCol_Tab, COL_ORANGE_DARK);
1094 set_color(ImGuiCol_TabHovered, COL_ORANGE_LIGHT);
1095 set_color(ImGuiCol_TabActive, COL_ORANGE_LIGHT);
1096 set_color(ImGuiCol_TabUnfocused, COL_GREY_DARK);
1097 set_color(ImGuiCol_TabUnfocusedActive, COL_GREY_LIGHT);
1098 }
1099
render_draw_data(ImDrawData * draw_data)1100 void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
1101 {
1102 // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
1103 ImGuiIO& io = ImGui::GetIO();
1104 int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x);
1105 int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y);
1106 if (fb_width == 0 || fb_height == 0)
1107 return;
1108 draw_data->ScaleClipRects(io.DisplayFramebufferScale);
1109
1110 // We are using the OpenGL fixed pipeline to make the example code simpler to read!
1111 // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill.
1112 GLint last_texture; glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));
1113 GLint last_polygon_mode[2]; glsafe(::glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode));
1114 GLint last_viewport[4]; glsafe(::glGetIntegerv(GL_VIEWPORT, last_viewport));
1115 GLint last_scissor_box[4]; glsafe(::glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box));
1116 glsafe(::glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT));
1117 glsafe(::glEnable(GL_BLEND));
1118 glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
1119 glsafe(::glDisable(GL_CULL_FACE));
1120 glsafe(::glDisable(GL_DEPTH_TEST));
1121 glsafe(::glDisable(GL_LIGHTING));
1122 glsafe(::glDisable(GL_COLOR_MATERIAL));
1123 glsafe(::glEnable(GL_SCISSOR_TEST));
1124 glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
1125 glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY));
1126 glsafe(::glEnableClientState(GL_COLOR_ARRAY));
1127 glsafe(::glEnable(GL_TEXTURE_2D));
1128 glsafe(::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL));
1129 glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE));
1130 GLint texture_env_mode = GL_MODULATE;
1131 glsafe(::glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &texture_env_mode));
1132 glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE));
1133 //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound
1134
1135 // Setup viewport, orthographic projection matrix
1136 // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
1137 glsafe(::glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height));
1138 glsafe(::glMatrixMode(GL_PROJECTION));
1139 glsafe(::glPushMatrix());
1140 glsafe(::glLoadIdentity());
1141 glsafe(::glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f));
1142 glsafe(::glMatrixMode(GL_MODELVIEW));
1143 glsafe(::glPushMatrix());
1144 glsafe(::glLoadIdentity());
1145
1146 // Render command lists
1147 ImVec2 pos = draw_data->DisplayPos;
1148 for (int n = 0; n < draw_data->CmdListsCount; n++)
1149 {
1150 const ImDrawList* cmd_list = draw_data->CmdLists[n];
1151 const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
1152 const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
1153 glsafe(::glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))));
1154 glsafe(::glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))));
1155 glsafe(::glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))));
1156
1157 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
1158 {
1159 const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
1160 if (pcmd->UserCallback)
1161 {
1162 // User callback (registered via ImDrawList::AddCallback)
1163 pcmd->UserCallback(cmd_list, pcmd);
1164 }
1165 else
1166 {
1167 ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y);
1168 if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
1169 {
1170 // Apply scissor/clipping rectangle
1171 glsafe(::glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)));
1172
1173 // Bind texture, Draw
1174 glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId));
1175 glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer));
1176 }
1177 }
1178 idx_buffer += pcmd->ElemCount;
1179 }
1180 }
1181
1182 // Restore modified state
1183 glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, texture_env_mode));
1184 glsafe(::glDisableClientState(GL_COLOR_ARRAY));
1185 glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY));
1186 glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
1187 glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture));
1188 glsafe(::glMatrixMode(GL_MODELVIEW));
1189 glsafe(::glPopMatrix());
1190 glsafe(::glMatrixMode(GL_PROJECTION));
1191 glsafe(::glPopMatrix());
1192 glsafe(::glPopAttrib());
1193 glsafe(::glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]));
1194 glsafe(::glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]));
1195 glsafe(::glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]));
1196 }
1197
display_initialized() const1198 bool ImGuiWrapper::display_initialized() const
1199 {
1200 const ImGuiIO& io = ImGui::GetIO();
1201 return io.DisplaySize.x >= 0.0f && io.DisplaySize.y >= 0.0f;
1202 }
1203
destroy_font()1204 void ImGuiWrapper::destroy_font()
1205 {
1206 if (m_font_texture != 0) {
1207 ImGuiIO& io = ImGui::GetIO();
1208 io.Fonts->TexID = 0;
1209 glsafe(::glDeleteTextures(1, &m_font_texture));
1210 m_font_texture = 0;
1211 }
1212 }
1213
clipboard_get(void * user_data)1214 const char* ImGuiWrapper::clipboard_get(void* user_data)
1215 {
1216 ImGuiWrapper *self = reinterpret_cast<ImGuiWrapper*>(user_data);
1217
1218 const char* res = "";
1219
1220 if (wxTheClipboard->Open()) {
1221 if (wxTheClipboard->IsSupported(wxDF_TEXT)) {
1222 wxTextDataObject data;
1223 wxTheClipboard->GetData(data);
1224
1225 if (data.GetTextLength() > 0) {
1226 self->m_clipboard_text = into_u8(data.GetText());
1227 res = self->m_clipboard_text.c_str();
1228 }
1229 }
1230
1231 wxTheClipboard->Close();
1232 }
1233
1234 return res;
1235 }
1236
clipboard_set(void *,const char * text)1237 void ImGuiWrapper::clipboard_set(void* /* user_data */, const char* text)
1238 {
1239 if (wxTheClipboard->Open()) {
1240 wxTheClipboard->SetData(new wxTextDataObject(wxString::FromUTF8(text))); // object owned by the clipboard
1241 wxTheClipboard->Close();
1242 }
1243 }
1244
1245
1246 } // namespace GUI
1247 } // namespace Slic3r
1248