27#include "freertos/FreeRTOS.h"
28#include "freertos/timers.h"
39#include "images/bitmaps.h"
43#pragma GCC optimize ("O2")
56void dumpEvent(uiEvent * event)
59 static const char * TOSTR[] = {
"UIEVT_NULL",
"UIEVT_DEBUGMSG",
"UIEVT_APPINIT",
"UIEVT_GENPAINTEVENTS",
"UIEVT_PAINT",
"UIEVT_ACTIVATE",
60 "UIEVT_DEACTIVATE",
"UIEVT_MOUSEMOVE",
"UIEVT_MOUSEWHEEL",
"UIEVT_MOUSEBUTTONDOWN",
61 "UIEVT_MOUSEBUTTONUP",
"UIEVT_SETPOS",
"UIEVT_SETSIZE",
"UIEVT_RESHAPEWINDOW",
62 "UIEVT_MOUSEENTER",
"UIEVT_MOUSELEAVE",
"UIEVT_MAXIMIZE",
"UIEVT_MINIMIZE",
"UIEVT_RESTORE",
63 "UIEVT_SHOW",
"UIEVT_HIDE",
"UIEVT_SETFOCUS",
"UIEVT_KILLFOCUS",
"UIEVT_KEYDOWN",
"UIEVT_KEYUP",
"UIEVT_KEYTYPE",
64 "UIEVT_TIMER",
"UIEVT_CLICK",
"UIEVT_DBLCLICK",
"UIEVT_EXITMODAL",
"UIEVT_DESTROY",
"UIEVT_CLOSE",
65 "UIEVT_QUIT",
"UIEVT_CREATE",
"UIEVT_CHILDSETFOCUS",
"UIEVT_CHILDKILLFOCUS"
67 printf(
"#%d ", idx++);
68 printf(TOSTR[event->id]);
69 if (event->dest && event->dest->objectType().uiFrame && ((
uiFrame*)(event->dest))->title())
70 printf(
" dst=\"%s\"(%p) ", ((
uiFrame*)(event->dest))->title(), event->dest);
72 printf(
" dst=%p ", event->dest);
74 auto ot =
event->dest->objectType();
76 if (ot.uiApp) printf(
"uiApp ");
77 if (ot.uiEvtHandler) printf(
"uiEvtHandler ");
78 if (ot.uiWindow) printf(
"uiWindow ");
79 if (ot.uiFrame) printf(
"uiFrame ");
80 if (ot.uiControl) printf(
"uiControl ");
81 if (ot.uiScrollableControl) printf(
"uiScrollableControl ");
82 if (ot.uiButton) printf(
"uiButton ");
83 if (ot.uiTextEdit) printf(
"uiTextEdit ");
84 if (ot.uiLabel) printf(
"uiLabel ");
85 if (ot.uiStaticLabel) printf(
"uiStaticLabel ");
86 if (ot.uiImage) printf(
"uiImage ");
87 if (ot.uiPanel) printf(
"uiPanel ");
88 if (ot.uiPaintBox) printf(
"uiPaintBox ");
89 if (ot.uiCustomListBox) printf(
"uiCustomListBox ");
90 if (ot.uiListBox) printf(
"uiListBox ");
91 if (ot.uiFileBrowser) printf(
"uiFileBrowser ");
92 if (ot.uiComboBox) printf(
"uiComboBox ");
93 if (ot.uiCheckBox) printf(
"uiCheckBox ");
94 if (ot.uiSlider) printf(
"uiSlider ");
95 if (ot.uiColorListBox) printf(
"uiColorListBox ");
96 if (ot.uiCustomComboBox) printf(
"uiCustomComboBox ");
97 if (ot.uiColorBox) printf(
"uiColorBox ");
98 if (ot.uiColorComboBox) printf(
"uiColorComboBox ");
99 if (ot.uiProgressBar) printf(
"uiProgressBar ");
100 if (ot.uiSplitButton) printf(
"uiSplitButton ");
101 if (ot.uiSimpleMenu) printf(
"uiSimpleMenu ");
106 printf(event->params.debugMsg);
108 case UIEVT_MOUSEMOVE:
109 printf(
"X=%d Y=%d", event->params.mouse.status.X, event->params.mouse.status.Y);
111 case UIEVT_MOUSEWHEEL:
112 printf(
"delta=%d", event->params.mouse.status.wheelDelta);
114 case UIEVT_MOUSEBUTTONDOWN:
115 case UIEVT_MOUSEBUTTONUP:
117 printf(
"btn=%d", event->params.mouse.changedButton);
120 case UIEVT_GENPAINTEVENTS:
121 case UIEVT_RESHAPEWINDOW:
122 printf(
"rect=%d,%d,%d,%d", event->params.rect.X1, event->params.rect.Y1, event->params.rect.X2, event->params.rect.Y2);
125 printf(
"pos=%d,%d", event->params.pos.X, event->params.pos.Y);
128 printf(
"size=%d,%d", event->params.size.width, event->params.size.height);
133 #ifdef FABGLIB_HAS_VirtualKeyO_STRING
134 printf(
"VK=%s ", Keyboard::virtualKeyToString(event->params.key.VK));
135 if (event->params.key.LALT) printf(
" +LALT");
136 if (event->params.key.RALT) printf(
" +RALT");
137 if (event->params.key.CTRL) printf(
" +CTRL");
138 if (event->params.key.SHIFT) printf(
" +SHIFT");
139 if (event->params.key.GUI) printf(
" +GUI");
143 printf(
"handle=%p", event->params.timerHandle);
177uiEvtHandler::uiEvtHandler(
uiApp * app)
180 objectType().uiEvtHandler =
true;
184uiEvtHandler::~uiEvtHandler()
187 m_app->killEvtHandlerTimers(
this);
191void uiEvtHandler::processEvent(uiEvent * event)
212 : uiEvtHandler(nullptr),
213 m_rootWindow(nullptr),
214 m_activeWindow(nullptr),
215 m_focusedWindow(nullptr),
216 m_lastFocusedWindow(nullptr),
217 m_capturedMouseWindow(nullptr),
218 m_freeMouseWindow(nullptr),
219 m_modalWindow(nullptr),
220 m_combineMouseMoveEvents(false),
221 m_keyDownHandler(nullptr),
222 m_caretWindow(nullptr),
223 m_caretTimer(nullptr),
224 m_caretInvertState(-1),
225 m_lastMouseUpTimeMS(0),
228 objectType().uiApp =
true;
240 m_displayController = displayController;
241 m_displayColors = displayController->
colorsCount();
243 m_canvas =
new Canvas(m_displayController);
245 m_keyboard = keyboard;
247 if (PS2Controller::initialized()) {
249 if (m_keyboard ==
nullptr)
251 if (m_mouse ==
nullptr)
255 m_eventsQueue = xQueueCreate(FABGLIB_UI_EVENTS_QUEUE_SIZE,
sizeof(uiEvent));
267 m_rootWindow->setApp(
this);
282 m_lastUserActionTimeMS = esp_timer_get_time() / 1000;
286 m_activeWindow = m_rootWindow;
289 uiEvent evt = uiEvent(
this, UIEVT_APPINIT);
297 if (getEvent(&event, -1)) {
299 preprocessEvent(&event);
307 event.dest->processEvent(&event);
309 if (event.id == UIEVT_QUIT) {
310 exitCode =
event.params.exitCode;
316 killEvtHandlerTimers(
this);
328 m_rootWindow =
nullptr;
336 vQueueDelete(m_eventsQueue);
337 m_eventsQueue =
nullptr;
345void uiApp::asyncRunTask(
void * arg)
349 if (
app->m_asyncRunWait)
350 xSemaphoreGive(
app->m_asyncRunWait);
357 m_displayController = displayController;
358 m_keyboard = keyboard;
360 m_asyncRunWait =
nullptr;
362 if (CoreUsage::busiestCore() == -1)
363 xTaskCreate(&asyncRunTask,
"", taskStack,
this, 5,
nullptr);
365 xTaskCreatePinnedToCore(&asyncRunTask,
"", taskStack,
this, 5,
nullptr, CoreUsage::quietCore());
373 m_asyncRunWait = xSemaphoreCreateBinary();
374 xSemaphoreTake(m_asyncRunWait, portMAX_DELAY);
375 vSemaphoreDelete(m_asyncRunWait);
376 m_asyncRunWait =
nullptr;
383 while (getEvent(&event, 0)) {
385 preprocessEvent(&event);
388 printf(
"processEvents(): ");
393 event.dest->processEvent(&event);
400 for (
auto child = m_rootWindow->
lastChild(); child; child = child->
prev())
402 uiEvent evt = uiEvent(
nullptr, UIEVT_QUIT);
403 evt.params.exitCode = exitCode;
408void uiApp::preprocessEvent(uiEvent * event)
410 if (event->dest ==
nullptr) {
413 case UIEVT_MOUSEMOVE:
414 case UIEVT_MOUSEWHEEL:
415 case UIEVT_MOUSEBUTTONDOWN:
416 case UIEVT_MOUSEBUTTONUP:
417 preprocessMouseEvent(event);
421 preprocessKeyboardEvent(event);
430 if (event->params.timerHandle == m_caretTimer) {
432 event->dest =
nullptr;
442 if (m_modalWindow !=
nullptr)
443 filterModalEvent(event);
448void uiApp::filterModalEvent(uiEvent * event)
450 if (event->dest !=
nullptr &&
451 event->dest->objectType().uiWindow &&
452 event->dest != m_modalWindow &&
453 event->dest != m_activeWindow &&
454 event->dest != m_focusedWindow &&
455 !m_modalWindow->isChild((uiWindow*)event->dest)) {
457 case UIEVT_MOUSEMOVE:
458 case UIEVT_MOUSEWHEEL:
459 case UIEVT_MOUSEBUTTONDOWN:
460 case UIEVT_MOUSEBUTTONUP:
461 case UIEVT_MOUSEENTER:
464 event->dest =
nullptr;
465 if (event->id == UIEVT_MOUSEENTER) {
480void uiApp::preprocessMouseEvent(uiEvent * event)
483 if (m_combineMouseMoveEvents && event->id == UIEVT_MOUSEMOVE) {
485 while (peekEvent(&nextEvent, 0) && nextEvent.id == UIEVT_MOUSEMOVE)
489 m_lastUserActionTimeMS = esp_timer_get_time() / 1000;
491 Point mousePos = Point(event->params.mouse.status.X, event->params.mouse.status.Y);
495 uiWindow * oldFreeMouseWindow = m_freeMouseWindow;
496 Point winMousePos = mousePos;
497 if (m_capturedMouseWindow) {
499 for (uiWindow * cur = m_capturedMouseWindow; cur != m_rootWindow; cur = cur->
parent())
500 winMousePos = winMousePos.sub(cur->pos());
501 event->dest = m_capturedMouseWindow;
503 if (event->id == UIEVT_MOUSEBUTTONUP && event->params.mouse.changedButton == 1) {
507 uiEvent evt = uiEvent(m_capturedMouseWindow, UIEVT_MOUSELEAVE);
509 m_freeMouseWindow = oldFreeMouseWindow =
nullptr;
511 captureMouse(
nullptr);
515 event->dest = m_freeMouseWindow;
517 event->params.mouse.status.X = winMousePos.X;
518 event->params.mouse.status.Y = winMousePos.Y;
521 if (oldFreeMouseWindow != m_freeMouseWindow) {
522 if (m_freeMouseWindow) {
523 uiEvent evt = uiEvent(m_freeMouseWindow, UIEVT_MOUSEENTER);
526 if (oldFreeMouseWindow) {
527 uiEvent evt = uiEvent(oldFreeMouseWindow, UIEVT_MOUSELEAVE);
533 if (event->id == UIEVT_MOUSEBUTTONUP && event->params.mouse.changedButton == 1) {
534 int curTime = esp_timer_get_time() / 1000;
535 if (m_lastMouseUpPos == mousePos && curTime - m_lastMouseUpTimeMS <= m_appProps.
doubleClickTime) {
537 uiEvent evt = *event;
538 evt.id = UIEVT_DBLCLICK;
541 m_lastMouseUpTimeMS = curTime;
542 m_lastMouseUpPos = mousePos;
547void uiApp::preprocessKeyboardEvent(uiEvent * event)
549 m_lastUserActionTimeMS = esp_timer_get_time() / 1000;
552 if (m_focusedWindow) {
553 event->dest = m_focusedWindow;
556 if (m_focusedWindow != m_activeWindow) {
557 uiEvent evt = *event;
558 evt.dest = m_activeWindow;
562 if (event->id == UIEVT_KEYDOWN)
563 m_keyDownHandler =
event->dest;
564 else if (event->id == UIEVT_KEYUP && m_keyDownHandler == event->dest) {
565 uiEvent evt = uiEvent(event->dest, UIEVT_KEYTYPE);
566 evt.params.key =
event->params.key;
573void uiApp::captureMouse(uiWindow * window)
575 m_capturedMouseWindow = window;
576 if (m_capturedMouseWindow)
590 for (; child; child = child->
prev()) {
593 point = point.sub(child->
pos());
597 if (child ==
nullptr)
611 return xQueueSendToBack(m_eventsQueue, event, 0) == pdTRUE;
617 return xQueueSendToFront(m_eventsQueue, event, 0) == pdTRUE;
621void uiApp::postDebugMsg(
char const * msg)
623 uiEvent evt = uiEvent(
nullptr, UIEVT_DEBUGMSG);
624 evt.params.debugMsg = msg;
629bool uiApp::getEvent(uiEvent * event,
int timeOutMS)
631 return xQueueReceive(m_eventsQueue, event, msToTicks(timeOutMS)) == pdTRUE;
635bool uiApp::peekEvent(uiEvent * event,
int timeOutMS)
637 return xQueuePeek(m_eventsQueue, event, msToTicks(timeOutMS)) == pdTRUE;
641void uiApp::processEvent(uiEvent * event)
643 uiEvtHandler::processEvent(event);
652 onTimer(event->params.timerHandle);
667 if (value != m_activeWindow) {
669 while (value && !value->m_windowProps.activable) {
670 value = value->m_parent;
674 if (value == m_activeWindow)
683 m_activeWindow = value;
687 uiEvent evt = uiEvent(prev, UIEVT_DEACTIVATE);
691 if (m_activeWindow) {
693 uiEvent evt = uiEvent(m_activeWindow, UIEVT_ACTIVATE);
709 if (value && !value->isFocusable())
712 if (m_focusedWindow != value) {
718 m_lastFocusedWindow = prev;
720 uiEvent evt = uiEvent(prev, UIEVT_KILLFOCUS);
721 evt.params.focusInfo.oldFocused = m_lastFocusedWindow;
722 evt.params.focusInfo.newFocused = value;
726 evt = uiEvent(prev->
parent(), UIEVT_CHILDKILLFOCUS);
727 evt.params.focusInfo.oldFocused = m_lastFocusedWindow;
728 evt.params.focusInfo.newFocused = value;
733 m_focusedWindow = value;
738 if (m_focusedWindow) {
739 uiEvent evt = uiEvent(m_focusedWindow, UIEVT_SETFOCUS);
740 evt.params.focusInfo.oldFocused = m_lastFocusedWindow;
741 evt.params.focusInfo.newFocused = m_focusedWindow;
743 if (m_focusedWindow->
parent()) {
745 evt = uiEvent(m_focusedWindow->
parent(), UIEVT_CHILDSETFOCUS);
746 evt.params.focusInfo.oldFocused = m_lastFocusedWindow;
747 evt.params.focusInfo.newFocused = m_focusedWindow;
764 int startingIndex = m_focusedWindow ? m_focusedWindow->
focusIndex() + delta : 0;
766 int newIndex = startingIndex;
769 uiWindow * newFocusedCtrl = parent->findChildWithFocusIndex(newIndex, &maxIndex);
770 if (maxIndex == -1) {
771 return m_focusedWindow;
773 if (newFocusedCtrl) {
775 return newFocusedCtrl;
778 newIndex = (newIndex >= maxIndex ? 0 : newIndex + delta);
780 newIndex = (newIndex <= 0 ? maxIndex : newIndex + delta);
781 }
while (newIndex != startingIndex);
783 return m_focusedWindow;
795 uiEvent evt = uiEvent(m_rootWindow, UIEVT_GENPAINTEVENTS);
796 evt.params.rect = rect;
823 uiEvent evt = uiEvent(window, UIEVT_RESHAPEWINDOW);
824 evt.params.rect = rect;
831 window->m_state.
visible = value;
832 uiEvent evt = uiEvent(window, value ? UIEVT_SHOW : UIEVT_HIDE);
841 auto state =
new ModalWindowState;
842 state->window = window;
843 state->modalResult = -1;
846 state->prevModal = m_modalWindow;
859 while (getEvent(&event, timeout)) {
861 if (m_modalWindow != state->window && event.dest == state->window) {
863 m_modalWindow = state->window;
866 preprocessEvent(&event);
869 printf(
"processModalWindowEvents(): ");
873 if (event.id == UIEVT_EXITMODAL && event.dest == state->window) {
875 state->modalResult =
event.params.modalResult;
877 }
else if (event.id == UIEVT_CLOSE) {
883 event.dest->processEvent(&event);
893 m_modalWindow = state->prevModal;
897 int result = state->modalResult;
914 uiEvent evt = uiEvent(frame, value ? UIEVT_MAXIMIZE : UIEVT_RESTORE);
921 uiEvent evt = uiEvent(frame, value ? UIEVT_MINIMIZE : UIEVT_RESTORE);
926void uiApp::timerFunc(TimerHandle_t xTimer)
929 uiEvent evt = uiEvent(dest, UIEVT_TIMER);
930 evt.params.timerHandle = xTimer;
938 TimerHandle_t h = xTimerCreate(
"", pdMS_TO_TICKS(periodMS), pdTRUE, dest, &uiApp::timerFunc);
939 m_timers.push_back(uiTimerAssoc(dest, h));
948 m_timers.remove(uiTimerAssoc(dest, handle));
949 xTimerStop(handle, portMAX_DELAY);
950 xTimerDelete(handle, portMAX_DELAY);
956 for (
auto t : m_timers)
957 if (t.first == dest) {
958 xTimerStop(t.second, portMAX_DELAY);
959 xTimerDelete(t.second, portMAX_DELAY);
961 m_timers.remove_if([&](uiTimerAssoc
const & p) {
return p.first == dest; });
967void uiApp::showCaret(uiWindow * window)
969 if (m_caretWindow != window) {
970 if (window && window == m_focusedWindow) {
972 m_caretWindow = window;
974 m_caretInvertState = 0;
976 }
else if (m_caretTimer) {
980 m_caretTimer =
nullptr;
981 m_caretWindow = NULL;
987void uiApp::suspendCaret(
bool value)
991 if (m_caretInvertState != -1) {
992 xTimerStop(m_caretTimer, 0);
994 m_caretInvertState = -1;
997 if (m_caretInvertState == -1) {
998 xTimerStart(m_caretTimer, 0);
999 m_caretInvertState = 0;
1008void uiApp::setCaret(
bool value)
1014void uiApp::setCaret(Point
const & pos)
1016 setCaret(m_caretRect.move(pos));
1020void uiApp::setCaret(Rect
const & rect)
1028void uiApp::blinkCaret(
bool forceOFF)
1030 if (m_caretWindow && m_caretInvertState != -1 && (forceOFF ==
false || m_caretInvertState == 1)) {
1034 Rect aRect = m_caretWindow->
transformRect(m_caretRect, m_rootWindow);
1036 m_caretInvertState = m_caretInvertState ? 0 : 1;
1046 for (
auto child = window->
lastChild(); child; child = child->
prev())
1049 if (m_caretWindow == window)
1051 if (m_focusedWindow == window)
1053 if (m_activeWindow == window)
1058 uiEvent evt = uiEvent(window, UIEVT_DESTROY);
1064void uiApp::cleanWindowReferences(
uiWindow * window)
1066 if (m_capturedMouseWindow == window)
1067 m_capturedMouseWindow =
nullptr;
1068 if (m_freeMouseWindow == window)
1069 m_freeMouseWindow =
nullptr;
1070 if (m_activeWindow == window)
1071 m_activeWindow =
nullptr;
1072 if (m_focusedWindow == window)
1073 m_focusedWindow =
nullptr;
1074 if (m_modalWindow == window)
1075 m_modalWindow =
nullptr;
1076 if (m_caretWindow == window)
1077 m_caretWindow =
nullptr;
1083 auto font = &FONT_std_14;
1084 const int titleHeight = title && strlen(title) ? font->height : 0;
1085 const int textExtent = m_canvas->
textExtent(font, text);
1086 const int button1Extent = button1Text ? m_canvas->
textExtent(font, button1Text) + 10 : 0;
1087 const int button2Extent = button2Text ? m_canvas->
textExtent(font, button2Text) + 10 : 0;
1088 const int button3Extent = button3Text ? m_canvas->
textExtent(font, button3Text) + 10 : 0;
1089 const int buttonsWidth = imax(imax(imax(button1Extent, button2Extent), button3Extent), 40);
1097 const int buttonsHeight = font->height + 6;
1102 const int bitmapWidth = bitmap ? bitmap->width : 0;
1103 const int bitmapHeight = bitmap ? bitmap->height : 0;
1104 constexpr int buttonsSpace = 10;
1105 const int bitmapSpace = bitmap ? 8 : 0;
1106 const int textHeight = imax(font->height, bitmapHeight);
1107 const int requiredWidth = imin(imax(bitmapWidth + bitmapSpace + textExtent + 10, buttonsWidth * totButtons + (2 + buttonsSpace) * totButtons), m_canvas->
getWidth());
1108 const int requiredHeight = textHeight + buttonsHeight + titleHeight + font->height * 3;
1109 const int frameX = (m_canvas->
getWidth() - requiredWidth) / 2;
1110 const int frameY = (m_canvas->
getHeight() - requiredHeight) / 2;
1112 auto mainFrame =
new uiFrame(m_rootWindow, title,
Point(frameX, frameY),
Size(requiredWidth, requiredHeight),
false);
1113 mainFrame->frameProps().resizeable =
false;
1114 mainFrame->frameProps().hasMaximizeButton =
false;
1115 mainFrame->frameProps().hasMinimizeButton =
false;
1117 int x = (requiredWidth - bitmapWidth - bitmapSpace - textExtent) / 2;
1119 int y = font->height + titleHeight + (textHeight - bitmapHeight) / 2;
1121 x += bitmapWidth + bitmapSpace;
1124 int y = font->height + titleHeight + (textHeight - font->height) / 2;
1129 y += textHeight + titleHeight;
1130 auto panel =
new uiPanel(mainFrame,
Point(0, y),
Size(mainFrame->size().width, mainFrame->size().height - y));
1131 panel->windowStyle().borderColor =
RGB888(128, 128, 128);
1132 panel->panelStyle().backgroundColor = mainFrame->frameStyle().backgroundColor;
1136 y = (panel->size().height - buttonsHeight) / 2;
1137 x = mainFrame->windowStyle().borderSize + requiredWidth - buttonsWidth * totButtons - buttonsSpace * totButtons;
1139 auto button1 = button1Text ?
new uiButton(panel, button1Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1141 button1->onClick = [&]() { mainFrame->exitModal(1); };
1142 x += buttonsWidth + buttonsSpace;
1145 auto button2 = button2Text ?
new uiButton(panel, button2Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1147 button2->onClick = [&]() { mainFrame->exitModal(2); };
1148 x += buttonsWidth + buttonsSpace;
1151 auto button3 = button3Text ?
new uiButton(panel, button3Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1153 button3->onClick = [&]() { mainFrame->exitModal(3); };
1154 x += buttonsWidth + buttonsSpace;
1158 mainFrame->onShow = [&]() {
1167 switch (modalResult) {
1182 auto font = &FONT_std_14;
1183 const int titleHeight = title && strlen(title) ? font->height : 0;
1184 const int textExtent = m_canvas->
textExtent(font, text);
1185 const int editExtent = imin(maxLength * m_canvas->
textExtent(font,
"M"), m_rootWindow->
clientSize().
width / 2 - textExtent);
1186 const int button1Extent = button1Text ? m_canvas->
textExtent(font, button1Text) + 10 : 0;
1187 const int button2Extent = button2Text ? m_canvas->
textExtent(font, button2Text) + 10 : 0;
1188 const int buttonsWidth = imax(imax(button1Extent, button2Extent), 40);
1194 const int buttonsHeight = font->height + 6;
1195 const int textHeight = font->height;
1196 constexpr int buttonsSpace = 10;
1197 const int requiredWidth = imin(imax(editExtent + textExtent + 10, buttonsWidth * totButtons + (2 + buttonsSpace) * totButtons), m_canvas->
getWidth());
1198 const int requiredHeight = textHeight + buttonsHeight + titleHeight + font->height * 3;
1199 const int frameX = (m_canvas->
getWidth() - requiredWidth) / 2;
1200 const int frameY = (m_canvas->
getHeight() - requiredHeight) / 2;
1202 auto mainFrame =
new uiFrame(m_rootWindow, title,
Point(frameX, frameY),
Size(requiredWidth, requiredHeight),
false);
1203 mainFrame->frameProps().resizeable =
false;
1204 mainFrame->frameProps().hasMaximizeButton =
false;
1205 mainFrame->frameProps().hasMinimizeButton =
false;
1208 mainFrame->exitModal(1);
1210 mainFrame->exitModal(0);
1214 int y = font->height + titleHeight + (textHeight - font->height) / 2;
1217 auto edit =
new uiTextEdit(mainFrame, inOutString,
Point(x + textExtent + 5, y - 4),
Size(editExtent - 15, textHeight + 6));
1221 y += textHeight + titleHeight;
1222 auto panel =
new uiPanel(mainFrame,
Point(0, y),
Size(mainFrame->size().width, mainFrame->size().height - y));
1223 panel->windowStyle().borderColor =
RGB888(128, 128, 128);
1224 panel->panelStyle().backgroundColor = mainFrame->frameStyle().backgroundColor;
1228 y = (panel->size().height - buttonsHeight) / 2;
1229 x = mainFrame->windowStyle().borderSize + requiredWidth - buttonsWidth * totButtons - buttonsSpace * totButtons;
1231 auto button1 = button1Text ?
new uiButton(panel, button1Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1233 button1->onClick = [&]() { mainFrame->exitModal(1); };
1234 x += buttonsWidth + buttonsSpace;
1237 auto button2 = button2Text ?
new uiButton(panel, button2Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1239 button2->onClick = [&]() { mainFrame->exitModal(2); };
1240 x += buttonsWidth + buttonsSpace;
1244 mainFrame->onShow = [&]() {
1253 switch (modalResult) {
1256 int len = imin(maxLength, strlen(edit->text()));
1257 memcpy(inOutString, edit->text(), len);
1258 inOutString[len] = 0;
1269uiMessageBoxResult uiApp::fileDialog(
char const * title,
char * inOutDirectory,
int maxDirNameSize,
char * inOutFilename,
int maxFileNameSize,
char const * buttonOKText,
char const * buttonCancelText,
int frameWidth,
int frameHeight)
1271 auto mainFrame =
new uiFrame(m_rootWindow, title, UIWINDOW_PARENTCENTER,
Size(frameWidth, frameHeight),
false);
1272 mainFrame->frameProps().resizeable =
false;
1273 mainFrame->frameProps().hasMaximizeButton =
false;
1274 mainFrame->frameProps().hasMinimizeButton =
false;
1277 mainFrame->exitModal(1);
1279 mainFrame->exitModal(0);
1283 constexpr int x = 8;
1284 constexpr int hh = 20;
1285 constexpr int dy = hh + 8;
1286 constexpr int lbloy = 3;
1288 constexpr int fnBorder = 20;
1290 auto filenameEdit =
new uiTextEdit(mainFrame, inOutFilename,
Point(x + 50 + fnBorder, y),
Size(frameWidth - x - 58 - fnBorder * 2, hh));
1294 auto browser =
new uiFileBrowser(mainFrame,
Point(x, y),
Size(frameWidth - x * 2, frameHeight - y - 35));
1295 browser->setDirectory(inOutDirectory);
1296 browser->onChange = [&]() {
1297 if (!browser->isDirectory()) {
1298 filenameEdit->setText(browser->filename());
1299 filenameEdit->repaint();
1302 browser->onDblClick = [&]() {
1303 if (!browser->isDirectory())
1304 mainFrame->exitModal(1);
1307 y += browser->clientSize().height + (dy - hh);
1312 auto buttonCancel =
new uiButton(mainFrame, buttonCancelText,
Point(frameWidth - buttonCancelLen - buttonOKLen - 20, y),
Size(buttonCancelLen, hh));
1313 auto buttonOK =
new uiButton(mainFrame, buttonOKText,
Point(frameWidth - buttonOKLen - 8, y),
Size(buttonOKLen, hh));
1315 buttonCancel->onClick = [&]() { mainFrame->exitModal(0); };
1316 buttonOK->onClick = [&]() { mainFrame->exitModal(1); };
1319 mainFrame->onShow = [&]() {
1326 switch (modalResult) {
1329 int len = imin(maxDirNameSize, strlen(browser->directory()));
1330 memcpy(inOutDirectory, browser->directory(), len);
1331 inOutDirectory[len] = 0;
1333 len = imin(maxFileNameSize, strlen(filenameEdit->text()));
1334 memcpy(inOutFilename, filenameEdit->text(), len);
1335 inOutFilename[len] = 0;
1381 m_firstChild(nullptr),
1382 m_lastChild(nullptr),
1383 m_styleClassID(styleClassID),
1384 m_isMouseOver(false),
1385 m_parentProcessKbdEvents(false)
1393 m_windowStyle.adaptToDisplayColors(
app()->displayColors());
1398 if (m_pos == UIWINDOW_PARENTCENTER) {
1402 m_pos =
Point(0, 0);
1409 if (visible &&
app())
1413 m_focusIndex = pframe ? ((
uiFrame*)pframe)->getNextFreeFocusIndex() : 0;
1416 uiEvent evt = uiEvent(
this, UIEVT_CREATE);
1422uiWindow::~uiWindow()
1428void uiWindow::freeChildren()
1434 m_firstChild = m_lastChild =
nullptr;
1438void uiWindow::addChild(uiWindow * child)
1442 m_lastChild->m_next = child;
1443 child->m_prev = m_lastChild;
1444 m_lastChild = child;
1447 m_firstChild = m_lastChild = child;
1454void uiWindow::insertAfter(uiWindow * child, uiWindow * underlyingChild)
1461 child->m_prev = underlyingChild;
1462 if (underlyingChild) {
1464 child->m_next = underlyingChild->m_next;
1466 child->m_next->m_prev = child;
1467 underlyingChild->m_next = child;
1468 if (m_lastChild == underlyingChild)
1469 m_lastChild = child;
1472 m_firstChild->m_prev = child;
1473 child->m_next = m_firstChild;
1474 m_firstChild = child;
1479void uiWindow::removeChild(uiWindow * child,
bool freeChild)
1482 if (child == m_firstChild)
1483 m_firstChild = child->m_next;
1485 child->m_prev->m_next = child->m_next;
1487 if (child == m_lastChild)
1488 m_lastChild = child->m_prev;
1490 child->m_next->m_prev = child->m_prev;
1494 app()->cleanWindowReferences(child);
1496 child->m_prev = child->m_next =
nullptr;
1503void uiWindow::moveChildOnTop(uiWindow * child)
1505 removeChild(child,
false);
1512void uiWindow::moveAfter(uiWindow * child, uiWindow * underlyingChild)
1514 removeChild(child,
false);
1515 insertAfter(child, underlyingChild);
1521 parent()->moveChildOnTop(
this);
1527 parent()->moveAfter(
this, insertionPoint);
1532bool uiWindow::isChild(
uiWindow * window)
1535 if (child == window || (child->hasChildren() && child->isChild(window)))
1545 for (
uiWindow * win =
this; win != baseWindow; win = win->m_parent)
1546 r = r.translate(win->m_pos);
1583 return rect(origin).shrink(bSize);
1599void uiWindow::beginPaint(uiEvent * paintEvent,
Rect const & clippingRect)
1603 canvas()->
setClippingRect( clippingRect.intersection(paintEvent->params.rect) );
1609void uiWindow::processEvent(uiEvent * event)
1611 uiEvtHandler::processEvent(event);
1613 switch (event->id) {
1616 m_parent->removeChild(
this);
1624 case UIEVT_ACTIVATE:
1629 for (
uiWindow * child =
this; child->parent() !=
nullptr; child = child->parent()) {
1630 if (child != child->parent()->lastChild()) {
1631 child->parent()->moveChildOnTop(child);
1632 winToRepaint = child;
1635 winToRepaint->repaint();
1639 case UIEVT_DEACTIVATE:
1644 case UIEVT_MOUSEBUTTONDOWN:
1645 if (event->params.mouse.changedButton == 1) {
1652 app()->captureMouse(
this);
1656 case UIEVT_MOUSEBUTTONUP:
1658 if (event->params.mouse.changedButton == 1) {
1660 if (
rect(
uiOrigin::Window).contains(event->params.mouse.status.X, event->params.mouse.status.Y)) {
1661 uiEvent evt = *event;
1662 evt.id = UIEVT_CLICK;
1676 case UIEVT_RESHAPEWINDOW:
1677 reshape(event->params.rect);
1680 case UIEVT_GENPAINTEVENTS:
1681 generatePaintEvents(event->params.rect);
1684 case UIEVT_MOUSEENTER:
1685 m_isMouseOver =
true;
1689 case UIEVT_MOUSELEAVE:
1690 m_isMouseOver =
false;
1694 if (m_parentProcessKbdEvents)
1695 m_parent->processEvent(event);
1699 if (m_parentProcessKbdEvents)
1700 m_parent->processEvent(event);
1708 case UIEVT_SETFOCUS:
1709 case UIEVT_KILLFOCUS:
1719void uiWindow::paintWindow()
1725 for (
int i = 0; i < bSize; ++i)
1732void uiWindow::generatePaintEvents(Rect
const & paintRect)
1734 app()->setCaret(
false);
1736 rects.push(paintRect);
1737 while (!rects.isEmpty()) {
1738 Rect thisRect = rects.pop();
1739 bool noIntesections =
true;
1742 if (win->state().visible && thisRect.intersects(winRect)) {
1743 noIntesections =
false;
1744 removeRectangle(rects, thisRect, winRect);
1745 Rect newRect = thisRect.intersection(winRect).translate(-win->pos().X, -win->pos().Y);
1746 win->generatePaintEvents(newRect);
1750 if (noIntesections) {
1751 uiEvent evt = uiEvent(
nullptr, UIEVT_PAINT);
1753 evt.params.rect = thisRect;
1764void uiWindow::reshape(Rect
const & r)
1772 if (oldRect == newRect)
1776 m_pos = Point(r.X1, r.Y1);
1779 if (!oldRect.intersects(newRect)) {
1784 removeRectangle(rects, oldRect, newRect);
1785 while (!rects.isEmpty())
1790 uiEvent evt = uiEvent(
this, UIEVT_SETPOS);
1791 evt.params.pos =
pos();
1795 evt = uiEvent(
this, UIEVT_SETSIZE);
1796 evt.params.size =
size();
1800 int dx = newRect.width() - oldRect.width();
1801 int dy = newRect.height() - oldRect.height();
1802 if (dx != 0 || dy != 0) {
1805 Rect newChildRect = childRect;
1807 if (!child->m_anchors.left && !child->m_anchors.right) {
1809 int ofs = dx > 0 ? imax(1, dx / 2) : imin(-1, dx / 2);
1810 newChildRect.X1 += ofs;
1811 newChildRect.X2 += ofs;
1812 }
else if (!child->m_anchors.left)
1813 newChildRect.X1 += dx;
1814 if (child->m_anchors.right)
1815 newChildRect.X2 += dx;
1818 if (!child->m_anchors.top && !child->m_anchors.bottom) {
1820 int ofs = dy > 0 ? imax(1, dy / 2) : imin(-1, dy / 2);
1821 newChildRect.Y1 += ofs;
1822 newChildRect.Y2 += ofs;
1823 }
else if (!child->m_anchors.top)
1824 newChildRect.Y1 += dy;
1825 if (child->m_anchors.bottom)
1826 newChildRect.Y2 += dy;
1828 if (newChildRect != childRect) {
1829 uiEvent evt = uiEvent(child, UIEVT_RESHAPEWINDOW);
1830 evt.params.rect = newChildRect;
1840Canvas * uiWindow::canvas()
1842 return app()->canvas();
1848 uiEvent evt = uiEvent(
this, UIEVT_EXITMODAL);
1849 evt.params.modalResult = modalResult;
1866bool uiWindow::isFocusable()
1873uiWindow * uiWindow::findChildWithFocusIndex(
int focusIndex,
int * maxIndex)
1875 for (
auto child = m_firstChild; child; child = child->m_next) {
1876 if (child->isFocusable()) {
1877 *maxIndex = imax(*maxIndex, child->m_focusIndex);
1882 if (child->hasChildren()) {
1883 auto r = child->findChildWithFocusIndex(
focusIndex, maxIndex);
1896 while (ret && ret->
objectType().uiFrame == 0)
1912 :
uiWindow(parent, pos, size, visible, 0),
1915 m_mouseDownFrameItem(uiFrameItem::None),
1916 m_mouseMoveFrameItem(uiFrameItem::None),
1917 m_lastReshapingBox(
Rect(0, 0, 0, 0)),
1918 m_nextFreeFocusIndex(0),
1919 m_mouseDownPos(
Point(-1, -1))
1927 m_frameStyle.adaptToDisplayColors(
app()->displayColors());
1944 m_titleLength = strlen(value);
1945 m_title = (
char*) realloc(m_title, m_titleLength + 1);
1946 strcpy(m_title, value);
1958 va_start(ap, format);
1959 int size = vsnprintf(
nullptr, 0, format, ap) + 1;
1962 va_start(ap, format);
1964 vsnprintf(buf,
size, format, ap);
1971int uiFrame::titleBarHeight()
1973 return m_frameStyle.
titleFont->height + 3;
1977Rect uiFrame::titleBarRect()
1980 r.Y2 = r.Y1 + titleBarHeight() - 1;
1990 if (m_titleLength > 0)
1991 r.
Y1 += titleBarHeight();
1997Size uiFrame::minWindowSize()
2001 r.
width += CORNERSENSE * 2;
2002 r.
height += CORNERSENSE * 2;
2006 if (m_titleLength > 0) {
2007 int barHeight = titleBarHeight();
2010 r.
width += barHeight * 3;
2011 r.
width += barHeight * 4;
2021Rect uiFrame::getBtnRect(
int buttonIndex)
2023 int btnSize = titleBarHeight();
2024 Rect barRect = titleBarRect();
2025 Rect btnRect = Rect(barRect.X2 - btnSize - CORNERSENSE / 2, barRect.Y1,
2026 barRect.X2 - CORNERSENSE / 2, barRect.Y2);
2027 while (buttonIndex--)
2028 btnRect = btnRect.translate(-btnSize, 0);
2033void uiFrame::paintFrame()
2037 if (m_titleLength > 0) {
2038 int barHeight = titleBarHeight();
2044 int btnX = paintButtons(bkgRect);
2050 bkgRect.Y1 += barHeight;
2061int uiFrame::paintButtons(Rect
const & bkgRect)
2063 int buttonsX = bkgRect.X2;
2066 Rect r = getBtnRect(0);
2068 if (m_mouseMoveFrameItem == uiFrameItem::CloseButton) {
2075 canvas()->
drawLine(r.X1, r.Y1, r.X2, r.Y2);
2076 canvas()->
drawLine(r.X2, r.Y1, r.X1, r.Y2);
2080 Rect r = getBtnRect(1);
2082 if (m_mouseMoveFrameItem == uiFrameItem::MaximizeButton) {
2091 r = r.shrink(1).translate(-1, +1);
2093 r = r.translate(+2, -2);
2094 canvas()->
moveTo(r.X1, r.Y1 + 2);
2095 canvas()->
lineTo(r.X1, r.Y1);
2096 canvas()->
lineTo(r.X2, r.Y1);
2097 canvas()->
lineTo(r.X2, r.Y2);
2098 canvas()->
lineTo(r.X2 - 2, r.Y2);
2104 Rect r = getBtnRect(2);
2106 if (m_mouseMoveFrameItem == uiFrameItem::MinimizeButton) {
2113 int h = (r.Y2 - r.Y1 + 1) / 2;
2114 canvas()->
drawLine(r.X1, r.Y1 + h, r.X2, r.Y1 + h);
2120void uiFrame::processEvent(uiEvent * event)
2122 uiWindow::processEvent(event);
2124 switch (event->id) {
2132 case UIEVT_MOUSEBUTTONDOWN:
2133 if (event->params.mouse.changedButton == 1) {
2134 m_mouseDownPos = Point(event->params.mouse.status.X, event->params.mouse.status.Y);
2135 m_mouseDownFrameItem = getFrameItemAt(event->params.mouse.status.X, event->params.mouse.status.Y);
2136 m_sizeAtMouseDown =
size();
2137 app()->combineMouseMoveEvents(
true);
2141 case UIEVT_MOUSEBUTTONUP:
2142 if (event->params.mouse.changedButton == 1) {
2143 int mouseX =
event->params.mouse.status.X;
2144 int mouseY =
event->params.mouse.status.Y;
2147 movingCapturedMouse(mouseX, mouseY,
false);
2150 movingFreeMouse(mouseX, mouseY);
2153 handleButtonsClick(mouseX, mouseY,
false);
2155 app()->combineMouseMoveEvents(
false);
2159 case UIEVT_MOUSEMOVE:
2160 if (
app()->capturedMouseWindow() ==
this)
2161 movingCapturedMouse(event->params.mouse.status.X, event->params.mouse.status.Y,
true);
2163 movingFreeMouse(event->params.mouse.status.X, event->params.mouse.status.Y);
2166 case UIEVT_MOUSELEAVE:
2167 if (m_mouseMoveFrameItem == uiFrameItem::CloseButton)
2169 if (m_mouseMoveFrameItem == uiFrameItem::MaximizeButton)
2171 if (m_mouseMoveFrameItem == uiFrameItem::MinimizeButton)
2173 m_mouseMoveFrameItem = uiFrameItem::None;
2176 case UIEVT_DBLCLICK:
2177 handleButtonsClick(event->params.mouse.status.X, event->params.mouse.status.Y,
true);
2188 case UIEVT_MAXIMIZE:
2196 case UIEVT_MINIMIZE:
2215 onTimer(event->params.timerHandle);
2220 if (event->params.key.VK ==
VK_TAB) {
2221 if (event->params.key.SHIFT)
2239uiFrameItem uiFrame::getFrameItemAt(
int x,
int y)
2241 Point p = Point(x, y);
2243 if (m_titleLength > 0) {
2245 return uiFrameItem::CloseButton;
2248 return uiFrameItem::MaximizeButton;
2251 return uiFrameItem::MinimizeButton;
2260 if (Rect(CORNERSENSE, 0, w - CORNERSENSE,
windowStyle().borderSize).contains(p))
2261 return uiFrameItem::TopCenterResize;
2264 if (Rect(0, CORNERSENSE,
windowStyle().borderSize, h - CORNERSENSE).contains(p))
2265 return uiFrameItem::CenterLeftResize;
2268 if (Rect(w -
windowStyle().borderSize, CORNERSENSE, w - 1, h - CORNERSENSE).contains(p))
2269 return uiFrameItem::CenterRightResize;
2272 if (Rect(CORNERSENSE, h -
windowStyle().borderSize, w - CORNERSENSE, h - 1).contains(p))
2273 return uiFrameItem::BottomCenterResize;
2276 if (Rect(0, 0, CORNERSENSE, CORNERSENSE).contains(p))
2277 return uiFrameItem::TopLeftResize;
2280 if (Rect(w - CORNERSENSE, 0, w - 1, CORNERSENSE).contains(p))
2281 return uiFrameItem::TopRightResize;
2284 if (Rect(0, h - CORNERSENSE, CORNERSENSE, h - 1).contains(p))
2285 return uiFrameItem::BottomLeftResize;
2288 if (Rect(w - CORNERSENSE, h - CORNERSENSE, w - 1, h - 1).contains(p))
2289 return uiFrameItem::BottomRightResize;
2294 if (m_titleLength > 0 && m_frameProps.
moveable && !m_frameState.
maximized && titleBarRect().contains(p))
2295 return uiFrameItem::MoveArea;
2297 return uiFrameItem::None;
2301void uiFrame::movingCapturedMouse(
int mouseX,
int mouseY,
bool mouseIsDown)
2303 int dx = mouseX - m_mouseDownPos.
X;
2304 int dy = mouseY - m_mouseDownPos.
Y;
2306 Size minSize = minWindowSize();
2310 switch (m_mouseDownFrameItem) {
2312 case uiFrameItem::MoveArea:
2313 newRect = newRect.move(
pos().
X + dx,
pos().
Y + dy);
2316 case uiFrameItem::CenterRightResize:
2317 newRect = newRect.resize(imax(m_sizeAtMouseDown.
width + dx, minSize.width), newRect.height());
2320 case uiFrameItem::CenterLeftResize:
2323 r.X1 =
pos().
X + dx;
2324 newRect.X1 = r.X1 - imax(0, minSize.width - r.size().width);
2328 case uiFrameItem::TopLeftResize:
2331 r.X1 =
pos().
X + dx;
2332 newRect.X1 = r.X1 - imax(0, minSize.width - r.size().width);
2333 r.Y1 =
pos().
Y + dy;
2334 newRect.Y1 = r.Y1 - imax(0, minSize.height - r.size().height);
2338 case uiFrameItem::TopCenterResize:
2341 r.Y1 =
pos().
Y + dy;
2342 newRect.Y1 = r.Y1 - imax(0, minSize.height - r.size().height);
2346 case uiFrameItem::TopRightResize:
2349 r.X2 =
pos().
X + m_sizeAtMouseDown.
width + dx;
2350 newRect.X2 = r.X2 + imax(0, minSize.width - r.size().width);
2351 r.Y1 =
pos().
Y + dy;
2352 newRect.Y1 = r.Y1 - imax(0, minSize.height - r.size().height);
2356 case uiFrameItem::BottomLeftResize:
2359 r.X1 =
pos().
X + dx;
2360 newRect.X1 = r.X1 - imax(0, minSize.width - r.size().width);
2361 r.Y2 =
pos().
Y + m_sizeAtMouseDown.
height + dy;
2362 newRect.Y2 = r.Y2 + imax(0, minSize.height - r.size().height);
2366 case uiFrameItem::BottomCenterResize:
2367 newRect = newRect.resize(newRect.width(), imax(m_sizeAtMouseDown.
height + dy, minSize.height));
2370 case uiFrameItem::BottomRightResize:
2371 newRect = newRect.resize(imax(m_sizeAtMouseDown.
width + dx, minSize.width), imax(m_sizeAtMouseDown.
height + dy, minSize.height));
2379 if (mouseIsDown ==
false || (
app()->appProps().realtimeReshaping && m_mouseDownFrameItem != uiFrameItem::MoveArea) || (
app()->appProps().realtimeMoving && m_mouseDownFrameItem == uiFrameItem::MoveArea)) {
2380 m_lastReshapingBox = Rect();
2383 drawReshapingBox(newRect);
2387void uiFrame::drawReshapingBox(Rect boxRect)
2395 if (m_lastReshapingBox != Rect()) {
2397 if (m_titleLength > 0)
2398 canvas()->
drawLine(m_lastReshapingBox.
X1, m_lastReshapingBox.
Y1 + clientOffsetY, m_lastReshapingBox.
X2, m_lastReshapingBox.
Y1 + clientOffsetY);
2400 if (boxRect != Rect()) {
2402 if (m_titleLength > 0)
2403 canvas()->
drawLine(boxRect.X1, boxRect.Y1 + clientOffsetY, boxRect.X2, boxRect.Y1 + clientOffsetY);
2406 m_lastReshapingBox = boxRect;
2410void uiFrame::movingFreeMouse(
int mouseX,
int mouseY)
2412 uiFrameItem prevSensPos = m_mouseMoveFrameItem;
2414 m_mouseMoveFrameItem = getFrameItemAt(mouseX, mouseY);
2416 if ((m_mouseMoveFrameItem == uiFrameItem::CloseButton || prevSensPos == uiFrameItem::CloseButton) && m_mouseMoveFrameItem != prevSensPos)
2419 if ((m_mouseMoveFrameItem == uiFrameItem::MaximizeButton || prevSensPos == uiFrameItem::MaximizeButton) && m_mouseMoveFrameItem != prevSensPos)
2422 if ((m_mouseMoveFrameItem == uiFrameItem::MinimizeButton || prevSensPos == uiFrameItem::MinimizeButton) && m_mouseMoveFrameItem != prevSensPos)
2427 switch (m_mouseMoveFrameItem) {
2429 case uiFrameItem::TopLeftResize:
2433 case uiFrameItem::TopCenterResize:
2437 case uiFrameItem::TopRightResize:
2441 case uiFrameItem::CenterLeftResize:
2445 case uiFrameItem::CenterRightResize:
2449 case uiFrameItem::BottomLeftResize:
2453 case uiFrameItem::BottomCenterResize:
2457 case uiFrameItem::BottomRightResize:
2469void uiFrame::handleButtonsClick(
int x,
int y,
bool doubleClick)
2471 if (m_titleLength > 0) {
2472 if (m_frameProps.
hasCloseButton && getBtnRect(0).contains(x, y) && getBtnRect(0).contains(m_mouseDownPos)) {
2474 uiEvent evt = uiEvent(
this, UIEVT_CLOSE);
2476 }
else if (m_frameProps.
hasMaximizeButton && ((getBtnRect(1).contains(x, y) && getBtnRect(1).contains(m_mouseDownPos)) ||
2477 (doubleClick && titleBarRect().contains(x, y)))) {
2482 }
else if (m_frameProps.
hasMinimizeButton && !m_frameState.
minimized && getBtnRect(2).contains(x, y) && getBtnRect(2).contains(m_mouseDownPos)) {
2487 m_mouseMoveFrameItem = uiFrameItem::None;
2502 :
uiWindow(parent, pos, size, visible, 0)
2512uiControl::~uiControl()
2517void uiControl::processEvent(uiEvent * event)
2519 uiWindow::processEvent(event);
2534 :
uiControl(parent, pos, size, visible, 0),
2549 m_buttonStyle.adaptToDisplayColors(
app()->displayColors());
2558uiButton::~uiButton()
2566 int len = strlen(value);
2567 m_text = (
char*) realloc(m_text, len + 1);
2568 strcpy(m_text, value);
2574void uiButton::paintButton()
2579 if (
app()->capturedMouseWindow() ==
this)
2586 paintContent(bkgRect);
2590void uiButton::paintContent(Rect
const & rect)
2592 Bitmap
const * bitmap = m_down ? m_buttonStyle.
downBitmap : m_buttonStyle.
bitmap;
2593 int textHeight = m_buttonStyle.
textFont->height;
2594 int bitmapWidth = bitmap ? bitmap->width : 0;
2595 int bitmapHeight = bitmap ? bitmap->height : 0;
2598 int x =
rect.
X1 + (
rect.size().
width - m_textExtent - bitmapTextSpace - bitmapWidth) / 2;
2599 int y =
rect.
Y1 + (
rect.size().
height - imax(textHeight, bitmapHeight)) / 2;
2603 x += bitmapWidth + bitmapTextSpace;
2604 y += (imax(textHeight, bitmapHeight) - textHeight) / 2;
2617void uiButton::processEvent(uiEvent * event)
2619 uiControl::processEvent(event);
2621 switch (event->id) {
2633 case UIEVT_MOUSEENTER:
2637 case UIEVT_MOUSEBUTTONDOWN:
2638 if (event->params.mouse.changedButton == 1)
2643 case UIEVT_MOUSEBUTTONUP:
2647 case UIEVT_MOUSELEAVE:
2665void uiButton::trigger()
2677 if (value != m_down) {
2696 :
uiControl(parent, pos, size, visible, 0),
2714 m_textEditStyle.adaptToDisplayColors(
app()->displayColors());
2723uiTextEdit::~uiTextEdit()
2732 m_textLength = strlen(value);
2733 checkAllocatedSpace(m_textLength);
2734 strcpy(m_text, value);
2736 m_text = strdup(
"");
2745 va_start(ap, format);
2746 int size = vsnprintf(
nullptr, 0, format, ap) + 1;
2749 va_start(ap, format);
2750 checkAllocatedSpace(
size + 1);
2751 vsnprintf(m_text,
size, format, ap);
2752 m_textLength = strlen(m_text);
2758void uiTextEdit::processEvent(uiEvent * event)
2760 uiControl::processEvent(event);
2762 switch (event->id) {
2768 app()->setCaret(
true);
2771 case UIEVT_MOUSEBUTTONDOWN:
2772 if (event->params.mouse.changedButton == 1) {
2773 int col = getColFromMouseX(event->params.mouse.status.X);
2774 moveCursor(col, col);
2779 case UIEVT_MOUSEBUTTONUP:
2782 case UIEVT_MOUSEENTER:
2786 case UIEVT_MOUSELEAVE:
2790 case UIEVT_MOUSEMOVE:
2792 if (
app()->capturedMouseWindow() ==
this)
2793 moveCursor(getColFromMouseX(event->params.mouse.status.X), m_selCursorCol);
2796 case UIEVT_SETFOCUS:
2799 app()->showCaret(
this);
2804 case UIEVT_KILLFOCUS:
2806 app()->showCaret(NULL);
2812 handleKeyDown(event->params.key);
2819 case UIEVT_DBLCLICK:
2820 selectWordAt(event->params.mouse.status.X);
2832 if (m_codepage ==
nullptr || m_codepage->codepage != m_textEditStyle.
textFont->codepage)
2833 m_codepage = CodePages::get(m_textEditStyle.
textFont->codepage);
2835 VirtualKeyItem item = { };
2837 item.CTRL = key.
CTRL;
2838 item.SHIFT = key.
SHIFT;
2839 return virtualKeyToASCII(item, m_codepage);
2849 if (m_cursorCol != m_selCursorCol)
2851 else if (m_cursorCol > 0) {
2853 moveCursor(m_cursorCol - 1, m_cursorCol - 1);
2869 auto ASCII = keyToASCII(key);
2871 if (ASCII >= 0x20 && ASCII != 0x7F) {
2872 if (m_cursorCol != m_selCursorCol)
2890 int newCurCol = key.
CTRL ? getWordPosAtLeft() : m_cursorCol - 1;
2891 moveCursor(newCurCol, (key.
SHIFT ? m_selCursorCol : newCurCol));
2902 int newCurCol = key.
CTRL ? getWordPosAtRight() : m_cursorCol + 1;
2903 moveCursor(newCurCol, (key.
SHIFT ? m_selCursorCol : newCurCol));
2911 moveCursor(0, (key.
SHIFT ? m_selCursorCol : 0));
2918 moveCursor(m_textLength, (key.
SHIFT ? m_selCursorCol : m_textLength));
2928 moveCursor(m_textLength, 0);
2940Rect uiTextEdit::getEditRect()
2946void uiTextEdit::paintTextEdit()
2948 m_contentRect = getEditRect();
2960uint8_t
const * uiTextEdit::getCharInfo(
char ch,
int *
width)
2964 uint8_t
const * chptr;
2965 if (m_textEditStyle.
textFont->chptr) {
2967 chptr = m_textEditStyle.
textFont->data + m_textEditStyle.
textFont->chptr[(int)(ch)];
2971 chptr = m_textEditStyle.
textFont->data + ch;
2978void uiTextEdit::paintContent()
2980 m_contentRect = m_contentRect.shrink(2);
2981 canvas()->
setClippingRect(canvas()->getClippingRect().intersection(m_contentRect));
2984 GlyphOptions glyphOpt = GlyphOptions().FillBackground(
false).DoubleWidth(0).Bold(
false).Italic(
false).Underline(
false).Invert(0);
2985 if (m_selCursorCol != m_cursorCol)
2986 glyphOpt.FillBackground(
true);
2989 for (
int x = m_contentRect.
X1 + m_viewX, y = m_contentRect.
Y1, col = 0, fontWidth; m_text[col]; ++col, x += fontWidth) {
2990 uint8_t
const * chptr = getCharInfo(m_text[col], &fontWidth);
2991 if (m_selCursorCol != m_cursorCol && (col == m_selCursorCol || col == m_cursorCol)) {
2992 glyphOpt.invert = !glyphOpt.invert;
2995 if (x >= m_contentRect.
X1 && x <= m_contentRect.
X2)
2996 canvas()->
drawGlyph(x, y, fontWidth, m_textEditStyle.
textFont->height, chptr, 0);
3004int uiTextEdit::charColumnToWindowX(
int col)
3006 int x = m_contentRect.
X1 + m_viewX;
3007 for (
int curcol = 0, fontWidth; m_text[curcol]; ++curcol, x += fontWidth) {
3008 getCharInfo(m_text[curcol], &fontWidth);
3017void uiTextEdit::updateCaret()
3020 int x = charColumnToWindowX(m_cursorCol);
3021 app()->setCaret(Rect(x, m_contentRect.
Y1, x, m_contentRect.
Y1 + m_textEditStyle.
textFont->height));
3030void uiTextEdit::moveCursor(
int col,
int selCol)
3032 col = iclamp(col, 0, m_textLength);
3033 selCol = iclamp(selCol, 0, m_textLength);
3035 if (col == m_cursorCol && selCol == m_selCursorCol)
3038 bool doRepaint =
false;
3041 if (m_cursorCol != m_selCursorCol && col == selCol)
3045 m_selCursorCol = selCol;
3047 if (m_cursorCol != m_selCursorCol)
3051 int x = charColumnToWindowX(m_cursorCol);
3053 int prevCharWidth = 0;
3055 getCharInfo(m_text[col - 1], &prevCharWidth);
3058 getCharInfo(m_text[col < m_textLength ? col : col - 1], &charWidth);
3060 if (x - prevCharWidth < m_contentRect.
X1) {
3062 m_viewX += m_contentRect.
X1 - (x - prevCharWidth);
3064 }
else if (x + charWidth > m_contentRect.
X2) {
3066 m_viewX -= (x + charWidth - m_contentRect.
X2);
3078int uiTextEdit::getColFromMouseX(
int mouseX)
3081 for (
int x = m_contentRect.
X1 + m_viewX, fontWidth; m_text[col]; ++col, x += fontWidth) {
3082 getCharInfo(m_text[col], &fontWidth);
3083 if (mouseX < x || (mouseX >= x && mouseX < x + fontWidth))
3091void uiTextEdit::checkAllocatedSpace(
int requiredLength)
3094 if (m_textSpace < requiredLength) {
3095 if (m_textSpace == 0) {
3097 m_textSpace = requiredLength;
3100 while (m_textSpace < requiredLength)
3103 m_text = (
char*) realloc(m_text, m_textSpace);
3109void uiTextEdit::insert(
char c)
3112 checkAllocatedSpace(m_textLength);
3113 memmove(m_text + m_cursorCol + 1, m_text + m_cursorCol, m_textLength - m_cursorCol);
3114 m_text[m_cursorCol] = c;
3115 moveCursor(m_cursorCol + 1, m_cursorCol + 1);
3122void uiTextEdit::removeSel()
3124 if (m_textLength > 0) {
3125 if (m_cursorCol > m_selCursorCol)
3126 iswap(m_cursorCol, m_selCursorCol);
3127 int count = imax(1, m_selCursorCol - m_cursorCol);
3128 if (m_cursorCol < m_textLength) {
3129 memmove(m_text + m_cursorCol, m_text + m_cursorCol + count, m_textLength - m_cursorCol - count + 1);
3130 m_textLength -= count;
3131 moveCursor(m_cursorCol, m_cursorCol);
3140int uiTextEdit::getWordPosAtLeft()
3142 int col = m_cursorCol - 1;
3143 while (col > 0 && (!isspace(m_text[col - 1]) || isspace(m_text[col])))
3145 return imax(0, col);
3150int uiTextEdit::getWordPosAtRight()
3152 int col = m_cursorCol + 1;
3153 while (col < m_textLength && (!isspace(m_text[col - 1]) || isspace(m_text[col])))
3155 return imin(m_textLength, col);
3161void uiTextEdit::selectWordAt(
int mouseX)
3163 int col = getColFromMouseX(mouseX), left = col, right = col;
3164 bool lspc = isspace(m_text[col]);
3165 while (left > 0 && (
bool)isspace(m_text[left - 1]) == lspc)
3167 while (right < m_textLength && (
bool)isspace(m_text[right]) == lspc)
3169 moveCursor(left, right);
3185 :
uiControl(parent, pos, size, visible, 0),
3194 m_labelStyle.adaptToDisplayColors(
app()->displayColors());
3213 int len = strlen(value);
3214 m_text = (
char*) realloc(m_text, len + 1);
3215 strcpy(m_text, value);
3223 va_start(ap, format);
3224 int size = vsnprintf(
nullptr, 0, format, ap) + 1;
3227 va_start(ap, format);
3228 m_text = (
char*) realloc(m_text,
size + 1);
3229 vsnprintf(m_text,
size, format, ap);
3244void uiLabel::paintLabel()
3265 int y = r.
Y1 + (r.height() - m_labelStyle.
textFont->height) / 2;
3270void uiLabel::processEvent(uiEvent * event)
3272 uiControl::processEvent(event);
3274 switch (event->id) {
3310 m_staticLabelStyle.adaptToDisplayColors(
app()->displayColors());
3333void uiStaticLabel::paintLabel()
3342 int y = r.
Y1 + (r.height() - m_staticLabelStyle.
textFont->height) / 2;
3347void uiStaticLabel::processEvent(uiEvent * event)
3349 uiControl::processEvent(event);
3351 switch (event->id) {
3376 :
uiControl(parent, pos, size, visible, 0),
3385 m_imageStyle.adaptToDisplayColors(
app()->displayColors());
3405 if (m_autoSize &&
bitmap)
3410void uiImage::paintImage()
3416 int x = r.
X1 + (r.
X2 + 1 - m_bitmap->
width) / 2;
3417 int y = r.
Y1 + (r.
Y2 + 1 - m_bitmap->
height) / 2;
3423void uiImage::processEvent(uiEvent * event)
3425 uiControl::processEvent(event);
3427 switch (event->id) {
3450 :
uiControl(parent, pos, size, visible, 0)
3459 m_panelStyle.adaptToDisplayColors(
app()->displayColors());
3471void uiPanel::paintPanel()
3480void uiPanel::processEvent(uiEvent * event)
3482 uiControl::processEvent(event);
3484 switch (event->id) {
3516 m_paintBoxStyle.adaptToDisplayColors(
app()->displayColors());
3523uiPaintBox::~uiPaintBox()
3528void uiPaintBox::paintPaintBox()
3540void uiPaintBox::processEvent(uiEvent * event)
3542 uiScrollableControl::processEvent(event);
3544 switch (event->id) {
3567 :
uiControl(parent, pos, size, visible, 0),
3581uiColorBox::~uiColorBox()
3593void uiColorBox::paintColorBox()
3602void uiColorBox::processEvent(uiEvent * event)
3604 uiControl::processEvent(event);
3606 switch (event->id) {
3629 :
uiControl(parent, pos, size, visible, 0),
3630 m_HScrollBarPosition(0),
3631 m_HScrollBarVisible(0),
3632 m_HScrollBarRange(0),
3633 m_VScrollBarPosition(0),
3634 m_VScrollBarVisible(0),
3635 m_VScrollBarRange(0),
3636 m_mouseOverItem(uiScrollBarItem::None),
3637 m_scrollTimer(nullptr),
3638 m_mouseDownPos(
Point(-1, -1))
3643 m_scrollableControlStyle.adaptToDisplayColors(
app()->displayColors());
3650uiScrollableControl::~uiScrollableControl()
3662 position = iclamp(position, 0, range - visible);
3663 switch (orientation) {
3666 bool changedPos = (m_VScrollBarPosition != position);
3667 if (m_VScrollBarVisible != visible || m_VScrollBarRange != range || changedPos) {
3668 m_VScrollBarVisible = visible;
3669 m_VScrollBarRange = range;
3670 m_VScrollBarPosition = position;
3671 if (repaintScrollbar)
3672 repaintScrollBar(orientation);
3680 bool changedPos = (m_HScrollBarPosition != position);
3681 if (m_HScrollBarVisible != visible || m_HScrollBarRange != range || changedPos) {
3682 m_HScrollBarVisible = visible;
3683 m_HScrollBarRange = range;
3684 m_HScrollBarPosition = position;
3685 if (repaintScrollbar)
3686 repaintScrollBar(orientation);
3696void uiScrollableControl::repaintScrollBar(
uiOrientation orientation)
3702void uiScrollableControl::processEvent(uiEvent * event)
3704 uiControl::processEvent(event);
3706 switch (event->id) {
3710 paintScrollableControl();
3713 case UIEVT_MOUSEBUTTONDOWN:
3714 if (event->params.mouse.changedButton == 1) {
3715 m_mouseDownPos = Point(event->params.mouse.status.X, event->params.mouse.status.Y);
3716 m_mouseDownHScrollBarPosition = m_HScrollBarPosition;
3717 m_mouseDownVScrollBarPosition = m_VScrollBarPosition;
3718 if (m_mouseOverItem == uiScrollBarItem::LeftButton || m_mouseOverItem == uiScrollBarItem::RightButton ||
3719 m_mouseOverItem == uiScrollBarItem::TopButton || m_mouseOverItem == uiScrollBarItem::BottomButton) {
3722 handleButtonsScroll();
3726 app()->combineMouseMoveEvents(
true);
3730 case UIEVT_MOUSEBUTTONUP:
3731 if (event->params.mouse.changedButton == 1) {
3732 app()->combineMouseMoveEvents(
false);
3733 if (m_scrollTimer) {
3735 m_scrollTimer =
nullptr;
3740 case UIEVT_MOUSEMOVE:
3741 if (
app()->capturedMouseWindow() ==
this)
3742 handleCapturedMouseMove(event->params.mouse.status.X, event->params.mouse.status.Y);
3744 handleFreeMouseMove(event->params.mouse.status.X, event->params.mouse.status.Y);
3747 case UIEVT_MOUSEWHEEL:
3748 if (m_VScrollBarRange)
3753 if (event->params.timerHandle == m_scrollTimer)
3754 handleButtonsScroll();
3763void uiScrollableControl::handleButtonsScroll()
3765 switch (m_mouseOverItem) {
3766 case uiScrollBarItem::LeftButton:
3769 case uiScrollBarItem::RightButton:
3772 case uiScrollBarItem::TopButton:
3775 case uiScrollBarItem::BottomButton:
3784void uiScrollableControl::handlePageScroll()
3786 switch (m_mouseOverItem) {
3787 case uiScrollBarItem::PageLeft:
3790 case uiScrollBarItem::PageRight:
3793 case uiScrollBarItem::PageUp:
3796 case uiScrollBarItem::PageDown:
3805void uiScrollableControl::handleFreeMouseMove(
int mouseX,
int mouseY)
3807 auto prev = m_mouseOverItem;
3808 m_mouseOverItem = getItemAt(mouseX, mouseY);
3809 if (m_mouseOverItem !=
prev) {
3810 if (m_VScrollBarRange)
3812 if (m_HScrollBarRange)
3818void uiScrollableControl::handleCapturedMouseMove(
int mouseX,
int mouseY)
3820 if (m_mouseOverItem == uiScrollBarItem::HBar) {
3822 int offset = mouseX - m_mouseDownPos.
X;
3823 int newPos = m_mouseDownHScrollBarPosition + offset * m_HScrollBarRange / m_HBarArea;
3825 }
else if (m_mouseOverItem == uiScrollBarItem::VBar) {
3827 int offset = mouseY - m_mouseDownPos.
Y;
3828 int newPos = m_mouseDownVScrollBarPosition + offset * m_VScrollBarRange / m_VBarArea;
3834uiScrollBarItem uiScrollableControl::getItemAt(
int x,
int y)
3836 if (m_HScrollBarRange) {
3837 Rect lbtn, rbtn, bar;
3838 Rect box = getHScrollBarRects(&lbtn, &rbtn, &bar);
3839 if (lbtn.contains(x, y))
3840 return uiScrollBarItem::LeftButton;
3841 if (rbtn.contains(x, y))
3842 return uiScrollBarItem::RightButton;
3843 if (bar.contains(x, y))
3844 return uiScrollBarItem::HBar;
3845 if (box.contains(x, y))
3846 return x < bar.X1 ? uiScrollBarItem::PageLeft : uiScrollBarItem::PageRight;
3848 if (m_VScrollBarRange) {
3849 Rect tbtn, bbtn, bar;
3850 Rect box = getVScrollBarRects(&tbtn, &bbtn, &bar);
3851 if (tbtn.contains(x, y))
3852 return uiScrollBarItem::TopButton;
3853 if (bbtn.contains(x, y))
3854 return uiScrollBarItem::BottomButton;
3855 if (bar.contains(x, y))
3856 return uiScrollBarItem::VBar;
3857 if (box.contains(x, y))
3858 return y < bar.Y1 ? uiScrollBarItem::PageUp: uiScrollBarItem::PageDown;
3860 return uiScrollBarItem::None;
3864Rect uiScrollableControl::getVScrollBarRects(Rect * topButton, Rect * bottomButton, Rect * bar)
3868 Rect box = Rect(cArea.X2 + 1 - sbSize, cArea.Y1, cArea.X2, cArea.Y2 - (m_HScrollBarRange ? sbSize : 0));
3869 if (topButton && bottomButton && bar) {
3871 *topButton = Rect(box.X1 + 2, box.Y1 + 2, box.X2 - 2, box.Y1 + sbSize - 2);
3872 *bottomButton = Rect(box.X1 + 2, box.Y2 - sbSize + 2, box.X2 - 2, box.Y2 - 2);
3874 int barAreaY1 = topButton->Y2 + 2;
3875 int barAreaY2 = bottomButton->Y1 - 2;
3876 m_VBarArea = barAreaY2 - barAreaY1 + 1;
3877 int barOffsetY = m_VScrollBarPosition * m_VBarArea / m_VScrollBarRange;
3878 int barHeight = m_VScrollBarVisible * m_VBarArea / m_VScrollBarRange;
3879 *bar = Rect(box.X1 + 1, barAreaY1 + barOffsetY, box.X2 - 1, barAreaY1 + barOffsetY + barHeight);
3885Rect uiScrollableControl::getHScrollBarRects(Rect * leftButton, Rect * rightButton, Rect * bar)
3889 Rect box = Rect(cArea.X1, cArea.Y2 + 1 - sbSize, cArea.X2 - (m_VScrollBarRange ? sbSize : 0), cArea.Y2);
3890 if (leftButton && rightButton && bar) {
3892 *leftButton = Rect(box.X1 + 2, box.Y1 + 2, box.X1 + sbSize - 2, box.Y2 - 2);
3893 *rightButton = Rect(box.X2 - sbSize + 2, box.Y1 + 2, box.X2 - 2, box.Y2 - 2);
3895 int barAreaX1 = leftButton->X2 + 2;
3896 int barAreaX2 = rightButton->X1 - 2;
3897 m_HBarArea = barAreaX2 - barAreaX1 + 1;
3898 int barOffsetX = m_HScrollBarPosition * m_HBarArea / m_HScrollBarRange;
3899 int barWidth = m_HScrollBarVisible * m_HBarArea / m_HScrollBarRange;
3900 *bar = Rect(barAreaX1 + barOffsetX, box.Y1 + 1, barAreaX1 + barOffsetX + barWidth, box.Y2 - 1);
3906void uiScrollableControl::paintScrollableControl()
3910 if (m_HScrollBarRange) {
3912 Rect lbtn, rbtn, bar;
3913 Rect box = getHScrollBarRects(&lbtn, &rbtn, &bar);
3917 canvas()->
fillRectangle(Rect(box.X1, box.Y1, bar.X1 - 1, box.Y2));
3918 canvas()->
fillRectangle(Rect(bar.X2 + 1, box.Y1, box.X2, box.Y2));
3919 canvas()->
drawLine(bar.X1, box.Y1, bar.X2, box.Y1);
3920 canvas()->
drawLine(bar.X1, box.Y2, bar.X2, box.Y2);
3922 canvas()->
setPenColor(m_mouseOverItem == uiScrollBarItem::LeftButton ? mouseOverFColor : FColor);
3923 canvas()->
drawLine(lbtn.X2, lbtn.Y1, lbtn.X1, lbtn.Y1 + lbtn.height() / 2);
3924 canvas()->
drawLine(lbtn.X1, lbtn.Y1 + lbtn.height() / 2, lbtn.X2, lbtn.Y2);
3925 canvas()->
setPenColor(m_mouseOverItem == uiScrollBarItem::RightButton ? mouseOverFColor : FColor);
3926 canvas()->
drawLine(rbtn.X1, rbtn.Y1, rbtn.X2, rbtn.Y1 + lbtn.height() / 2);
3927 canvas()->
drawLine(rbtn.X2, rbtn.Y1 + lbtn.height() / 2, rbtn.X1, rbtn.Y2);
3929 canvas()->
setBrushColor(m_mouseOverItem == uiScrollBarItem::HBar ? mouseOverFColor : FColor);
3932 if (m_VScrollBarRange) {
3934 Rect ubtn, bbtn, bar;
3935 Rect box = getVScrollBarRects(&ubtn, &bbtn, &bar);
3939 canvas()->
fillRectangle(Rect(box.X1, box.Y1, box.X2, bar.Y1 - 1));
3940 canvas()->
fillRectangle(Rect(box.X1, bar.Y2 + 1, box.X2, box.Y2));
3941 canvas()->
drawLine(box.X1, bar.Y1, box.X1, bar.Y2);
3942 canvas()->
drawLine(box.X2, bar.Y1, box.X2, bar.Y2);
3944 if (m_HScrollBarRange)
3947 canvas()->
setPenColor(m_mouseOverItem == uiScrollBarItem::TopButton ? mouseOverFColor : FColor);
3948 canvas()->
drawLine(ubtn.X1, ubtn.Y2, ubtn.X1 + ubtn.width() / 2, ubtn.Y1);
3949 canvas()->
drawLine(ubtn.X1 + ubtn.width() / 2, ubtn.Y1, ubtn.X2, ubtn.Y2);
3950 canvas()->
setPenColor(m_mouseOverItem == uiScrollBarItem::BottomButton ? mouseOverFColor : FColor);
3951 canvas()->
drawLine(bbtn.X1, bbtn.Y1, bbtn.X1 + bbtn.width() / 2, bbtn.Y2);
3952 canvas()->
drawLine(bbtn.X1 + bbtn.width() / 2, bbtn.Y2, bbtn.X2, bbtn.Y1);
3954 canvas()->
setBrushColor(m_mouseOverItem == uiScrollBarItem::VBar ? mouseOverFColor : FColor);
3963 r.
X2 -= (m_VScrollBarRange ? m_scrollableControlStyle.
scrollBarSize : 0);
3964 r.
Y2 -= (m_HScrollBarRange ? m_scrollableControlStyle.
scrollBarSize : 0);
3981 m_firstVisibleItem(0)
3991 m_listBoxStyle.adaptToDisplayColors(
app()->displayColors());
3998uiCustomListBox::~uiCustomListBox()
4003void uiCustomListBox::processEvent(uiEvent * event)
4005 uiScrollableControl::processEvent(event);
4007 switch (event->id) {
4014 case UIEVT_MOUSEBUTTONDOWN:
4015 if (event->params.mouse.changedButton == 1)
4016 mouseDownSelect(event->params.mouse.status.X, event->params.mouse.status.Y);
4019 case UIEVT_MOUSEMOVE:
4021 mouseMoveSelect(event->params.mouse.status.X, event->params.mouse.status.Y);
4025 handleKeyDown(event->params.key);
4036 case UIEVT_KILLFOCUS:
4048 case UIEVT_DBLCLICK:
4060 bool shift = key.
SHIFT;
4089 selectItem(items_getCount() - 1, shift, shift);
4100 if (items_getCount() > 0) {
4101 index = iclamp(index, 0, items_getCount() - 1);
4104 items_deselectAll();
4106 if (index <= first) {
4107 for (
int i = index; i <= first; ++i)
4108 items_select(i,
true);
4110 for (
int i = index; i >= first; --i)
4111 items_select(i,
true);
4114 items_select(index,
true);
4118 makeItemVisible(index);
4127void uiCustomListBox::makeItemVisible(
int index)
4130 if (index < m_firstVisibleItem)
4131 m_firstVisibleItem = index;
4140 items_deselectAll();
4146void uiCustomListBox::paintListBox()
4152 if (itmRect.height() * items_getCount() > cliRect.height()) {
4153 int visible = cliRect.height() / itmRect.height();
4154 int range = items_getCount();
4163 m_firstVisibleItem = 0;
4169 int index = m_firstVisibleItem;
4171 if (!itmRect.intersects(cliRect))
4176 if (index < items_getCount() && items_selected(index))
4181 if (index < items_getCount()) {
4184 items_draw(index, itmRect);
4188 itmRect = itmRect.translate(0, m_listBoxStyle.
itemHeight);
4198 for (
int i = 0; i < items_getCount(); ++i)
4199 if (items_selected(i))
4208 for (
int i = items_getCount() - 1; i >= 0; --i)
4209 if (items_selected(i))
4228int uiCustomListBox::getItemAtMousePos(
int mouseX,
int mouseY)
4231 if (cliRect.contains(mouseX, mouseY)) {
4232 int idx = m_firstVisibleItem + (mouseY - cliRect.
Y1) / m_listBoxStyle.
itemHeight;
4233 return idx < items_getCount() ? idx : -1;
4239void uiCustomListBox::mouseDownSelect(
int mouseX,
int mouseY)
4241 int idx = getItemAtMousePos(mouseX, mouseY);
4245 bool wasSelected = items_selected(idx);
4247 items_select(idx, !wasSelected);
4249 items_deselectAll();
4251 items_select(idx,
true);
4255 items_deselectAll();
4256 items_select(idx,
true);
4258 }
else if (idx == -1)
4259 items_deselectAll();
4267void uiCustomListBox::mouseMoveSelect(
int mouseX,
int mouseY)
4269 int idx = getItemAtMousePos(mouseX, mouseY);
4270 if (idx >= 0 && !items_selected(idx)) {
4271 items_deselectAll();
4272 items_select(idx,
true);
4298void uiListBox::items_draw(
int index,
const Rect & itemRect)
4300 int x = itemRect.
X1 + 1;
4317 m_selectedColor((
Color)0)
4328void uiColorListBox::items_draw(
int index,
const Rect & itemRect)
4330 constexpr int BORDER = 1;
4332 canvas()->
fillRectangle(itemRect.
X1 + BORDER, itemRect.
Y1 + BORDER, itemRect.
X2 - BORDER, itemRect.
Y2 - BORDER);
4356void uiFileBrowser::items_draw(
int index,
const Rect & itemRect)
4358 int x = itemRect.
X1 + 1;
4362 static const char * DIRTXT =
"[dir]";
4369void uiFileBrowser::items_select(
int index,
bool select)
4373 else if (index == m_selected || index == -1)
4381 m_selected = m_dir.
count() > 0 ? 0 : -1;
4389 m_selected = m_dir.
count() > 0 ? 0 : -1;
4396 return m_selected >= 0 ? m_dir.
get(m_selected)->
name :
nullptr;
4402 return m_selected >= 0 ? m_dir.
get(m_selected)->
isDir :
false;
4406void uiFileBrowser::enterSubDir()
4408 if (m_selected >= 0) {
4409 auto selItem = m_dir.
get(m_selected);
4410 if (selItem->isDir) {
4423 m_selected = imin(m_dir.
count() - 1, m_selected);
4429void uiFileBrowser::processEvent(uiEvent * event)
4431 uiCustomListBox::processEvent(event);
4433 switch (event->id) {
4448 case UIEVT_DBLCLICK:
4468 :
uiControl(parent, pos, size, visible, 0),
4469 m_listHeight(listHeight),
4471 m_listBoxParent(nullptr)
4480 m_comboBoxStyle.adaptToDisplayColors(
app()->displayColors());
4492uiCustomComboBox::~uiCustomComboBox()
4504 updateEditControl();
4508void uiCustomComboBox::processEvent(uiEvent * event)
4510 uiControl::processEvent(event);
4512 switch (event->id) {
4519 updateEditControl();
4526 m_loseFocusBy = key.
SHIFT ? -1 : 2;
4528 uiEvent evt = uiEvent(
parentFrame(), UIEVT_KEYDOWN);
4529 evt.params.key = key;
4531 evt.id = UIEVT_KEYUP;
4543 case UIEVT_MOUSEBUTTONDOWN:
4544 if (event->params.mouse.changedButton == 1 && getButtonRect().contains(event->params.mouse.status.X, event->params.mouse.status.Y))
4548 case UIEVT_CHILDSETFOCUS:
4549 if (m_comboBoxProps.
openOnFocus && event->params.focusInfo.newFocused == editcontrol()
4550 && event->params.focusInfo.oldFocused != listbox()
4551 && event->params.focusInfo.oldFocused !=
this) {
4556 case UIEVT_SETFOCUS:
4557 if (m_loseFocusBy) {
4561 if (event->params.focusInfo.oldFocused != listbox() && event->params.focusInfo.oldFocused != editcontrol()) {
4564 }
else if (!isListBoxOpen()) {
4567 }
else if (event->params.focusInfo.oldFocused == listbox()) {
4574 listbox()->processEvent(event);
4579 if (((event->params.key.RALT || event->params.key.LALT) && (event->params.key.VK ==
VK_DOWN || event->params.key.VK ==
VK_UP)) || (event->params.key.VK ==
VK_RETURN))
4593void uiCustomComboBox::openListBox()
4596 if (r.Y2 + m_listHeight + 1 >=
app()->rootWindow()->
size().
height) {
4599 r.Y1 = r.Y2 - m_listHeight;
4603 r.Y2 = r.Y1 + m_listHeight;
4616void uiCustomComboBox::closeListBox()
4619 if (
app()->focusedWindow() ==
nullptr ||
app()->focusedWindow() == listbox()) {
4627void uiCustomComboBox::switchListBox()
4629 if (isListBoxOpen()) {
4638Size uiCustomComboBox::getEditControlSize()
4641 return Size(clientArea.width() - buttonWidth(), clientArea.height());
4645int uiCustomComboBox::buttonWidth()
4648 return clientArea.height() / 2;
4652Rect uiCustomComboBox::getButtonRect()
4655 btnRect.X1 = btnRect.X2 - buttonWidth() + 1;
4660void uiCustomComboBox::paintButton()
4662 Rect btnRect = getButtonRect();
4670 Rect arrowRect = btnRect.hShrink(1).vShrink(2);
4671 int hHeight = arrowRect.height() / 2;
4672 int hWidth = arrowRect.width() / 2;
4673 constexpr int vDist = 2;
4674 canvas()->
drawLine(arrowRect.X1, arrowRect.Y1 + hHeight - vDist, arrowRect.X1 + hWidth, arrowRect.Y1);
4675 canvas()->
drawLine(arrowRect.X1 + hWidth, arrowRect.Y1, arrowRect.X2, arrowRect.Y1 + hHeight - vDist);
4676 canvas()->
drawLine(arrowRect.X1, arrowRect.Y1 + hHeight + vDist, arrowRect.X1 + hWidth, arrowRect.Y2);
4677 canvas()->
drawLine(arrowRect.X1 + hWidth, arrowRect.Y2, arrowRect.X2, arrowRect.Y1 + hHeight + vDist);
4692 m_textEdit(nullptr),
4708uiComboBox::~uiComboBox()
4714void uiComboBox::updateEditControl()
4733 m_colorBox(nullptr),
4734 m_colorListBox(nullptr)
4746uiColorComboBox::~uiColorComboBox()
4752void uiColorComboBox::updateEditControl()
4768 :
uiControl(parent, pos, size, visible, 0),
4782 m_checkBoxStyle.adaptToDisplayColors(
app()->displayColors());
4789uiCheckBox::~uiCheckBox()
4794void uiCheckBox::paintCheckBox()
4809 canvas()->
drawLine(r.X1, r.Y2 - r.height() / 3, r.X1 + r.width() / 3, r.Y2);
4810 canvas()->
drawLine(r.X1 + r.width() / 3, r.Y2, r.X2, r.Y1);
4814 canvas()->
fillEllipse(r.X1 + r.width() / 2 - 1, r.Y1 + r.height() / 2 - 1, r.width(), r.height());
4821void uiCheckBox::processEvent(uiEvent * event)
4823 uiControl::processEvent(event);
4825 switch (event->id) {
4837 case UIEVT_MOUSEENTER:
4841 case UIEVT_MOUSEBUTTONDOWN:
4842 if (event->params.mouse.changedButton == 1)
4846 case UIEVT_MOUSELEAVE:
4864void uiCheckBox::trigger()
4868 m_checked = !m_checked;
4882 if (value != m_checked) {
4892void uiCheckBox::unCheckGroup()
4894 if (m_groupIndex == -1)
4899 if (chk->m_groupIndex == m_groupIndex)
4917 :
uiControl(parent, pos, size, visible, 0),
4918 m_orientation(orientation),
4922 m_ticksFrequency(25)
4932 m_sliderStyle.adaptToDisplayColors(
app()->displayColors());
4939uiSlider::~uiSlider()
4946 if (value != m_position) {
4947 m_position = iclamp(value, m_min, m_max);
4958 m_ticksFrequency = ticksFrequency;
4959 m_position = iclamp(m_position, m_min, m_max);
4963void uiSlider::paintSlider()
4966 Rect slideRect = cRect.shrink(4);
4967 Rect gripRect = getGripRect();
4973 switch (m_orientation) {
4986 if (m_ticksFrequency > 0) {
4988 int range = m_max - m_min + 0;
4989 for (
int p = m_min; p <= m_max; p += m_ticksFrequency) {
4990 switch (m_orientation) {
4993 int x = slideRect.
X1 + slideRect.width() * (p - m_min) / range;
4999 int y = slideRect.
Y2 - slideRect.height() * (p - m_min) / range;
5014Rect uiSlider::getGripRect()
5017 Rect slideRect = cRect.shrink(4);
5018 int range = m_max - m_min + 0;
5019 switch (m_orientation) {
5022 int x = slideRect.X1 + slideRect.width() * (m_position - m_min) / range;
5023 return Rect(x - 4, cRect.Y1, x + 4, cRect.Y2);
5027 int y = slideRect.Y2 - slideRect.height() * (m_position - m_min) / range;
5028 return Rect(cRect.X1, y - 4, cRect.X2, y + 4);
5036void uiSlider::moveGripTo(
int x,
int y)
5039 Rect slideRect = cRect.shrink(4);
5040 int range = m_max - m_min + 1;
5041 switch (m_orientation) {
5043 setPosition(m_min + (x - slideRect.X1) * range / slideRect.width());
5046 setPosition(m_min + (slideRect.Y2 - y) * range / slideRect.height());
5052void uiSlider::processEvent(uiEvent * event)
5054 uiControl::processEvent(event);
5056 switch (event->id) {
5063 case UIEVT_MOUSEBUTTONDOWN:
5064 moveGripTo(event->params.mouse.status.X, event->params.mouse.status.Y);
5067 case UIEVT_MOUSEMOVE:
5068 if (
app()->capturedMouseWindow() ==
this)
5069 moveGripTo(event->params.mouse.status.X, event->params.mouse.status.Y);
5073 handleKeyDown(event->params.key);
5076 case UIEVT_MOUSEENTER:
5080 case UIEVT_MOUSELEAVE:
5145 :
uiControl(parent, pos, size, visible, 0)
5154 m_progressBarStyle.adaptToDisplayColors(
app()->displayColors());
5163uiProgressBar::~uiProgressBar()
5168void uiProgressBar::paintProgressBar()
5172 int splitPos = cRect.width() * m_percentage / 100;
5173 Rect fRect = Rect(cRect.X1, cRect.Y1, cRect.X1 + splitPos, cRect.Y2);
5174 Rect bRect = Rect(cRect.X1 + splitPos + 1, cRect.Y1, cRect.X2, cRect.Y2);
5184 sprintf(txt,
"%d%%", m_percentage);
5188 int y = cRect.Y1 + (cRect.height() - m_progressBarStyle.
textFont->height) / 2;
5194void uiProgressBar::processEvent(uiEvent * event)
5196 uiControl::processEvent(event);
5198 switch (event->id) {
5213 value = imin(imax(0, value), 100);
5214 if (value != m_percentage) {
5215 m_percentage = value;
5243void uiSimpleMenu::processEvent(uiEvent * event)
5245 uiCustomListBox::processEvent(event);
5247 switch (event->id) {
5249 case UIEVT_MOUSEBUTTONUP:
5250 if (event->params.mouse.changedButton == 1) {
5251 int idx = getItemAtMousePos(event->params.mouse.status.X, event->params.mouse.status.Y);
5262 }
else if (event->params.key.VK ==
VK_ESCAPE) {
5274void uiSimpleMenu::items_draw(
int index,
const Rect & itemRect)
5276 int x = itemRect.X1 + 1;
5303 if (!isListBoxOpen())
5308 m_menu->
items().appendSepList(itemsText, separator);
5312 m_selectedItem = idx;
5320uiSplitButton::~uiSplitButton()
5325void uiSplitButton::processEvent(uiEvent * event)
5327 uiCustomComboBox::processEvent(event);
5329 switch (event->id) {
5331 case UIEVT_SETFOCUS:
5332 if (m_selectedItem > -1) {
5334 m_selectedItem = -1;
5344void uiSplitButton::openListBox()
5347 uiCustomComboBox::openListBox();
5351void uiSplitButton::updateEditControl()
5356void uiSplitButton::paintButton()
5358 Rect btnRect = getButtonRect();
5365 Rect arrowRect = btnRect.hShrink(btnRect.width() / 4).vShrink(btnRect.height() / 4);
5366 if ((arrowRect.X1 + arrowRect.X2) & 1)
5368 bool up = isListBoxOpen();
5369 Point points[3] = { { arrowRect.X1, up ? arrowRect.Y2 : arrowRect.Y1 },
5370 { arrowRect.X2, up ? arrowRect.Y2 : arrowRect.Y1 },
5371 { (arrowRect.X1 + arrowRect.X2) / 2, up ? arrowRect.Y1 : arrowRect.Y2 } };
This file contains fabgl::Canvas definition.
virtual int colorsCount()=0
Determines number of colors this display can provide.
void enableBackgroundPrimitiveTimeout(bool value)
Enables or disables execution time limitation inside vertical retracing interrupt.
void setMouseCursor(Cursor *cursor)
Sets mouse cursor and make it visible.
Represents the base abstract class for bitmapped display controllers.
void fillRectangle(int X1, int Y1, int X2, int Y2)
Fills a rectangle using the current brush color.
int getHeight()
Determines the canvas height in pixels.
void drawTextWithEllipsis(FontInfo const *fontInfo, int X, int Y, char const *text, int maxX)
Draws a string at specified position. Add ellipses before truncation.
void drawBitmap(int X, int Y, Bitmap const *bitmap)
Draws a bitmap at specified position.
void setPaintOptions(PaintOptions options)
Sets paint options.
void setOrigin(int X, int Y)
Sets the axes origin.
void setClippingRect(Rect const &rect)
Sets clipping rectangle relative to the origin.
void drawRectangle(int X1, int Y1, int X2, int Y2)
Draws a rectangle using the current pen color.
void drawGlyph(int X, int Y, int width, int height, uint8_t const *data, int index=0)
Draws a glyph at specified position.
int getWidth()
Determines the canvas width in pixels.
void drawPath(Point const *points, int pointsCount)
Draws a sequence of lines.
void fillEllipse(int X, int Y, int width, int height)
Fills an ellipse specifying center and size, using current brush color.
void setBrushColor(uint8_t red, uint8_t green, uint8_t blue)
Sets brush (background) color specifying color components.
int textExtent(FontInfo const *fontInfo, char const *text)
Calculates text extension in pixels.
void invertRectangle(int X1, int Y1, int X2, int Y2)
Inverts a rectangle.
void drawLine(int X1, int Y1, int X2, int Y2)
Draws a line specifying initial and ending coordinates.
void setPenColor(uint8_t red, uint8_t green, uint8_t blue)
Sets pen (foreground) color specifying color components.
void clear()
Fills the entire canvas with the brush color.
void resetPaintOptions()
Resets paint options.
void resetGlyphOptions()
Resets glyph options.
void drawText(int X, int Y, char const *text, bool wrap=false)
Draws a string at specified position.
void lineTo(int X, int Y)
Draws a line starting from current pen position.
void moveTo(int X, int Y)
Moves current pen position to the spcified coordinates.
void setGlyphOptions(GlyphOptions options)
Sets drawing options for the next glyphs.
void fillPath(Point const *points, int pointsCount)
Fills the polygon enclosed in a sequence of lines.
A class with a set of drawing methods.
void changeDirectory(const char *subdir)
Sets relative directory path.
bool reload()
Reloads directory content.
bool setDirectory(const char *path)
Sets absolute directory path.
int count()
Determines number of files in current directory.
DirItem const * get(int index)
Gets file/directory at index.
void setUIApp(uiApp *app)
Sets current UI app.
The PS2 Keyboard controller class.
void setupAbsolutePositioner(int width, int height, bool createAbsolutePositionsQueue, BitmappedDisplayController *updateDisplayController=nullptr, uiApp *app=nullptr)
Initializes absolute position handler.
void terminateAbsolutePositioner()
Terminates absolute position handler.
bool isMouseAvailable()
Checks if mouse has been detected and correctly initialized.
The PS2 Mouse controller class.
static Mouse * mouse()
Returns the instance of Mouse object automatically created by PS2Controller.
static Keyboard * keyboard()
Returns the instance of Keyboard object automatically created by PS2Controller.
void killTimer(uiTimerHandle handle)
Kills a timer.
virtual void init()
Method to inherit to implement an application.
int run(BitmappedDisplayController *displayController, Keyboard *keyboard=nullptr, Mouse *mouse=nullptr)
Initializes application and executes the main event loop.
void resizeWindow(uiWindow *window, int width, int height)
Resizes a window.
bool postEvent(uiEvent const *event)
Places an event in the event queue and returns without waiting for the receiver to process the event.
void destroyWindow(uiWindow *window)
Destroys a window.
uiWindow * screenToWindow(Point &point)
Determines which window a point belongs to.
int showModalWindow(uiWindow *window)
Makes a window visible and handles it has a modal window.
uiWindow * moveFocus(int delta)
Move focus to a control with current focus index plus a delta.
void showWindow(uiWindow *window, bool value)
Makes a window visible or invisible.
uiMessageBoxResult messageBox(char const *title, char const *text, char const *button1Text, char const *button2Text=nullptr, char const *button3Text=nullptr, uiMessageBoxIcon icon=uiMessageBoxIcon::Question)
Displays a modal dialog box with an icon, text and some buttons.
void repaintRect(Rect const &rect)
Repaints a screen area.
uiMessageBoxResult inputBox(char const *title, char const *text, char *inOutString, int maxLength, char const *button1Text, char const *button2Text=nullptr)
Displays a modal dialog box with a text, a text edit and up to two buttons.
int endModalWindow(ModalWindowState *state)
Ends modal window processing.
uiWindow * setFocusedWindow(uiWindow *value)
Sets the focused window (control)
void enableKeyboardAndMouseEvents(bool value)
Enables or disables mouse and keyboard events.
void quit(int exitCode)
Terminates application and free resources.
ModalWindowState * initModalWindow(uiWindow *window)
Begins modal window processing.
uiWindow * activeWindow()
Gets a pointer to the currently active window.
uiTimerHandle setTimer(uiEvtHandler *dest, int periodMS)
Setups a timer.
uiMessageBoxResult fileDialog(char const *title, char *inOutDirectory, int maxDirNameSize, char *inOutFilename, int maxFileNameSize, char const *buttonOKText, char const *buttonCancelText, int frameWidth=200, int frameHeight=250)
Displays a modal open/save dialog box.
uiStyle * style()
Gets current application controls style.
Delegate< uiTimerHandle > onTimer
Timer event delegate.
void joinAsyncRun()
Waits for runAsync termination.
uiFrame * rootWindow()
Gets a pointer to the root window.
void repaintWindow(uiWindow *window)
Repaints a window.
uiWindow * setActiveWindow(uiWindow *value)
Sets the active window.
void processEvents()
Processes all events in queue.
void minimizeFrame(uiFrame *frame, bool value)
Minimizes or restores a frame.
uiWindow * focusedWindow()
Gets the focused window (control)
void reshapeWindow(uiWindow *window, Rect const &rect)
Reshapes a window.
void moveWindow(uiWindow *window, int x, int y)
Moves a window.
uiApp & runAsync(BitmappedDisplayController *displayController, int taskStack=3000, Keyboard *keyboard=nullptr, Mouse *mouse=nullptr)
Initializes application and executes asynchronously the main event loop.
bool insertEvent(uiEvent const *event)
Inserts (first position) an event in the event queue and returns without waiting for the receiver to ...
void maximizeFrame(uiFrame *frame, bool value)
Maximizes or restores a frame.
bool processModalWindowEvents(ModalWindowState *state, int timeout)
Processes all messages from modal window.
Represents the whole application base class.