27 #include "freertos/FreeRTOS.h" 28 #include "freertos/timers.h" 39 #include "images/bitmaps.h" 43 #pragma GCC optimize ("O2") 127 uiObject::~uiObject()
141 uiEvtHandler::uiEvtHandler(uiApp * app)
144 objectType().uiEvtHandler =
true;
148 uiEvtHandler::~uiEvtHandler()
151 m_app->killEvtHandlerTimers(
this);
155 void uiEvtHandler::processEvent(uiEvent * event)
176 : uiEvtHandler(nullptr),
177 m_rootWindow(nullptr),
178 m_activeWindow(nullptr),
179 m_focusedWindow(nullptr),
180 m_capturedMouseWindow(nullptr),
181 m_freeMouseWindow(nullptr),
182 m_modalWindow(nullptr),
183 m_combineMouseMoveEvents(false),
184 m_caretWindow(nullptr),
185 m_caretTimer(nullptr),
186 m_caretInvertState(-1),
187 m_lastMouseUpTimeMS(0),
190 objectType().uiApp =
true;
202 m_displayController = displayController;
204 m_canvas =
new Canvas(m_displayController);
206 m_keyboard = keyboard;
208 if (PS2Controller::initialized()) {
210 if (m_keyboard ==
nullptr)
211 m_keyboard = PS2Controller::keyboard();
212 if (m_mouse ==
nullptr)
213 m_mouse = PS2Controller::mouse();
216 m_eventsQueue = xQueueCreate(FABGLIB_UI_EVENTS_QUEUE_SIZE,
sizeof(uiEvent));
219 if (m_mouse && m_mouse->isMouseAvailable())
220 m_mouse->setupAbsolutePositioner(m_canvas->getWidth(), m_canvas->getHeight(),
false, m_displayController,
this);
224 m_keyboard->setUIApp(
this);
227 m_rootWindow =
new uiFrame(
nullptr,
"",
Point(0, 0),
Size(m_canvas->getWidth(), m_canvas->getHeight()),
false);
228 m_rootWindow->setApp(
this);
229 m_rootWindow->setCanvas(m_canvas);
231 m_rootWindow->windowStyle().borderSize = 0;
232 m_rootWindow->frameStyle().backgroundColor =
RGB888(255, 255, 255);
234 m_rootWindow->frameProps().resizeable =
false;
235 m_rootWindow->frameProps().moveable =
false;
238 if (m_mouse && m_mouse->isMouseAvailable())
239 m_displayController->setMouseCursor(m_rootWindow->windowStyle().defaultCursor);
242 m_displayController->enableBackgroundPrimitiveTimeout(
false);
244 m_lastUserActionTimeMS = esp_timer_get_time() / 1000;
246 showWindow(m_rootWindow,
true);
248 m_activeWindow = m_rootWindow;
251 uiEvent evt = uiEvent(
this, UIEVT_APPINIT);
259 if (getEvent(&event, -1)) {
264 preprocessEvent(&event);
267 event.dest->processEvent(&event);
269 if (event.id == UIEVT_QUIT) {
270 exitCode =
event.params.exitCode;
276 killEvtHandlerTimers(
this);
280 m_displayController->setMouseCursor(
nullptr);
282 if (m_rootWindow->frameProps().fillBackground) {
283 m_canvas->setBrushColor(m_rootWindow->frameStyle().backgroundColor);
288 m_rootWindow =
nullptr;
291 m_keyboard->setUIApp(
nullptr);
293 if (m_mouse && m_mouse->isMouseAvailable())
294 m_mouse->terminateAbsolutePositioner();
296 vQueueDelete(m_eventsQueue);
297 m_eventsQueue =
nullptr;
305 void uiApp::asyncRunTask(
void * arg)
307 auto app = (
uiApp*)arg;
308 app->run(app->m_displayController, app->m_keyboard, app->m_mouse);
315 m_displayController = displayController;
316 m_keyboard = keyboard;
319 if (CoreUsage::busiestCore() == -1)
320 xTaskCreate(&asyncRunTask,
"", taskStack,
this, 5,
nullptr);
322 xTaskCreatePinnedToCore(&asyncRunTask,
"", taskStack,
this, 5,
nullptr, CoreUsage::quietCore());
326 void uiApp::processEvents()
329 while (getEvent(&event, 0)) {
331 preprocessEvent(&event);
334 event.dest->processEvent(&event);
339 void uiApp::quit(
int exitCode)
341 for (
auto child = m_rootWindow->lastChild(); child; child = child->prev())
342 destroyWindow(child);
343 uiEvent evt = uiEvent(
nullptr, UIEVT_QUIT);
344 evt.params.exitCode = exitCode;
349 void uiApp::preprocessEvent(uiEvent * event)
351 if (event->dest ==
nullptr) {
354 case UIEVT_MOUSEMOVE:
355 case UIEVT_MOUSEWHEEL:
356 case UIEVT_MOUSEBUTTONDOWN:
357 case UIEVT_MOUSEBUTTONUP:
358 preprocessMouseEvent(event);
362 preprocessKeyboardEvent(event);
371 if (event->params.timerHandle == m_caretTimer) {
373 event->dest =
nullptr;
383 if (m_modalWindow !=
nullptr)
384 filterModalEvent(event);
389 void uiApp::filterModalEvent(uiEvent * event)
391 if (event->dest !=
nullptr && event->dest->objectType().uiWindow &&
event->dest != m_modalWindow && !m_modalWindow->isChild((uiWindow*)event->dest)) {
393 case UIEVT_MOUSEMOVE:
394 case UIEVT_MOUSEWHEEL:
395 case UIEVT_MOUSEBUTTONDOWN:
396 case UIEVT_MOUSEBUTTONUP:
397 case UIEVT_MOUSEENTER:
400 event->dest =
nullptr;
401 if (event->id == UIEVT_MOUSEENTER) {
403 m_displayController->setMouseCursor(m_modalWindow->windowStyle().defaultCursor);
416 void uiApp::preprocessMouseEvent(uiEvent * event)
419 if (m_combineMouseMoveEvents && event->id == UIEVT_MOUSEMOVE) {
421 while (peekEvent(&nextEvent, 0) && nextEvent.id == UIEVT_MOUSEMOVE)
425 m_lastUserActionTimeMS = esp_timer_get_time() / 1000;
427 Point mousePos = Point(event->params.mouse.status.X, event->params.mouse.status.Y);
431 uiWindow * oldFreeMouseWindow = m_freeMouseWindow;
432 Point winMousePos = mousePos;
433 if (m_capturedMouseWindow) {
435 for (uiWindow * cur = m_capturedMouseWindow; cur != m_rootWindow; cur = cur->parent())
436 winMousePos = winMousePos.sub(cur->pos());
437 event->dest = m_capturedMouseWindow;
439 if (event->id == UIEVT_MOUSEBUTTONUP && event->params.mouse.changedButton == 1) {
441 if (!m_capturedMouseWindow->rect(uiOrigin::Window).contains(winMousePos)) {
443 uiEvent evt = uiEvent(m_capturedMouseWindow, UIEVT_MOUSELEAVE);
445 m_freeMouseWindow = oldFreeMouseWindow =
nullptr;
449 m_freeMouseWindow = screenToWindow(winMousePos);
450 event->dest = m_freeMouseWindow;
452 event->params.mouse.status.X = winMousePos.X;
453 event->params.mouse.status.Y = winMousePos.Y;
456 if (oldFreeMouseWindow != m_freeMouseWindow) {
457 if (m_freeMouseWindow) {
458 uiEvent evt = uiEvent(m_freeMouseWindow, UIEVT_MOUSEENTER);
461 if (oldFreeMouseWindow) {
462 uiEvent evt = uiEvent(oldFreeMouseWindow, UIEVT_MOUSELEAVE);
468 if (event->id == UIEVT_MOUSEBUTTONUP && event->params.mouse.changedButton == 1) {
469 int curTime = esp_timer_get_time() / 1000;
470 if (m_lastMouseUpPos == mousePos && curTime - m_lastMouseUpTimeMS <= m_appProps.doubleClickTime) {
472 uiEvent evt = *event;
473 evt.id = UIEVT_DBLCLICK;
476 m_lastMouseUpTimeMS = curTime;
477 m_lastMouseUpPos = mousePos;
482 void uiApp::preprocessKeyboardEvent(uiEvent * event)
484 m_lastUserActionTimeMS = esp_timer_get_time() / 1000;
487 if (m_focusedWindow) {
488 event->dest = m_focusedWindow;
491 if (m_focusedWindow != m_activeWindow) {
492 uiEvent evt = *event;
493 evt.dest = m_activeWindow;
500 void uiApp::captureMouse(uiWindow * window)
502 m_capturedMouseWindow = window;
503 if (m_capturedMouseWindow)
517 for (; child; child = child->
prev()) {
518 if (child->
state().
visible && win->
clientRect(uiOrigin::Window).contains(point) && child->
rect(uiOrigin::Parent).contains(point)) {
520 point = point.sub(child->
pos());
524 if (child ==
nullptr)
536 bool uiApp::postEvent(uiEvent
const * event)
538 return xQueueSendToBack(m_eventsQueue, event, 0) == pdTRUE;
542 bool uiApp::insertEvent(uiEvent
const * event)
544 return xQueueSendToFront(m_eventsQueue, event, 0) == pdTRUE;
548 void uiApp::postDebugMsg(
char const * msg)
550 uiEvent evt = uiEvent(
nullptr, UIEVT_DEBUGMSG);
551 evt.params.debugMsg = msg;
556 bool uiApp::getEvent(uiEvent * event,
int timeOutMS)
558 return xQueueReceive(m_eventsQueue, event, msToTicks(timeOutMS)) == pdTRUE;
562 bool uiApp::peekEvent(uiEvent * event,
int timeOutMS)
564 return xQueuePeek(m_eventsQueue, event, msToTicks(timeOutMS)) == pdTRUE;
568 void uiApp::processEvent(uiEvent * event)
570 uiEvtHandler::processEvent(event);
579 onTimer(event->params.timerHandle);
594 if (value != m_activeWindow) {
596 while (value && !value->m_windowProps.activable) {
597 value = value->m_parent;
601 if (value == m_activeWindow)
605 setFocusedWindow(
nullptr);
610 m_activeWindow = value;
614 uiEvent evt = uiEvent(prev, UIEVT_DEACTIVATE);
618 if (m_activeWindow) {
620 uiEvent evt = uiEvent(m_activeWindow, UIEVT_ACTIVATE);
636 if (value && !value->isFocusable())
639 if (m_focusedWindow != value) {
642 uiEvent evt = uiEvent(prev, UIEVT_KILLFOCUS);
643 evt.params.focusInfo.oldFocused = prev;
644 evt.params.focusInfo.newFocused = value;
648 evt = uiEvent(prev->
parent(), UIEVT_CHILDKILLFOCUS);
649 evt.params.focusInfo.oldFocused = prev;
650 evt.params.focusInfo.newFocused = value;
655 m_focusedWindow = value;
660 if (m_focusedWindow) {
661 uiEvent evt = uiEvent(m_focusedWindow, UIEVT_SETFOCUS);
662 evt.params.focusInfo.oldFocused = prev;
663 evt.params.focusInfo.newFocused = m_focusedWindow;
665 if (m_focusedWindow->parent()) {
667 evt = uiEvent(m_focusedWindow->parent(), UIEVT_CHILDSETFOCUS);
668 evt.params.focusInfo.oldFocused = prev;
669 evt.params.focusInfo.newFocused = m_focusedWindow;
685 int startingIndex = m_focusedWindow ? m_focusedWindow->
focusIndex() + delta : 0;
686 int newIndex = startingIndex;
689 uiWindow * newFocusedCtrl = parent->findChildWithFocusIndex(newIndex, &maxIndex);
691 return m_focusedWindow;
692 if (newFocusedCtrl) {
693 setFocusedWindow(newFocusedCtrl);
694 return newFocusedCtrl;
697 newIndex = (newIndex >= maxIndex ? 0 : newIndex + delta);
699 newIndex = (newIndex <= 0 ? maxIndex : newIndex + delta);
700 }
while (newIndex != startingIndex);
701 return m_focusedWindow;
707 repaintRect(window->
rect(uiOrigin::Screen));
711 void uiApp::repaintRect(
Rect const & rect)
713 uiEvent evt = uiEvent(m_rootWindow, UIEVT_GENPAINTEVENTS);
714 evt.params.rect = rect;
720 void uiApp::moveWindow(
uiWindow * window,
int x,
int y)
728 reshapeWindow(window, window->
rect(uiOrigin::Parent).resize(
width,
height));
734 reshapeWindow(window, window->
rect(uiOrigin::Parent).resize(size));
741 uiEvent evt = uiEvent(window, UIEVT_RESHAPEWINDOW);
742 evt.params.rect = rect;
747 void uiApp::showWindow(
uiWindow * window,
bool value)
749 window->m_state.
visible = value;
750 uiEvent evt = uiEvent(window, value ? UIEVT_SHOW : UIEVT_HIDE);
755 ModalWindowState * uiApp::initModalWindow(
uiWindow * window)
757 showWindow(window,
true);
759 auto state =
new ModalWindowState;
760 state->window = window;
761 state->modalResult = -1;
762 state->prevFocusedWindow = setFocusedWindow(
nullptr);
763 state->prevActiveWindow = setActiveWindow(window);
764 state->prevModal = m_modalWindow;
773 bool uiApp::processModalWindowEvents(ModalWindowState * state,
int timeout)
777 while (getEvent(&event, timeout)) {
779 if (m_modalWindow != state->window && event.dest == state->window) {
781 m_modalWindow = state->window;
783 if (event.id == UIEVT_EXITMODAL) {
785 state->modalResult =
event.params.modalResult;
787 }
else if (event.id == UIEVT_CLOSE) {
792 preprocessEvent(&event);
795 event.dest->processEvent(&event);
803 int uiApp::endModalWindow(ModalWindowState * state)
805 m_modalWindow = state->prevModal;
806 setActiveWindow(state->prevActiveWindow);
807 showWindow(state->window,
false);
808 setFocusedWindow(state->prevFocusedWindow);
809 int result = state->modalResult;
817 auto state = initModalWindow(window);
818 while (processModalWindowEvents(state, -1))
820 return endModalWindow(state);
824 void uiApp::maximizeWindow(
uiWindow * window,
bool value)
826 uiEvent evt = uiEvent(window, value ? UIEVT_MAXIMIZE : UIEVT_RESTORE);
831 void uiApp::minimizeWindow(
uiWindow * window,
bool value)
833 uiEvent evt = uiEvent(window, value ? UIEVT_MINIMIZE : UIEVT_RESTORE);
838 void uiApp::timerFunc(TimerHandle_t xTimer)
841 uiEvent evt = uiEvent(dest, UIEVT_TIMER);
842 evt.params.timerHandle = xTimer;
850 TimerHandle_t h = xTimerCreate(
"", pdMS_TO_TICKS(periodMS), pdTRUE, dest, &uiApp::timerFunc);
851 m_timers.push_back(uiTimerAssoc(dest, h));
857 void uiApp::killTimer(uiTimerHandle handle)
860 m_timers.remove(uiTimerAssoc(dest, handle));
861 xTimerStop(handle, portMAX_DELAY);
862 xTimerDelete(handle, portMAX_DELAY);
868 for (
auto t : m_timers)
869 if (t.first == dest) {
870 xTimerStop(t.second, portMAX_DELAY);
871 xTimerDelete(t.second, portMAX_DELAY);
873 m_timers.remove_if([&](uiTimerAssoc
const & p) {
return p.first == dest; });
879 void uiApp::showCaret(uiWindow * window)
881 if (m_caretWindow != window) {
882 if (window && window == m_focusedWindow) {
884 m_caretWindow = window;
885 m_caretTimer = setTimer(m_rootWindow, m_appProps.caretBlinkingTime);
886 m_caretInvertState = 0;
888 }
else if (m_caretTimer) {
891 killTimer(m_caretTimer);
892 m_caretTimer =
nullptr;
893 m_caretWindow = NULL;
899 void uiApp::suspendCaret(
bool value)
903 if (m_caretInvertState != -1) {
904 xTimerStop(m_caretTimer, 0);
906 m_caretInvertState = -1;
909 if (m_caretInvertState == -1) {
910 xTimerStart(m_caretTimer, 0);
911 m_caretInvertState = 0;
920 void uiApp::setCaret(
bool value)
926 void uiApp::setCaret(Point
const & pos)
928 setCaret(m_caretRect.move(pos));
932 void uiApp::setCaret(Rect
const & rect)
940 void uiApp::blinkCaret(
bool forceOFF)
942 if (m_caretWindow && m_caretInvertState != -1 && (forceOFF ==
false || m_caretInvertState == 1)) {
943 m_canvas->resetPaintOptions();
944 m_canvas->setOrigin(m_rootWindow->pos());
945 m_canvas->setClippingRect(m_caretWindow->clientRect(uiOrigin::Screen));
946 Rect aRect = m_caretWindow->transformRect(m_caretRect, m_rootWindow);
947 m_canvas->invertRectangle(aRect);
948 m_caretInvertState = m_caretInvertState ? 0 : 1;
958 for (
auto child = window->
lastChild(); child; child = child->
prev())
959 destroyWindow(child);
961 if (m_caretWindow == window)
963 if (m_focusedWindow == window)
964 setFocusedWindow(
nullptr);
965 if (m_activeWindow == window)
966 setActiveWindow(
nullptr);
968 showWindow(window,
false);
970 uiEvent evt = uiEvent(window, UIEVT_DESTROY);
976 void uiApp::cleanWindowReferences(
uiWindow * window)
978 if (m_capturedMouseWindow == window)
979 m_capturedMouseWindow =
nullptr;
980 if (m_freeMouseWindow == window)
981 m_freeMouseWindow =
nullptr;
982 if (m_activeWindow == window)
983 m_activeWindow =
nullptr;
984 if (m_focusedWindow == window)
985 m_focusedWindow =
nullptr;
986 if (m_modalWindow == window)
987 m_modalWindow =
nullptr;
988 if (m_caretWindow == window)
989 m_caretWindow =
nullptr;
995 auto font = &FONT_std_14;
996 const int titleHeight = title && strlen(title) ? font->height : 0;
997 const int textExtent = m_canvas->textExtent(font, text);
998 const int button1Extent = button1Text ? m_canvas->textExtent(font, button1Text) + 10 : 0;
999 const int button2Extent = button2Text ? m_canvas->textExtent(font, button2Text) + 10 : 0;
1000 const int button3Extent = button3Text ? m_canvas->textExtent(font, button3Text) + 10 : 0;
1001 const int buttonsWidth = imax(imax(imax(button1Extent, button2Extent), button3Extent), 40);
1009 const int buttonsHeight = font->height + 6;
1010 auto bitmap = (icon == uiMessageBoxIcon::Question ? &questionBitmap :
1011 (icon == uiMessageBoxIcon::Info ? &infoBitmap :
1012 (icon == uiMessageBoxIcon::Warning ? &warnBitmap :
1013 (icon == uiMessageBoxIcon::Error ? &errorBitmap :
nullptr))));
1014 const int bitmapWidth = bitmap ? bitmap->width : 0;
1015 const int bitmapHeight = bitmap ? bitmap->height : 0;
1016 constexpr
int buttonsSpace = 10;
1017 const int bitmapSpace = bitmap ? 8 : 0;
1018 const int textHeight = imax(font->height, bitmapHeight);
1019 const int requiredWidth = imin(imax(bitmapWidth + bitmapSpace + textExtent + 10, buttonsWidth * totButtons + (2 + buttonsSpace) * totButtons), m_canvas->getWidth());
1020 const int requiredHeight = textHeight + buttonsHeight + titleHeight + font->height * 3;
1021 const int frameX = (m_canvas->getWidth() - requiredWidth) / 2;
1022 const int frameY = (m_canvas->getHeight() - requiredHeight) / 2;
1024 auto mainFrame =
new uiFrame(m_rootWindow, title,
Point(frameX, frameY),
Size(requiredWidth, requiredHeight),
false);
1025 mainFrame->frameProps().resizeable =
false;
1026 mainFrame->frameProps().hasMaximizeButton =
false;
1027 mainFrame->frameProps().hasMinimizeButton =
false;
1029 int x = (requiredWidth - bitmapWidth - bitmapSpace - textExtent) / 2;
1031 int y = font->height + titleHeight + (textHeight - bitmapHeight) / 2;
1033 x += bitmapWidth + bitmapSpace;
1036 int y = font->height + titleHeight + (textHeight - font->height) / 2;
1041 y += textHeight + titleHeight;
1042 auto panel =
new uiPanel(mainFrame,
Point(0, y),
Size(mainFrame->size().width, mainFrame->size().height - y));
1043 panel->windowStyle().borderColor =
RGB888(128, 128, 128);
1044 panel->panelStyle().backgroundColor = mainFrame->frameStyle().backgroundColor;
1048 y = (panel->size().height - buttonsHeight) / 2;
1049 x = mainFrame->windowStyle().borderSize + requiredWidth - buttonsWidth * totButtons - buttonsSpace * totButtons;
1051 auto button1 = button1Text ?
new uiButton(panel, button1Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1053 button1->onClick = [&]() { mainFrame->exitModal(1); };
1054 x += buttonsWidth + buttonsSpace;
1057 auto button2 = button2Text ?
new uiButton(panel, button2Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1059 button2->onClick = [&]() { mainFrame->exitModal(2); };
1060 x += buttonsWidth + buttonsSpace;
1063 auto button3 = button3Text ?
new uiButton(panel, button3Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1065 button3->onClick = [&]() { mainFrame->exitModal(3); };
1066 x += buttonsWidth + buttonsSpace;
1070 mainFrame->onShow = [&]() {
1071 setFocusedWindow(button1);
1076 int modalResult = showModalWindow(mainFrame);
1077 destroyWindow(mainFrame);
1079 switch (modalResult) {
1081 return uiMessageBoxResult::Button1;
1083 return uiMessageBoxResult::Button2;
1085 return uiMessageBoxResult::Button3;
1087 return uiMessageBoxResult::Cancel;
1092 uiMessageBoxResult uiApp::inputBox(
char const * title,
char const * text,
char * inOutString,
int maxLength,
char const * button1Text,
char const * button2Text)
1094 auto font = &FONT_std_14;
1095 const int titleHeight = title && strlen(title) ? font->height : 0;
1096 const int textExtent = m_canvas->textExtent(font, text);
1097 const int editExtent = imin(maxLength * m_canvas->textExtent(font,
"M"), m_rootWindow->clientSize().width / 2 - textExtent);
1098 const int button1Extent = button1Text ? m_canvas->textExtent(font, button1Text) + 10 : 0;
1099 const int button2Extent = button2Text ? m_canvas->textExtent(font, button2Text) + 10 : 0;
1100 const int buttonsWidth = imax(imax(button1Extent, button2Extent), 40);
1106 const int buttonsHeight = font->height + 6;
1107 const int textHeight = font->height;
1108 constexpr
int buttonsSpace = 10;
1109 const int requiredWidth = imin(imax(editExtent + textExtent + 10, buttonsWidth * totButtons + (2 + buttonsSpace) * totButtons), m_canvas->getWidth());
1110 const int requiredHeight = textHeight + buttonsHeight + titleHeight + font->height * 3;
1111 const int frameX = (m_canvas->getWidth() - requiredWidth) / 2;
1112 const int frameY = (m_canvas->getHeight() - requiredHeight) / 2;
1114 auto mainFrame =
new uiFrame(m_rootWindow, title,
Point(frameX, frameY),
Size(requiredWidth, requiredHeight),
false);
1115 mainFrame->frameProps().resizeable =
false;
1116 mainFrame->frameProps().hasMaximizeButton =
false;
1117 mainFrame->frameProps().hasMinimizeButton =
false;
1120 mainFrame->exitModal(1);
1122 mainFrame->exitModal(0);
1126 int y = font->height + titleHeight + (textHeight - font->height) / 2;
1129 auto edit =
new uiTextEdit(mainFrame, inOutString,
Point(x + textExtent + 5, y - 4),
Size(editExtent - 15, textHeight + 6));
1133 y += textHeight + titleHeight;
1134 auto panel =
new uiPanel(mainFrame,
Point(0, y),
Size(mainFrame->size().width, mainFrame->size().height - y));
1135 panel->windowStyle().borderColor =
RGB888(128, 128, 128);
1136 panel->panelStyle().backgroundColor = mainFrame->frameStyle().backgroundColor;
1140 y = (panel->size().height - buttonsHeight) / 2;
1141 x = mainFrame->windowStyle().borderSize + requiredWidth - buttonsWidth * totButtons - buttonsSpace * totButtons;
1143 auto button1 = button1Text ?
new uiButton(panel, button1Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1145 button1->onClick = [&]() { mainFrame->exitModal(1); };
1146 x += buttonsWidth + buttonsSpace;
1149 auto button2 = button2Text ?
new uiButton(panel, button2Text,
Point(x, y),
Size(buttonsWidth, buttonsHeight)) :
nullptr;
1151 button2->onClick = [&]() { mainFrame->exitModal(2); };
1152 x += buttonsWidth + buttonsSpace;
1156 mainFrame->onShow = [&]() {
1157 setFocusedWindow(edit);
1162 int modalResult = showModalWindow(mainFrame);
1163 destroyWindow(mainFrame);
1165 switch (modalResult) {
1168 int len = imin(maxLength, strlen(edit->text()));
1169 memcpy(inOutString, edit->text(), len);
1170 inOutString[len] = 0;
1171 return uiMessageBoxResult::Button1;
1174 return uiMessageBoxResult::Button2;
1176 return uiMessageBoxResult::Cancel;
1181 void uiApp::enableKeyboardAndMouseEvents(
bool value)
1185 m_keyboard->setUIApp(
this);
1186 if (m_mouse && m_mouse->isMouseAvailable()) {
1187 m_mouse->setUIApp(
this);
1188 m_displayController->setMouseCursor(m_rootWindow->windowStyle().defaultCursor);
1192 m_keyboard->setUIApp(
nullptr);
1193 if (m_mouse && m_mouse->isMouseAvailable()) {
1194 m_mouse->setUIApp(
nullptr);
1195 m_displayController->setMouseCursor(
nullptr);
1210 uiWindow::uiWindow(
uiWindow * parent,
const Point & pos,
const Size & size,
bool visible, uint32_t styleClassID)
1215 m_mouseDownPos(
Point(-1, -1)),
1216 m_isMouseOver(false),
1219 m_firstChild(nullptr),
1220 m_lastChild(nullptr),
1221 m_styleClassID(styleClassID),
1222 m_parentProcessKbdEvents(false)
1232 m_canvas =
app()->canvas();
1237 if (m_pos == UIWINDOW_PARENTCENTER) {
1241 m_pos =
Point(0, 0);
1248 if (visible &&
app())
1252 m_focusIndex = pframe ? ((
uiFrame*)pframe)->getNextFreeFocusIndex() : 0;
1255 uiEvent evt = uiEvent(
this, UIEVT_CREATE);
1261 uiWindow::~uiWindow()
1267 void uiWindow::freeChildren()
1273 m_firstChild = m_lastChild =
nullptr;
1277 void uiWindow::addChild(uiWindow * child)
1281 m_lastChild->m_next = child;
1282 child->m_prev = m_lastChild;
1283 m_lastChild = child;
1286 m_firstChild = m_lastChild = child;
1293 void uiWindow::insertAfter(uiWindow * child, uiWindow * underlyingChild)
1300 child->m_prev = underlyingChild;
1301 if (underlyingChild) {
1303 child->m_next = underlyingChild->m_next;
1305 child->m_next->m_prev = child;
1306 underlyingChild->m_next = child;
1307 if (m_lastChild == underlyingChild)
1308 m_lastChild = child;
1311 m_firstChild->m_prev = child;
1312 child->m_next = m_firstChild;
1313 m_firstChild = child;
1318 void uiWindow::removeChild(uiWindow * child,
bool freeChild)
1321 if (child == m_firstChild)
1322 m_firstChild = child->m_next;
1324 child->m_prev->m_next = child->m_next;
1326 if (child == m_lastChild)
1327 m_lastChild = child->m_prev;
1329 child->m_next->m_prev = child->m_prev;
1333 app()->cleanWindowReferences(child);
1335 child->m_prev = child->m_next =
nullptr;
1342 void uiWindow::moveChildOnTop(uiWindow * child)
1344 removeChild(child,
false);
1351 void uiWindow::moveAfter(uiWindow * child, uiWindow * underlyingChild)
1353 removeChild(child,
false);
1354 insertAfter(child, underlyingChild);
1360 parent()->moveChildOnTop(
this);
1366 parent()->moveAfter(
this, insertionPoint);
1371 bool uiWindow::isChild(
uiWindow * window)
1374 if (child == window || (child->hasChildren() && child->isChild(window)))
1384 for (
uiWindow * win =
this; win != baseWindow; win = win->m_parent)
1385 r = r.translate(win->m_pos);
1422 return rect(origin).shrink(bSize);
1438 void uiWindow::beginPaint(uiEvent * paintEvent,
Rect const & clippingRect)
1442 canvas()->
setClippingRect( clippingRect.intersection(paintEvent->params.rect) );
1448 void uiWindow::processEvent(uiEvent * event)
1450 uiEvtHandler::processEvent(event);
1452 switch (event->id) {
1455 m_parent->removeChild(
this);
1463 case UIEVT_ACTIVATE:
1468 for (
uiWindow * child =
this; child->parent() !=
nullptr; child = child->parent()) {
1469 if (child != child->parent()->lastChild()) {
1470 child->parent()->moveChildOnTop(child);
1471 winToRepaint = child;
1474 winToRepaint->repaint();
1478 case UIEVT_DEACTIVATE:
1483 case UIEVT_MOUSEBUTTONDOWN:
1484 if (event->params.mouse.changedButton == 1) {
1485 m_mouseDownPos = Point(event->params.mouse.status.X, event->params.mouse.status.Y);
1486 m_posAtMouseDown = m_pos;
1487 m_sizeAtMouseDown = m_size;
1494 app()->captureMouse(
this);
1498 case UIEVT_MOUSEBUTTONUP:
1500 if (event->params.mouse.changedButton == 1) {
1501 app()->captureMouse(
nullptr);
1503 if (
rect(
uiOrigin::Window).contains(event->params.mouse.status.X, event->params.mouse.status.Y)) {
1504 uiEvent evt = *event;
1505 evt.id = UIEVT_CLICK;
1515 case UIEVT_DBLCLICK:
1527 case UIEVT_MAXIMIZE:
1535 case UIEVT_MINIMIZE:
1549 case UIEVT_RESHAPEWINDOW:
1550 reshape(event->params.rect);
1553 case UIEVT_GENPAINTEVENTS:
1554 generatePaintEvents(event->params.rect);
1557 case UIEVT_MOUSEENTER:
1558 m_isMouseOver =
true;
1562 case UIEVT_MOUSELEAVE:
1563 m_isMouseOver =
false;
1567 if (m_parentProcessKbdEvents)
1568 m_parent->processEvent(event);
1572 if (m_parentProcessKbdEvents)
1573 m_parent->processEvent(event);
1581 case UIEVT_SETFOCUS:
1582 case UIEVT_KILLFOCUS:
1592 void uiWindow::paintWindow()
1598 for (
int i = 0; i < bSize; ++i)
1605 void uiWindow::generatePaintEvents(Rect
const & paintRect)
1607 app()->setCaret(
false);
1609 rects.push(paintRect);
1610 while (!rects.isEmpty()) {
1611 Rect thisRect = rects.pop();
1612 bool noIntesections =
true;
1615 if (win->state().visible && thisRect.intersects(winRect)) {
1616 noIntesections =
false;
1617 removeRectangle(rects, thisRect, winRect);
1618 Rect newRect = thisRect.intersection(winRect).translate(-win->pos().X, -win->pos().Y);
1619 win->generatePaintEvents(newRect);
1623 if (noIntesections) {
1624 uiEvent evt = uiEvent(
nullptr, UIEVT_PAINT);
1626 evt.params.rect = thisRect;
1637 void uiWindow::reshape(Rect
const & r)
1645 if (oldRect == newRect)
1649 m_pos = Point(r.X1, r.Y1);
1652 if (!oldRect.intersects(newRect)) {
1657 removeRectangle(rects, oldRect, newRect);
1658 while (!rects.isEmpty())
1663 uiEvent evt = uiEvent(
this, UIEVT_SETPOS);
1664 evt.params.pos =
pos();
1668 evt = uiEvent(
this, UIEVT_SETSIZE);
1669 evt.params.size =
size();
1673 int dx = newRect.width() - oldRect.width();
1674 int dy = newRect.height() - oldRect.height();
1675 if (dx != 0 || dy != 0) {
1678 Rect newChildRect = childRect;
1680 if (!child->m_anchors.left && !child->m_anchors.right) {
1682 int ofs = dx > 0 ? imax(1, dx / 2) : imin(-1, dx / 2);
1683 newChildRect.X1 += ofs;
1684 newChildRect.X2 += ofs;
1685 }
else if (!child->m_anchors.left)
1686 newChildRect.X1 += dx;
1687 if (child->m_anchors.right)
1688 newChildRect.X2 += dx;
1691 if (!child->m_anchors.top && !child->m_anchors.bottom) {
1693 int ofs = dy > 0 ? imax(1, dy / 2) : imin(-1, dy / 2);
1694 newChildRect.Y1 += ofs;
1695 newChildRect.Y2 += ofs;
1696 }
else if (!child->m_anchors.top)
1697 newChildRect.Y1 += dy;
1698 if (child->m_anchors.bottom)
1699 newChildRect.Y2 += dy;
1701 if (newChildRect != childRect) {
1703 child->m_pos.X = newChildRect.X1;
1704 child->m_pos.Y = newChildRect.Y1;
1705 child->m_size.width = newChildRect.width();
1706 child->m_size.height = newChildRect.height();
1708 uiEvent evt = uiEvent(child, UIEVT_SETPOS);
1709 evt.params.pos = child->pos();
1712 evt = uiEvent(child, UIEVT_SETSIZE);
1713 evt.params.size = child->size();
1725 uiEvent evt = uiEvent(
this, UIEVT_EXITMODAL);
1726 evt.params.modalResult = modalResult;
1737 bool uiWindow::isFocusable()
1744 uiWindow * uiWindow::findChildWithFocusIndex(
int focusIndex,
int * maxIndex)
1746 for (
auto child = m_firstChild; child; child = child->m_next) {
1747 if (child->isFocusable()) {
1748 *maxIndex = imax(*maxIndex, child->m_focusIndex);
1753 if (child->hasChildren()) {
1754 auto r = child->findChildWithFocusIndex(
focusIndex, maxIndex);
1767 while (ret && ret->
objectType().uiFrame == 0)
1783 :
uiWindow(parent, pos, size, visible, 0),
1786 m_mouseDownFrameItem(uiFrameItem::
None),
1787 m_mouseMoveFrameItem(uiFrameItem::
None),
1788 m_lastReshapingBox(
Rect(0, 0, 0, 0)),
1789 m_nextFreeFocusIndex(0)
1806 m_titleLength = strlen(value);
1807 m_title = (
char*) realloc(m_title, m_titleLength + 1);
1808 strcpy(m_title, value);
1815 va_start(ap, format);
1816 int size = vsnprintf(
nullptr, 0, format, ap) + 1;
1819 va_start(ap, format);
1821 vsnprintf(buf,
size, format, ap);
1828 int uiFrame::titleBarHeight()
1830 return m_frameStyle.
titleFont->height + 3;
1834 Rect uiFrame::titleBarRect()
1837 r.Y2 = r.Y1 + titleBarHeight() - 1;
1847 if (m_titleLength > 0)
1848 r.
Y1 += titleBarHeight();
1854 Size uiFrame::minWindowSize()
1857 if (m_frameProps.
resizeable && !
state().minimized && m_titleLength == 0) {
1858 r.
width += CORNERSENSE * 2;
1859 r.
height += CORNERSENSE * 2;
1863 if (m_titleLength > 0) {
1864 int barHeight = titleBarHeight();
1867 r.
width += barHeight * 3;
1868 r.
width += barHeight * 4;
1878 Rect uiFrame::getBtnRect(
int buttonIndex)
1880 int btnSize = titleBarHeight();
1881 Rect barRect = titleBarRect();
1882 Rect btnRect = Rect(barRect.X2 - btnSize - CORNERSENSE / 2, barRect.Y1,
1883 barRect.X2 - CORNERSENSE / 2, barRect.Y2);
1884 while (buttonIndex--)
1885 btnRect = btnRect.translate(-btnSize, 0);
1890 void uiFrame::paintFrame()
1894 if (m_titleLength > 0) {
1895 int barHeight = titleBarHeight();
1901 int btnX = paintButtons(bkgRect);
1907 bkgRect.Y1 += barHeight;
1910 if (m_frameProps.
fillBackground && !
state().minimized && bkgRect.width() > 0 && bkgRect.height() > 0) {
1918 int uiFrame::paintButtons(Rect
const & bkgRect)
1920 int buttonsX = bkgRect.X2;
1923 Rect r = getBtnRect(0);
1925 if (m_mouseMoveFrameItem == uiFrameItem::CloseButton) {
1932 canvas()->
drawLine(r.X1, r.Y1, r.X2, r.Y2);
1933 canvas()->
drawLine(r.X2, r.Y1, r.X1, r.Y2);
1937 Rect r = getBtnRect(1);
1939 if (m_mouseMoveFrameItem == uiFrameItem::MaximizeButton) {
1946 if (
state().maximized ||
state().minimized) {
1948 r = r.shrink(1).translate(-1, +1);
1950 r = r.translate(+2, -2);
1951 canvas()->
moveTo(r.X1, r.Y1 + 2);
1952 canvas()->
lineTo(r.X1, r.Y1);
1953 canvas()->
lineTo(r.X2, r.Y1);
1954 canvas()->
lineTo(r.X2, r.Y2);
1955 canvas()->
lineTo(r.X2 - 2, r.Y2);
1961 Rect r = getBtnRect(2);
1963 if (m_mouseMoveFrameItem == uiFrameItem::MinimizeButton) {
1970 int h = (r.Y2 - r.Y1 + 1) / 2;
1971 canvas()->
drawLine(r.X1, r.Y1 + h, r.X2, r.Y1 + h);
1977 void uiFrame::processEvent(uiEvent * event)
1979 uiWindow::processEvent(event);
1981 switch (event->id) {
1989 case UIEVT_MOUSEBUTTONDOWN:
1990 if (event->params.mouse.changedButton == 1) {
1991 m_mouseDownFrameItem = getFrameItemAt(event->params.mouse.status.X, event->params.mouse.status.Y);
1992 app()->combineMouseMoveEvents(
true);
1996 case UIEVT_MOUSEBUTTONUP:
1997 if (event->params.mouse.changedButton == 1) {
1998 int mouseX =
event->params.mouse.status.X;
1999 int mouseY =
event->params.mouse.status.Y;
2002 movingCapturedMouse(mouseX, mouseY,
false);
2005 movingFreeMouse(mouseX, mouseY);
2008 handleButtonsClick(mouseX, mouseY,
false);
2010 app()->combineMouseMoveEvents(
false);
2014 case UIEVT_MOUSEMOVE:
2015 if (
app()->capturedMouseWindow() ==
this)
2016 movingCapturedMouse(event->params.mouse.status.X, event->params.mouse.status.Y,
true);
2018 movingFreeMouse(event->params.mouse.status.X, event->params.mouse.status.Y);
2021 case UIEVT_MOUSELEAVE:
2022 if (m_mouseMoveFrameItem == uiFrameItem::CloseButton)
2024 if (m_mouseMoveFrameItem == uiFrameItem::MaximizeButton)
2026 if (m_mouseMoveFrameItem == uiFrameItem::MinimizeButton)
2028 m_mouseMoveFrameItem = uiFrameItem::None;
2031 case UIEVT_DBLCLICK:
2032 handleButtonsClick(event->params.mouse.status.X, event->params.mouse.status.Y,
true);
2048 onTimer(event->params.timerHandle);
2053 if (event->params.key.VK ==
VK_TAB) {
2054 if (event->params.key.SHIFT)
2072 uiFrameItem uiFrame::getFrameItemAt(
int x,
int y)
2074 Point p = Point(x, y);
2076 if (m_titleLength > 0) {
2078 return uiFrameItem::CloseButton;
2081 return uiFrameItem::MaximizeButton;
2084 return uiFrameItem::MinimizeButton;
2093 if (Rect(CORNERSENSE, 0, w - CORNERSENSE,
windowStyle().borderSize).contains(p))
2094 return uiFrameItem::TopCenterResize;
2097 if (Rect(0, CORNERSENSE,
windowStyle().borderSize, h - CORNERSENSE).contains(p))
2098 return uiFrameItem::CenterLeftResize;
2101 if (Rect(w -
windowStyle().borderSize, CORNERSENSE, w - 1, h - CORNERSENSE).contains(p))
2102 return uiFrameItem::CenterRightResize;
2105 if (Rect(CORNERSENSE, h -
windowStyle().borderSize, w - CORNERSENSE, h - 1).contains(p))
2106 return uiFrameItem::BottomCenterResize;
2109 if (Rect(0, 0, CORNERSENSE, CORNERSENSE).contains(p))
2110 return uiFrameItem::TopLeftResize;
2113 if (Rect(w - CORNERSENSE, 0, w - 1, CORNERSENSE).contains(p))
2114 return uiFrameItem::TopRightResize;
2117 if (Rect(0, h - CORNERSENSE, CORNERSENSE, h - 1).contains(p))
2118 return uiFrameItem::BottomLeftResize;
2121 if (Rect(w - CORNERSENSE, h - CORNERSENSE, w - 1, h - 1).contains(p))
2122 return uiFrameItem::BottomRightResize;
2127 if (m_titleLength > 0 && m_frameProps.
moveable && !
state().maximized && titleBarRect().contains(p))
2128 return uiFrameItem::MoveArea;
2130 return uiFrameItem::None;
2134 void uiFrame::movingCapturedMouse(
int mouseX,
int mouseY,
bool mouseIsDown)
2139 Size minSize = minWindowSize();
2143 switch (m_mouseDownFrameItem) {
2145 case uiFrameItem::MoveArea:
2146 newRect = newRect.move(
pos().
X + dx,
pos().
Y + dy);
2149 case uiFrameItem::CenterRightResize:
2150 newRect = newRect.resize(imax(sizeAtMouseDown().
width + dx, minSize.width), newRect.height());
2153 case uiFrameItem::CenterLeftResize:
2156 r.X1 =
pos().
X + dx;
2157 newRect.X1 = r.X1 - imax(0, minSize.width - r.size().width);
2161 case uiFrameItem::TopLeftResize:
2164 r.X1 =
pos().
X + dx;
2165 newRect.X1 = r.X1 - imax(0, minSize.width - r.size().width);
2166 r.Y1 =
pos().
Y + dy;
2167 newRect.Y1 = r.Y1 - imax(0, minSize.height - r.size().height);
2171 case uiFrameItem::TopCenterResize:
2174 r.Y1 =
pos().
Y + dy;
2175 newRect.Y1 = r.Y1 - imax(0, minSize.height - r.size().height);
2179 case uiFrameItem::TopRightResize:
2182 r.X2 =
pos().
X + sizeAtMouseDown().
width + dx;
2183 newRect.X2 = r.X2 + imax(0, minSize.width - r.size().width);
2184 r.Y1 =
pos().
Y + dy;
2185 newRect.Y1 = r.Y1 - imax(0, minSize.height - r.size().height);
2189 case uiFrameItem::BottomLeftResize:
2192 r.X1 =
pos().
X + dx;
2193 newRect.X1 = r.X1 - imax(0, minSize.width - r.size().width);
2194 r.Y2 =
pos().
Y + sizeAtMouseDown().
height + dy;
2195 newRect.Y2 = r.Y2 + imax(0, minSize.height - r.size().height);
2199 case uiFrameItem::BottomCenterResize:
2200 newRect = newRect.resize(newRect.width(), imax(sizeAtMouseDown().
height + dy, minSize.height));
2203 case uiFrameItem::BottomRightResize:
2204 newRect = newRect.resize(imax(sizeAtMouseDown().
width + dx, minSize.width), imax(sizeAtMouseDown().
height + dy, minSize.height));
2212 if (mouseIsDown ==
false ||
app()->appProps().realtimeReshaping) {
2213 m_lastReshapingBox = Rect();
2216 drawReshapingBox(newRect);
2220 void uiFrame::drawReshapingBox(Rect boxRect)
2228 if (m_lastReshapingBox != Rect()) {
2230 if (m_titleLength > 0)
2231 canvas()->
drawLine(m_lastReshapingBox.
X1, m_lastReshapingBox.
Y1 + clientOffsetY, m_lastReshapingBox.
X2, m_lastReshapingBox.
Y1 + clientOffsetY);
2233 if (boxRect != Rect()) {
2235 if (m_titleLength > 0)
2236 canvas()->
drawLine(boxRect.X1, boxRect.Y1 + clientOffsetY, boxRect.X2, boxRect.Y1 + clientOffsetY);
2239 m_lastReshapingBox = boxRect;
2243 void uiFrame::movingFreeMouse(
int mouseX,
int mouseY)
2245 uiFrameItem prevSensPos = m_mouseMoveFrameItem;
2247 m_mouseMoveFrameItem = getFrameItemAt(mouseX, mouseY);
2249 if ((m_mouseMoveFrameItem == uiFrameItem::CloseButton || prevSensPos == uiFrameItem::CloseButton) && m_mouseMoveFrameItem != prevSensPos)
2252 if ((m_mouseMoveFrameItem == uiFrameItem::MaximizeButton || prevSensPos == uiFrameItem::MaximizeButton) && m_mouseMoveFrameItem != prevSensPos)
2255 if ((m_mouseMoveFrameItem == uiFrameItem::MinimizeButton || prevSensPos == uiFrameItem::MinimizeButton) && m_mouseMoveFrameItem != prevSensPos)
2260 switch (m_mouseMoveFrameItem) {
2262 case uiFrameItem::TopLeftResize:
2266 case uiFrameItem::TopCenterResize:
2270 case uiFrameItem::TopRightResize:
2274 case uiFrameItem::CenterLeftResize:
2278 case uiFrameItem::CenterRightResize:
2282 case uiFrameItem::BottomLeftResize:
2286 case uiFrameItem::BottomCenterResize:
2290 case uiFrameItem::BottomRightResize:
2302 void uiFrame::handleButtonsClick(
int x,
int y,
bool doubleClick)
2304 if (m_titleLength > 0) {
2307 uiEvent evt = uiEvent(
this, UIEVT_CLOSE);
2310 (doubleClick && titleBarRect().contains(x, y)))) {
2320 m_mouseMoveFrameItem = uiFrameItem::None;
2335 :
uiWindow(parent, pos, size, visible, 0)
2345 uiControl::~uiControl()
2350 void uiControl::processEvent(uiEvent * event)
2352 uiWindow::processEvent(event);
2367 :
uiControl(parent, pos, size, visible, 0),
2389 uiButton::~uiButton()
2397 int len = strlen(value);
2398 m_text = (
char*) realloc(m_text, len + 1);
2399 strcpy(m_text, value);
2405 void uiButton::paintButton()
2410 if (
app()->capturedMouseWindow() ==
this)
2417 paintContent(bkgRect);
2421 void uiButton::paintContent(Rect
const & rect)
2423 Bitmap
const * bitmap = m_down ? m_buttonStyle.
downBitmap : m_buttonStyle.
bitmap;
2424 int textHeight = m_buttonStyle.
textFont->height;
2425 int bitmapWidth = bitmap ? bitmap->width : 0;
2426 int bitmapHeight = bitmap ? bitmap->height : 0;
2429 int x =
rect.
X1 + (
rect.size().
width - m_textExtent - bitmapTextSpace - bitmapWidth) / 2;
2430 int y =
rect.
Y1 + (
rect.size().
height - imax(textHeight, bitmapHeight)) / 2;
2434 x += bitmapWidth + bitmapTextSpace;
2435 y += (imax(textHeight, bitmapHeight) - textHeight) / 2;
2443 void uiButton::processEvent(uiEvent * event)
2445 uiControl::processEvent(event);
2447 switch (event->id) {
2458 case UIEVT_MOUSEENTER:
2462 case UIEVT_MOUSEBUTTONDOWN:
2463 if (event->params.mouse.changedButton == 1)
2467 case UIEVT_MOUSELEAVE:
2485 void uiButton::trigger()
2497 if (value != m_down) {
2516 :
uiControl(parent, pos, size, visible, 0),
2540 uiTextEdit::~uiTextEdit()
2548 m_textLength = strlen(value);
2549 checkAllocatedSpace(m_textLength);
2550 strcpy(m_text, value);
2554 void uiTextEdit::processEvent(uiEvent * event)
2556 uiControl::processEvent(event);
2558 switch (event->id) {
2564 app()->setCaret(
true);
2567 case UIEVT_MOUSEBUTTONDOWN:
2568 if (event->params.mouse.changedButton == 1) {
2569 int col = getColFromMouseX(event->params.mouse.status.X);
2570 moveCursor(col, col);
2575 case UIEVT_MOUSEBUTTONUP:
2578 case UIEVT_MOUSEENTER:
2582 case UIEVT_MOUSELEAVE:
2586 case UIEVT_MOUSEMOVE:
2588 if (
app()->capturedMouseWindow() ==
this)
2589 moveCursor(getColFromMouseX(event->params.mouse.status.X), m_selCursorCol);
2592 case UIEVT_SETFOCUS:
2595 app()->showCaret(
this);
2600 case UIEVT_KILLFOCUS:
2602 app()->showCaret(NULL);
2608 handleKeyDown(event->params.key);
2611 case UIEVT_DBLCLICK:
2612 selectWordAt(event->params.mouse.status.X);
2621 int uiTextEdit::keyToASCII(uiKeyEventInfo
const & key)
2624 if (m_codepage ==
nullptr || m_codepage->codepage != m_textEditStyle.
textFont->codepage)
2625 m_codepage = CodePages::get(m_textEditStyle.
textFont->codepage);
2627 VirtualKeyItem item = { };
2629 item.CTRL = key.CTRL;
2630 item.SHIFT = key.SHIFT;
2631 return virtualKeyToASCII(item, m_codepage);
2635 void uiTextEdit::handleKeyDown(uiKeyEventInfo
const & key)
2641 if (m_cursorCol != m_selCursorCol)
2643 else if (m_cursorCol > 0) {
2645 moveCursor(m_cursorCol - 1, m_cursorCol - 1);
2661 auto ASCII = keyToASCII(key);
2663 if (ASCII >= 0x20 && ASCII != 0x7F) {
2664 if (m_cursorCol != m_selCursorCol)
2682 int newCurCol = key.CTRL ? getWordPosAtLeft() : m_cursorCol - 1;
2683 moveCursor(newCurCol, (key.SHIFT ? m_selCursorCol : newCurCol));
2694 int newCurCol = key.CTRL ? getWordPosAtRight() : m_cursorCol + 1;
2695 moveCursor(newCurCol, (key.SHIFT ? m_selCursorCol : newCurCol));
2703 moveCursor(0, (key.SHIFT ? m_selCursorCol : 0));
2710 moveCursor(m_textLength, (key.SHIFT ? m_selCursorCol : m_textLength));
2720 moveCursor(m_textLength, 0);
2732 Rect uiTextEdit::getEditRect()
2738 void uiTextEdit::paintTextEdit()
2740 m_contentRect = getEditRect();
2752 uint8_t
const * uiTextEdit::getCharInfo(
char ch,
int *
width)
2756 uint8_t
const * chptr;
2757 if (m_textEditStyle.
textFont->chptr) {
2759 chptr = m_textEditStyle.
textFont->data + m_textEditStyle.
textFont->chptr[(int)(ch)];
2763 chptr = m_textEditStyle.
textFont->data + ch;
2770 void uiTextEdit::paintContent()
2772 m_contentRect = m_contentRect.shrink(2);
2773 canvas()->
setClippingRect(canvas()->getClippingRect().intersection(m_contentRect));
2776 GlyphOptions glyphOpt = GlyphOptions().FillBackground(
false).DoubleWidth(0).Bold(
false).Italic(
false).Underline(
false).Invert(0);
2777 if (m_selCursorCol != m_cursorCol)
2778 glyphOpt.FillBackground(
true);
2781 for (
int x = m_contentRect.
X1 + m_viewX, y = m_contentRect.
Y1, col = 0, fontWidth; m_text[col]; ++col, x += fontWidth) {
2782 uint8_t
const * chptr = getCharInfo(m_text[col], &fontWidth);
2783 if (m_selCursorCol != m_cursorCol && (col == m_selCursorCol || col == m_cursorCol)) {
2784 glyphOpt.invert = !glyphOpt.invert;
2787 if (x >= m_contentRect.
X1 && x <= m_contentRect.
X2)
2788 canvas()->
drawGlyph(x, y, fontWidth, m_textEditStyle.
textFont->height, chptr, 0);
2796 int uiTextEdit::charColumnToWindowX(
int col)
2798 int x = m_contentRect.
X1 + m_viewX;
2799 for (
int curcol = 0, fontWidth; m_text[curcol]; ++curcol, x += fontWidth) {
2800 getCharInfo(m_text[curcol], &fontWidth);
2809 void uiTextEdit::updateCaret()
2812 int x = charColumnToWindowX(m_cursorCol);
2813 app()->setCaret(Rect(x, m_contentRect.
Y1, x, m_contentRect.
Y1 + m_textEditStyle.
textFont->height));
2822 void uiTextEdit::moveCursor(
int col,
int selCol)
2824 col = iclamp(col, 0, m_textLength);
2825 selCol = iclamp(selCol, 0, m_textLength);
2827 if (col == m_cursorCol && selCol == m_selCursorCol)
2830 bool doRepaint =
false;
2833 if (m_cursorCol != m_selCursorCol && col == selCol)
2837 m_selCursorCol = selCol;
2839 if (m_cursorCol != m_selCursorCol)
2843 int x = charColumnToWindowX(m_cursorCol);
2845 int prevCharWidth = 0;
2847 getCharInfo(m_text[col - 1], &prevCharWidth);
2850 getCharInfo(m_text[col < m_textLength ? col : col - 1], &charWidth);
2852 if (x - prevCharWidth < m_contentRect.
X1) {
2854 m_viewX += m_contentRect.
X1 - (x - prevCharWidth);
2856 }
else if (x + charWidth > m_contentRect.
X2) {
2858 m_viewX -= (x + charWidth - m_contentRect.
X2);
2870 int uiTextEdit::getColFromMouseX(
int mouseX)
2873 for (
int x = m_contentRect.
X1 + m_viewX, fontWidth; m_text[col]; ++col, x += fontWidth) {
2874 getCharInfo(m_text[col], &fontWidth);
2875 if (mouseX < x || (mouseX >= x && mouseX < x + fontWidth))
2883 void uiTextEdit::checkAllocatedSpace(
int requiredLength)
2886 if (m_textSpace < requiredLength) {
2887 if (m_textSpace == 0) {
2889 m_textSpace = requiredLength;
2892 while (m_textSpace < requiredLength)
2895 m_text = (
char*) realloc(m_text, m_textSpace);
2901 void uiTextEdit::insert(
char c)
2904 checkAllocatedSpace(m_textLength);
2905 memmove(m_text + m_cursorCol + 1, m_text + m_cursorCol, m_textLength - m_cursorCol);
2906 m_text[m_cursorCol] = c;
2907 moveCursor(m_cursorCol + 1, m_cursorCol + 1);
2914 void uiTextEdit::removeSel()
2916 if (m_textLength > 0) {
2917 if (m_cursorCol > m_selCursorCol)
2918 iswap(m_cursorCol, m_selCursorCol);
2919 int count = imax(1, m_selCursorCol - m_cursorCol);
2920 if (m_cursorCol < m_textLength) {
2921 memmove(m_text + m_cursorCol, m_text + m_cursorCol + count, m_textLength - m_cursorCol - count + 1);
2922 m_textLength -= count;
2923 moveCursor(m_cursorCol, m_cursorCol);
2932 int uiTextEdit::getWordPosAtLeft()
2934 int col = m_cursorCol - 1;
2935 while (col > 0 && (!isspace(m_text[col - 1]) || isspace(m_text[col])))
2937 return imax(0, col);
2942 int uiTextEdit::getWordPosAtRight()
2944 int col = m_cursorCol + 1;
2945 while (col < m_textLength && (!isspace(m_text[col - 1]) || isspace(m_text[col])))
2947 return imin(m_textLength, col);
2953 void uiTextEdit::selectWordAt(
int mouseX)
2955 int col = getColFromMouseX(mouseX), left = col, right = col;
2956 bool lspc = isspace(m_text[col]);
2957 while (left > 0 && (
bool)isspace(m_text[left - 1]) == lspc)
2959 while (right < m_textLength && (
bool)isspace(m_text[right]) == lspc)
2961 moveCursor(left, right);
2977 :
uiControl(parent, pos, size, visible, 0),
3003 int len = strlen(value);
3004 m_text = (
char*) realloc(m_text, len + 1);
3005 strcpy(m_text, value);
3013 va_start(ap, format);
3014 int size = vsnprintf(
nullptr, 0, format, ap) + 1;
3017 va_start(ap, format);
3019 vsnprintf(buf,
size, format, ap);
3035 void uiLabel::paintLabel()
3056 int y = r.
Y1 + (r.height() - m_labelStyle.
textFont->height) / 2;
3061 void uiLabel::processEvent(uiEvent * event)
3063 uiControl::processEvent(event);
3065 switch (event->id) {
3090 :
uiControl(parent, pos, size, visible, 0),
3116 if (m_autoSize &&
bitmap)
3121 void uiImage::paintImage()
3127 int x = r.
X1 + (r.
X2 + 1 - m_bitmap->
width) / 2;
3128 int y = r.
Y1 + (r.
Y2 + 1 - m_bitmap->
height) / 2;
3134 void uiImage::processEvent(uiEvent * event)
3136 uiControl::processEvent(event);
3138 switch (event->id) {
3161 :
uiControl(parent, pos, size, visible, 0)
3179 void uiPanel::paintPanel()
3188 void uiPanel::processEvent(uiEvent * event)
3190 uiControl::processEvent(event);
3192 switch (event->id) {
3228 uiPaintBox::~uiPaintBox()
3233 void uiPaintBox::paintPaintBox()
3245 void uiPaintBox::processEvent(uiEvent * event)
3247 uiScrollableControl::processEvent(event);
3249 switch (event->id) {
3272 :
uiControl(parent, pos, size, visible, 0),
3286 uiColorBox::~uiColorBox()
3298 void uiColorBox::paintColorBox()
3307 void uiColorBox::processEvent(uiEvent * event)
3309 uiControl::processEvent(event);
3311 switch (event->id) {
3334 :
uiControl(parent, pos, size, visible, 0),
3335 m_HScrollBarPosition(0),
3336 m_HScrollBarVisible(0),
3337 m_HScrollBarRange(0),
3338 m_VScrollBarPosition(0),
3339 m_VScrollBarVisible(0),
3340 m_VScrollBarRange(0),
3341 m_mouseOverItem(uiScrollBarItem::
None),
3342 m_scrollTimer(nullptr)
3351 uiScrollableControl::~uiScrollableControl()
3363 position = iclamp(position, 0, range - visible);
3364 switch (orientation) {
3367 bool changedPos = (m_VScrollBarPosition != position);
3368 if (m_VScrollBarVisible != visible || m_VScrollBarRange != range || changedPos) {
3369 m_VScrollBarVisible = visible;
3370 m_VScrollBarRange = range;
3371 m_VScrollBarPosition = position;
3372 if (repaintScrollbar)
3373 repaintScrollBar(orientation);
3381 bool changedPos = (m_HScrollBarPosition != position);
3382 if (m_HScrollBarVisible != visible || m_HScrollBarRange != range || changedPos) {
3383 m_HScrollBarVisible = visible;
3384 m_HScrollBarRange = range;
3385 m_HScrollBarPosition = position;
3386 if (repaintScrollbar)
3387 repaintScrollBar(orientation);
3397 void uiScrollableControl::repaintScrollBar(
uiOrientation orientation)
3403 void uiScrollableControl::processEvent(uiEvent * event)
3405 uiControl::processEvent(event);
3407 switch (event->id) {
3411 paintScrollableControl();
3414 case UIEVT_MOUSEBUTTONDOWN:
3415 if (event->params.mouse.changedButton == 1) {
3416 m_mouseDownHScrollBarPosition = m_HScrollBarPosition;
3417 m_mouseDownVScrollBarPosition = m_VScrollBarPosition;
3418 if (m_mouseOverItem == uiScrollBarItem::LeftButton || m_mouseOverItem == uiScrollBarItem::RightButton ||
3419 m_mouseOverItem == uiScrollBarItem::TopButton || m_mouseOverItem == uiScrollBarItem::BottomButton) {
3422 handleButtonsScroll();
3426 app()->combineMouseMoveEvents(
true);
3430 case UIEVT_MOUSEBUTTONUP:
3431 if (event->params.mouse.changedButton == 1) {
3432 app()->combineMouseMoveEvents(
false);
3433 if (m_scrollTimer) {
3435 m_scrollTimer =
nullptr;
3440 case UIEVT_MOUSEMOVE:
3441 if (
app()->capturedMouseWindow() ==
this)
3442 handleCapturedMouseMove(event->params.mouse.status.X, event->params.mouse.status.Y);
3444 handleFreeMouseMove(event->params.mouse.status.X, event->params.mouse.status.Y);
3447 case UIEVT_MOUSEWHEEL:
3448 if (m_VScrollBarRange)
3453 if (event->params.timerHandle == m_scrollTimer)
3454 handleButtonsScroll();
3463 void uiScrollableControl::handleButtonsScroll()
3465 switch (m_mouseOverItem) {
3466 case uiScrollBarItem::LeftButton:
3469 case uiScrollBarItem::RightButton:
3472 case uiScrollBarItem::TopButton:
3475 case uiScrollBarItem::BottomButton:
3484 void uiScrollableControl::handlePageScroll()
3486 switch (m_mouseOverItem) {
3487 case uiScrollBarItem::PageLeft:
3490 case uiScrollBarItem::PageRight:
3493 case uiScrollBarItem::PageUp:
3496 case uiScrollBarItem::PageDown:
3505 void uiScrollableControl::handleFreeMouseMove(
int mouseX,
int mouseY)
3507 auto prev = m_mouseOverItem;
3508 m_mouseOverItem = getItemAt(mouseX, mouseY);
3509 if (m_mouseOverItem !=
prev) {
3510 if (m_VScrollBarRange)
3512 if (m_HScrollBarRange)
3518 void uiScrollableControl::handleCapturedMouseMove(
int mouseX,
int mouseY)
3520 if (m_mouseOverItem == uiScrollBarItem::HBar) {
3523 int newPos = m_mouseDownHScrollBarPosition + offset * m_HScrollBarRange / m_HBarArea;
3525 }
else if (m_mouseOverItem == uiScrollBarItem::VBar) {
3528 int newPos = m_mouseDownVScrollBarPosition + offset * m_VScrollBarRange / m_VBarArea;
3534 uiScrollBarItem uiScrollableControl::getItemAt(
int x,
int y)
3536 if (m_HScrollBarRange) {
3537 Rect lbtn, rbtn, bar;
3538 Rect box = getHScrollBarRects(&lbtn, &rbtn, &bar);
3539 if (lbtn.contains(x, y))
3540 return uiScrollBarItem::LeftButton;
3541 if (rbtn.contains(x, y))
3542 return uiScrollBarItem::RightButton;
3543 if (bar.contains(x, y))
3544 return uiScrollBarItem::HBar;
3545 if (box.contains(x, y))
3546 return x < bar.X1 ? uiScrollBarItem::PageLeft : uiScrollBarItem::PageRight;
3548 if (m_VScrollBarRange) {
3549 Rect tbtn, bbtn, bar;
3550 Rect box = getVScrollBarRects(&tbtn, &bbtn, &bar);
3551 if (tbtn.contains(x, y))
3552 return uiScrollBarItem::TopButton;
3553 if (bbtn.contains(x, y))
3554 return uiScrollBarItem::BottomButton;
3555 if (bar.contains(x, y))
3556 return uiScrollBarItem::VBar;
3557 if (box.contains(x, y))
3558 return y < bar.Y1 ? uiScrollBarItem::PageUp: uiScrollBarItem::PageDown;
3560 return uiScrollBarItem::None;
3564 Rect uiScrollableControl::getVScrollBarRects(Rect * topButton, Rect * bottomButton, Rect * bar)
3568 Rect box = Rect(cArea.X2 + 1 - sbSize, cArea.Y1, cArea.X2, cArea.Y2 - (m_HScrollBarRange ? sbSize : 0));
3569 if (topButton && bottomButton && bar) {
3571 *topButton = Rect(box.X1 + 2, box.Y1 + 2, box.X2 - 2, box.Y1 + sbSize - 2);
3572 *bottomButton = Rect(box.X1 + 2, box.Y2 - sbSize + 2, box.X2 - 2, box.Y2 - 2);
3574 int barAreaY1 = topButton->Y2 + 2;
3575 int barAreaY2 = bottomButton->Y1 - 2;
3576 m_VBarArea = barAreaY2 - barAreaY1 + 1;
3577 int barOffsetY = m_VScrollBarPosition * m_VBarArea / m_VScrollBarRange;
3578 int barHeight = m_VScrollBarVisible * m_VBarArea / m_VScrollBarRange;
3579 *bar = Rect(box.X1 + 1, barAreaY1 + barOffsetY, box.X2 - 1, barAreaY1 + barOffsetY + barHeight);
3585 Rect uiScrollableControl::getHScrollBarRects(Rect * leftButton, Rect * rightButton, Rect * bar)
3589 Rect box = Rect(cArea.X1, cArea.Y2 + 1 - sbSize, cArea.X2 - (m_VScrollBarRange ? sbSize : 0), cArea.Y2);
3590 if (leftButton && rightButton && bar) {
3592 *leftButton = Rect(box.X1 + 2, box.Y1 + 2, box.X1 + sbSize - 2, box.Y2 - 2);
3593 *rightButton = Rect(box.X2 - sbSize + 2, box.Y1 + 2, box.X2 - 2, box.Y2 - 2);
3595 int barAreaX1 = leftButton->X2 + 2;
3596 int barAreaX2 = rightButton->X1 - 2;
3597 m_HBarArea = barAreaX2 - barAreaX1 + 1;
3598 int barOffsetX = m_HScrollBarPosition * m_HBarArea / m_HScrollBarRange;
3599 int barWidth = m_HScrollBarVisible * m_HBarArea / m_HScrollBarRange;
3600 *bar = Rect(barAreaX1 + barOffsetX, box.Y1 + 1, barAreaX1 + barOffsetX + barWidth, box.Y2 - 1);
3606 void uiScrollableControl::paintScrollableControl()
3610 if (m_HScrollBarRange) {
3612 Rect lbtn, rbtn, bar;
3613 Rect box = getHScrollBarRects(&lbtn, &rbtn, &bar);
3617 canvas()->
fillRectangle(Rect(box.X1, box.Y1, bar.X1 - 1, box.Y2));
3618 canvas()->
fillRectangle(Rect(bar.X2 + 1, box.Y1, box.X2, box.Y2));
3619 canvas()->
drawLine(bar.X1, box.Y1, bar.X2, box.Y1);
3620 canvas()->
drawLine(bar.X1, box.Y2, bar.X2, box.Y2);
3622 canvas()->
setPenColor(m_mouseOverItem == uiScrollBarItem::LeftButton ? mouseOverFColor : FColor);
3623 canvas()->
drawLine(lbtn.X2, lbtn.Y1, lbtn.X1, lbtn.Y1 + lbtn.height() / 2);
3624 canvas()->
drawLine(lbtn.X1, lbtn.Y1 + lbtn.height() / 2, lbtn.X2, lbtn.Y2);
3625 canvas()->
setPenColor(m_mouseOverItem == uiScrollBarItem::RightButton ? mouseOverFColor : FColor);
3626 canvas()->
drawLine(rbtn.X1, rbtn.Y1, rbtn.X2, rbtn.Y1 + lbtn.height() / 2);
3627 canvas()->
drawLine(rbtn.X2, rbtn.Y1 + lbtn.height() / 2, rbtn.X1, rbtn.Y2);
3629 canvas()->
setBrushColor(m_mouseOverItem == uiScrollBarItem::HBar ? mouseOverFColor : FColor);
3632 if (m_VScrollBarRange) {
3634 Rect ubtn, bbtn, bar;
3635 Rect box = getVScrollBarRects(&ubtn, &bbtn, &bar);
3639 canvas()->
fillRectangle(Rect(box.X1, box.Y1, box.X2, bar.Y1 - 1));
3640 canvas()->
fillRectangle(Rect(box.X1, bar.Y2 + 1, box.X2, box.Y2));
3641 canvas()->
drawLine(box.X1, bar.Y1, box.X1, bar.Y2);
3642 canvas()->
drawLine(box.X2, bar.Y1, box.X2, bar.Y2);
3644 if (m_HScrollBarRange)
3647 canvas()->
setPenColor(m_mouseOverItem == uiScrollBarItem::TopButton ? mouseOverFColor : FColor);
3648 canvas()->
drawLine(ubtn.X1, ubtn.Y2, ubtn.X1 + ubtn.width() / 2, ubtn.Y1);
3649 canvas()->
drawLine(ubtn.X1 + ubtn.width() / 2, ubtn.Y1, ubtn.X2, ubtn.Y2);
3650 canvas()->
setPenColor(m_mouseOverItem == uiScrollBarItem::BottomButton ? mouseOverFColor : FColor);
3651 canvas()->
drawLine(bbtn.X1, bbtn.Y1, bbtn.X1 + bbtn.width() / 2, bbtn.Y2);
3652 canvas()->
drawLine(bbtn.X1 + bbtn.width() / 2, bbtn.Y2, bbtn.X2, bbtn.Y1);
3654 canvas()->
setBrushColor(m_mouseOverItem == uiScrollBarItem::VBar ? mouseOverFColor : FColor);
3663 r.
X2 -= (m_VScrollBarRange ? m_scrollableControlStyle.
scrollBarSize : 0);
3664 r.
Y2 -= (m_HScrollBarRange ? m_scrollableControlStyle.
scrollBarSize : 0);
3681 m_firstVisibleItem(0)
3695 uiCustomListBox::~uiCustomListBox()
3700 void uiCustomListBox::processEvent(uiEvent * event)
3702 uiScrollableControl::processEvent(event);
3704 switch (event->id) {
3711 case UIEVT_MOUSEBUTTONDOWN:
3712 if (event->params.mouse.changedButton == 1)
3713 mouseDownSelect(event->params.mouse.status.X, event->params.mouse.status.Y);
3716 case UIEVT_MOUSEMOVE:
3718 mouseMoveSelect(event->params.mouse.status.X, event->params.mouse.status.Y);
3722 handleKeyDown(event->params.key);
3729 case UIEVT_KILLFOCUS:
3743 void uiCustomListBox::handleKeyDown(uiKeyEventInfo key)
3745 bool shift = key.SHIFT;
3774 selectItem(items_getCount() - 1, shift, shift);
3785 if (items_getCount() > 0) {
3786 index = iclamp(index, 0, items_getCount() - 1);
3789 items_deselectAll();
3791 if (index <= first) {
3792 for (
int i = index; i <= first; ++i)
3793 items_select(i,
true);
3795 for (
int i = index; i >= first; --i)
3796 items_select(i,
true);
3799 items_select(index,
true);
3803 makeItemVisible(index);
3812 void uiCustomListBox::makeItemVisible(
int index)
3815 if (index < m_firstVisibleItem)
3816 m_firstVisibleItem = index;
3825 items_deselectAll();
3831 void uiCustomListBox::paintListBox()
3837 if (itmRect.height() * items_getCount() > cliRect.height()) {
3838 int visible = cliRect.height() / itmRect.height();
3839 int range = items_getCount();
3848 m_firstVisibleItem = 0;
3854 int index = m_firstVisibleItem;
3856 if (!itmRect.intersects(cliRect))
3861 if (index < items_getCount() && items_selected(index))
3866 if (index < items_getCount()) {
3869 items_draw(index, itmRect);
3873 itmRect = itmRect.translate(0, m_listBoxStyle.
itemHeight);
3883 for (
int i = 0; i < items_getCount(); ++i)
3884 if (items_selected(i))
3893 for (
int i = items_getCount() - 1; i >= 0; --i)
3894 if (items_selected(i))
3913 int uiCustomListBox::getItemAtMousePos(
int mouseX,
int mouseY)
3916 if (cliRect.contains(mouseX, mouseY)) {
3917 int idx = m_firstVisibleItem + (mouseY - cliRect.
Y1) / m_listBoxStyle.
itemHeight;
3918 return idx < items_getCount() ? idx : -1;
3924 void uiCustomListBox::mouseDownSelect(
int mouseX,
int mouseY)
3926 int idx = getItemAtMousePos(mouseX, mouseY);
3930 bool wasSelected = items_selected(idx);
3932 items_select(idx, !wasSelected);
3934 items_deselectAll();
3936 items_select(idx,
true);
3940 items_deselectAll();
3941 items_select(idx,
true);
3943 }
else if (idx == -1)
3944 items_deselectAll();
3952 void uiCustomListBox::mouseMoveSelect(
int mouseX,
int mouseY)
3954 int idx = getItemAtMousePos(mouseX, mouseY);
3955 if (idx >= 0 && !items_selected(idx)) {
3956 items_deselectAll();
3957 items_select(idx,
true);
3983 void uiListBox::items_draw(
int index,
const Rect & itemRect)
3985 int x = itemRect.
X1 + 1;
4002 m_selectedColor((
Color)0)
4013 void uiColorListBox::items_draw(
int index,
const Rect & itemRect)
4015 constexpr
int BORDER = 1;
4017 canvas()->
fillRectangle(itemRect.
X1 + BORDER, itemRect.
Y1 + BORDER, itemRect.
X2 - BORDER, itemRect.
Y2 - BORDER);
4041 void uiFileBrowser::items_draw(
int index,
const Rect & itemRect)
4043 int x = itemRect.
X1 + 1;
4047 static const char * DIRTXT =
"[dir]";
4054 void uiFileBrowser::items_select(
int index,
bool select)
4058 else if (index == m_selected || index == -1)
4066 m_selected = m_dir.
count() > 0 ? 0 : -1;
4074 m_selected = m_dir.
count() > 0 ? 0 : -1;
4081 return m_selected >= 0 ? m_dir.
get(m_selected)->
name :
nullptr;
4087 return m_selected >= 0 ? m_dir.
get(m_selected)->
isDir :
false;
4091 void uiFileBrowser::enterSubDir()
4093 if (m_selected >= 0) {
4094 auto selItem = m_dir.
get(m_selected);
4095 if (selItem->isDir) {
4108 m_selected = imin(m_dir.
count() - 1, m_selected);
4114 void uiFileBrowser::processEvent(uiEvent * event)
4116 uiCustomListBox::processEvent(event);
4118 switch (event->id) {
4133 case UIEVT_DBLCLICK:
4153 :
uiControl(parent, pos, size, visible, 0),
4154 m_listHeight(listHeight)
4167 uiCustomComboBox::~uiCustomComboBox()
4179 updateEditControl();
4183 void uiCustomComboBox::processEvent(uiEvent * event)
4185 uiControl::processEvent(event);
4187 switch (event->id) {
4194 updateEditControl();
4197 listbox()->
onKeyUp = [&](uiKeyEventInfo key) {
4211 case UIEVT_MOUSEBUTTONDOWN:
4212 if (event->params.mouse.changedButton == 1 && getButtonRect().contains(event->params.mouse.status.X, event->params.mouse.status.Y))
4216 case UIEVT_CHILDSETFOCUS:
4217 if (m_comboBoxProps.
openOnFocus && event->params.focusInfo.newFocused == editcontrol()
4218 && event->params.focusInfo.oldFocused != listbox()
4219 && event->params.focusInfo.oldFocused !=
this) {
4224 case UIEVT_SETFOCUS:
4225 if (event->params.focusInfo.oldFocused != listbox() && event->params.focusInfo.oldFocused != editcontrol()) {
4231 }
else if (event->params.focusInfo.oldFocused == listbox()) {
4236 case UIEVT_KILLFOCUS:
4237 if (event->params.focusInfo.newFocused != listbox()) {
4243 listbox()->processEvent(event);
4248 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))
4258 void uiCustomComboBox::openListBox()
4262 r.Y2 = r.Y1 + m_listHeight;
4270 void uiCustomComboBox::closeListBox()
4276 void uiCustomComboBox::switchListBox()
4278 if (listbox()->
state().visible) {
4287 Size uiCustomComboBox::getEditControlSize()
4290 return Size(clientArea.width() - buttonWidth(), clientArea.height());
4294 int uiCustomComboBox::buttonWidth()
4297 return clientArea.height() / 2;
4301 Rect uiCustomComboBox::getButtonRect()
4304 btnRect.X1 = btnRect.X2 - buttonWidth() + 1;
4309 void uiCustomComboBox::paintComboBox()
4311 Rect btnRect = getButtonRect();
4319 Rect arrowRect = btnRect.hShrink(1).vShrink(2);
4320 int hHeight = arrowRect.height() / 2;
4321 int hWidth = arrowRect.width() / 2;
4322 constexpr
int vDist = 2;
4323 canvas()->
drawLine(arrowRect.X1, arrowRect.Y1 + hHeight - vDist, arrowRect.X1 + hWidth, arrowRect.Y1);
4324 canvas()->
drawLine(arrowRect.X1 + hWidth, arrowRect.Y1, arrowRect.X2, arrowRect.Y1 + hHeight - vDist);
4325 canvas()->
drawLine(arrowRect.X1, arrowRect.Y1 + hHeight + vDist, arrowRect.X1 + hWidth, arrowRect.Y2);
4326 canvas()->
drawLine(arrowRect.X1 + hWidth, arrowRect.Y2, arrowRect.X2, arrowRect.Y1 + hHeight + vDist);
4341 m_textEdit(nullptr),
4357 uiComboBox::~uiComboBox()
4363 void uiComboBox::updateEditControl()
4382 m_colorBox(nullptr),
4383 m_colorListBox(nullptr)
4395 uiColorComboBox::~uiColorComboBox()
4401 void uiColorComboBox::updateEditControl()
4417 :
uiControl(parent, pos, size, visible, 0),
4436 uiCheckBox::~uiCheckBox()
4441 void uiCheckBox::paintCheckBox()
4456 canvas()->
drawLine(r.X1, r.Y2 - r.height() / 3, r.X1 + r.width() / 3, r.Y2);
4457 canvas()->
drawLine(r.X1 + r.width() / 3, r.Y2, r.X2, r.Y1);
4461 canvas()->
fillEllipse(r.X1 + r.width() / 2 - 1, r.Y1 + r.height() / 2 - 1, r.width(), r.height());
4468 void uiCheckBox::processEvent(uiEvent * event)
4470 uiControl::processEvent(event);
4472 switch (event->id) {
4483 case UIEVT_MOUSEENTER:
4487 case UIEVT_MOUSEBUTTONDOWN:
4488 if (event->params.mouse.changedButton == 1)
4492 case UIEVT_MOUSELEAVE:
4510 void uiCheckBox::trigger()
4514 m_checked = !m_checked;
4528 if (value != m_checked) {
4538 void uiCheckBox::unCheckGroup()
4540 if (m_groupIndex == -1)
4543 if (sibling !=
this &&
objectType().uiCheckBox) {
4545 if (chk->m_groupIndex == m_groupIndex)
4563 :
uiControl(parent, pos, size, visible, 0),
4564 m_orientation(orientation),
4568 m_ticksFrequency(25)
4583 uiSlider::~uiSlider()
4590 if (value != m_position) {
4591 m_position = iclamp(value, m_min, m_max);
4602 m_ticksFrequency = ticksFrequency;
4603 m_position = iclamp(m_position, m_min, m_max);
4607 void uiSlider::paintSlider()
4610 Rect slideRect = cRect.shrink(4);
4611 Rect gripRect = getGripRect();
4617 switch (m_orientation) {
4630 if (m_ticksFrequency > 0) {
4632 int range = m_max - m_min + 0;
4633 for (
int p = m_min; p <= m_max; p += m_ticksFrequency) {
4634 switch (m_orientation) {
4637 int x = slideRect.
X1 + slideRect.width() * (p - m_min) / range;
4643 int y = slideRect.
Y2 - slideRect.height() * (p - m_min) / range;
4656 Rect uiSlider::getGripRect()
4659 Rect slideRect = cRect.shrink(4);
4660 int range = m_max - m_min + 0;
4661 switch (m_orientation) {
4664 int x = slideRect.X1 + slideRect.width() * (m_position - m_min) / range;
4665 return Rect(x - 4, cRect.Y1, x + 4, cRect.Y2);
4669 int y = slideRect.Y2 - slideRect.height() * (m_position - m_min) / range;
4670 return Rect(cRect.X1, y - 4, cRect.X2, y + 4);
4678 void uiSlider::moveGripTo(
int x,
int y)
4681 Rect slideRect = cRect.shrink(4);
4682 int range = m_max - m_min + 1;
4683 switch (m_orientation) {
4685 setPosition(m_min + (x - slideRect.X1) * range / slideRect.width());
4688 setPosition(m_min + (slideRect.Y2 - y) * range / slideRect.height());
4694 void uiSlider::processEvent(uiEvent * event)
4696 uiControl::processEvent(event);
4698 switch (event->id) {
4705 case UIEVT_MOUSEBUTTONDOWN:
4706 moveGripTo(event->params.mouse.status.X, event->params.mouse.status.Y);
4709 case UIEVT_MOUSEMOVE:
4710 if (
app()->capturedMouseWindow() ==
this)
4711 moveGripTo(event->params.mouse.status.X, event->params.mouse.status.Y);
4715 handleKeyDown(event->params.key);
4724 void uiSlider::handleKeyDown(uiKeyEventInfo key)
4779 :
uiControl(parent, pos, size, visible, 0)
4794 uiProgressBar::~uiProgressBar()
4799 void uiProgressBar::paintProgressBar()
4803 int splitPos = cRect.width() * m_percentage / 100;
4804 Rect fRect = Rect(cRect.X1, cRect.Y1, cRect.X1 + splitPos, cRect.Y2);
4805 Rect bRect = Rect(cRect.X1 + splitPos + 1, cRect.Y1, cRect.X2, cRect.Y2);
4815 sprintf(txt,
"%d%%", m_percentage);
4819 int y = cRect.Y1 + (cRect.height() - m_progressBarStyle.
textFont->height) / 2;
4825 void uiProgressBar::processEvent(uiEvent * event)
4827 uiControl::processEvent(event);
4829 switch (event->id) {
4844 value = imin(imax(0, value), 100);
4845 if (value != m_percentage) {
4846 m_percentage = value;
int textExtent(FontInfo const *fontInfo, char const *text)
Calculates text extension in pixels.
Represents a 24 bit RGB color.
uiWindow * setActiveWindow(uiWindow *value)
Sets the active window.
uiWindowStyle & windowStyle()
Sets or gets window style.
void selectItem(int index, bool add=false, bool range=false)
Selects a listbox item.
GlyphOptions & Bold(bool value)
Helper method to set or reset bold.
Delegate onChange
Change event delegate.
int selectedItem()
Represents currently selected item.
A class with a set of drawing methods.
Delegate onDblClick
Mouse double click event delegate.
Shows a list of 16 colors, one selectable.
A frame is a window with a title bar, maximize/minimize/close buttons and that is resizeable or movea...
int lastSelectedItem()
Gets the last selected item.
Contains details about the key event.
Rect rect(uiOrigin origin)
Determines the window bounding box.
uiTextEdit(uiWindow *parent, char const *text, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
uiOrientation
Item direction/orientation.
void setScrollBar(uiOrientation orientation, int position, int visible, int range, bool repaintScrollbar)
Sets scrollbar position, visible portion and range.
int firstSelectedItem()
Gets the first selected item.
Represents the whole application base class.
This file contains all classes related to FabGL Graphical User Interface.
This is a combination of a listbox and another component, base of all combobox components.
uiStyle * style()
Gets current application controls style.
void setText(char const *value)
Sets label text.
char const * text()
Gets current content of the text edit.
void selectItem(int index)
Selects an item.
Base class for all visible UI elements (Frames and Controls)
void update()
Reloads current directory content and repaints.
void setup(int min, int max, int ticksFrequency)
Sets minimum, maximum position and ticks frequency.
A color box is a control that shows a single color.
void setParentProcessKbdEvents(bool value)
Enables a child window to send keyboard events to its parent.
GlyphOptions & Italic(bool value)
Helper method to set or reset italic.
uiColorBox(uiWindow *parent, const Point &pos, const Size &size, Color color=Color::BrightWhite, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
DirItem const * get(int index)
Gets file/directory at index.
uiColorComboBox(uiWindow *parent, const Point &pos, const Size &size, int listHeight, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
FontInfo const * textFont
Shows a list of selectable string items.
This file contains fabgl::Keyboard definition.
Bitmap const * bitmap()
Gets image bitmap.
void bringAfter(uiWindow *insertionPoint)
Brings this window after another one.
Size size()
Determines the window size.
RGB888 mouseOverBackgroundButtonColor
uiWindow * parent()
Determines the parent window.
void resetGlyphOptions()
Resets glyph options.
uiImage(uiWindow *parent, Bitmap const *bitmap, const Point &pos, const Size &size=Size(0, 0), bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
Base class of all UI elements that can receive events.
Color
This enum defines named colors.
uiLabel(uiWindow *parent, char const *text, const Point &pos, const Size &size=Size(0, 0), bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
uiColorListBox(uiWindow *parent, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
bool setDirectory(const char *path)
Sets absolute directory path.
The PS2 Keyboard controller class.
Represents the base abstract class for bitmapped display controllers.
Delegate onClick
Mouse click event delegate.
This file contains fabgl::Canvas definition.
Size clientSize()
Determines the client area size.
uiWindow * lastChild()
Gets last child.
A panel is used to contain and to group some controls.
Represents a checkbox or a radiobutton.
void setPaintOptions(PaintOptions options)
Sets paint options.
This file contains fabgl::Mouse definition.
Delegate onResize
Resize window event delegate.
uiCheckBoxKind
Specifies the combobox behaviour.
void setTitleFmt(const char *format,...)
Sets window title as formatted text.
void fillEllipse(int X, int Y, int width, int height)
Fills an ellipse specifying center and size, using current brush color.
void update()
Updates the label content.
void setColor(Color value)
Sets current colorbox color.
uiWindowProps & windowProps()
Sets or gets window properties.
void setTextFmt(const char *format,...)
Sets label formatted text.
uiCustomComboBox(uiWindow *parent, const Point &pos, const Size &size, int listHeight, bool visible, uint32_t styleClassID)
Creates an instance of the object.
Point mouseDownPos()
Determines mouse position when left button was down.
void setBrushColor(uint8_t red, uint8_t green, uint8_t blue)
Sets brush (background) color specifying color components.
void setClippingRect(Rect const &rect)
Sets clipping rectangle relative to the origin.
RGB888 focusedSelectedBackgroundColor
uiButtonKind
Specifies the button kind.
Delegate< Rect > onPaint
Paint event delegate.
uiWindow * parentFrame()
Determines the parent frame.
void deselectAll()
Deselects all selected items.
Delegate onChange
Change event delegate.
uiCustomListBox(uiWindow *parent, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
RGB888 buttonBackgroundColor
Represents the coordinate of a point.
void setPosition(int value)
Sets the slider position.
This file contains some utility classes and functions.
Delegate< uiTimerHandle > onTimer
Timer event delegate.
RGB888 mouseOverBackgroundColor
uiSlider(uiWindow *parent, const Point &pos, const Size &size, uiOrientation orientation, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
void fillRectangle(int X1, int Y1, int X2, int Y2)
Fills a rectangle using the current brush color.
uiFrame(uiWindow *parent, char const *title, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
uiControl(uiWindow *parent, const Point &pos, const Size &size, bool visible, uint32_t styleClassID=0)
Creates an instance of the object.
uiOrigin
Specifies window rectangle origin.
Rect transformRect(Rect const &rect, uiWindow *baseWindow)
Transforms rectangle origins from current window to another one.
GlyphOptions & Underline(bool value)
Helper method to set or reset underlined.
uiPaintBox(uiWindow *parent, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
void drawGlyph(int X, int Y, int width, int height, uint8_t const *data, int index=0)
Draws a glyph at specified position.
char const * filename()
Currently selected filename.
uiMessageBoxResult
Return values from uiApp.messageBox() method.
Specifies various glyph painting options.
uint8_t focusedBorderSize
void maximizeWindow(uiWindow *window, bool value)
Maximizes or restores a window.
void drawTextWithEllipsis(FontInfo const *fontInfo, int X, int Y, char const *text, int maxX)
Draws a string at specified position. Add ellipses before truncation.
Represents a text edit control.
void minimizeWindow(uiWindow *window, bool value)
Minimizes or restores a window.
uiWindow * prev()
Gets previous sibling.
uiTextEditProps & textEditProps()
Sets or gets text edit properties.
RGB888 mouseOverButtonColor
CursorName
This enum defines a set of predefined mouse cursors.
int focusIndex()
Determines the focus index (aka tab-index)
RGB888 mouseOverBackgroundColor
FontInfo const * textFont
uiWindow * firstChild()
Gets first child.
void setPercentage(int value)
Sets percentage.
void setMouseCursor(Cursor *cursor)
Sets mouse cursor and make it visible.
uint8_t hasMaximizeButton
GlyphOptions & DoubleWidth(uint8_t value)
Helper method to set or reset doubleWidth.
void reshapeWindow(uiWindow *window, Rect const &rect)
Reshapes a window.
FontInfo const * titleFont
StringList & items()
A list of strings representing items of the combobox.
RGB888 activeTitleBackgroundColor
uiWindow * setFocusedWindow(uiWindow *value)
Sets the focused window (control)
void setBitmap(Bitmap const *bitmap)
Sets image bitmap.
Point pos()
Determines the window position relative to parent window.
virtual Rect clientRect(uiOrigin origin)
Determines the client area bounding box.
void setChecked(bool value)
Sets current checkbox or radiobutton checked status.
Represents a bidimensional size.
uiCheckBox(uiWindow *parent, const Point &pos, const Size &size, uiCheckBoxKind kind=uiCheckBoxKind::CheckBox, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
Delegate onKillFocus
Kill focus event delegate.
uiObjectType & objectType()
Determines the object type.
bool hasChildren()
Determines whether this window has children.
Delegate< uiKeyEventInfo > onKeyUp
Key-up event delegate.
RGB888 focusedBorderColor
Delegate onShow
Show window event delegate.
bool isDirectory()
Determines whether currently selected item is a directory.
uiTimerHandle setTimer(uiEvtHandler *dest, int periodMS)
Setups a timer.
int max()
Gets maximum position.
uiApp * app()
Determines the app that owns this object.
FontInfo const * textFont
void setGlyphOptions(GlyphOptions options)
Sets drawing options for the next glyphs.
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...
uint32_t styleClassID()
Determines current style class for this UI element.
void drawLine(int X1, int Y1, int X2, int Y2)
Draws a line specifying initial and ending coordinates.
A label is a static text UI element.
uiProgressBar(uiWindow *parent, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
uiComboBox(uiWindow *parent, const Point &pos, const Size &size, int listHeight, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
uiListBox(uiWindow *parent, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
void bringOnTop()
Brings this window on top.
uiWindowState state()
Determines the window state.
uiPanel(uiWindow *parent, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
Rect clientRect(uiOrigin origin)
Determines the client area bounding box.
uiWindow * moveFocus(int delta)
Move focus to a control with current focus index plus a delta.
RGB888 checkedBackgroundColor
void repaintRect(Rect const &rect)
Repaints a screen area.
This is the base class for all controls. A control can have focus and is not activable.
RGB888 titleBackgroundColor
bool hasFocus()
Determines whether this window or control has focus.
uint8_t selectOnMouseOver
void setPenColor(uint8_t red, uint8_t green, uint8_t blue)
Sets pen (foreground) color specifying color components.
void repaint()
Repaints this window.
uiFileBrowser(uiWindow *parent, const Point &pos, const Size &size, bool visible=true, uint32_t styleClassID=0)
Creates an instance of the object.
void repaint(Rect const &rect)
Repaints a rectangle of this window.
char const * title()
Determines the window title.
GlyphOptions & FillBackground(bool value)
Helper method to set or reset fillBackground.
RGB888 selectedBackgroundColor
Delegate onPaint
Paint event delegate.
RGB888 focusedBackgroundColor
Shows generic a list of selectable items.
uiWindow * next()
Gets next sibling.
void drawText(int X, int Y, char const *text, bool wrap=false)
Draws a string at specified position.
Delegate onHide
Hide window event delegate.
void setDirectory(char const *path)
Sets current directory as absolute path.
uiListBoxStyle & listBoxStyle()
Sets or gets listbox style.
Delegate onChange
Change event delegate.
void changeDirectory(const char *subdir)
Sets relative directory path.
Delegate< uiKeyEventInfo > onKeyUp
Key-up event delegate.
void setText(char const *value)
Replaces current text.
Delegate< uiKeyEventInfo > onKeyDown
Key-down event delegate.
Point clientPos()
Determines position of the client area.
uiWindow(uiWindow *parent, const Point &pos, const Size &size, bool visible, uint32_t styleClassID=0)
Creates an instance of the object.
GlyphOptions & Invert(uint8_t value)
Helper method to set or reset foreground and background swapping.
uiFrame * rootWindow()
Gets a pointer to the root window.
The PS2 Mouse controller class.
void setOrigin(int X, int Y)
Sets the axes origin.
void moveTo(int X, int Y)
Moves current pen position to the spcified coordinates.
int count()
Determines number of files in current directory.
bool reload()
Reloads directory content.
Delegate onChange
Text edit event delegate.
void resetPaintOptions()
Resets paint options.
bool isMouseOver()
Determines whether the mouse is over this window.
void showWindow(uiWindow *window, bool value)
Makes a window visible or invisible.
uint8_t hasMinimizeButton
RGB888 focusedBackgroundColor
void exitModal(int modalResult)
Exits from a modal window.
uiWindow * focusedWindow()
Gets the focused window (control)
void changeDirectory(char const *path)
Changes current directory as relative path.
void drawRectangle(int X1, int Y1, int X2, int Y2)
Draws a rectangle using the current pen color.
char const * text()
Determines label text.
void lineTo(int X, int Y)
Draws a line starting from current pen position.
void setTitle(char const *value)
Sets window title.
uiMessageBoxIcon
Icon displayed by the uiApp.messageBox() method.
FontInfo const * textFont
int min()
Gets minimum position.
Image control to display a static bitmap.
void killTimer(uiTimerHandle handle)
Kills a timer.
void drawBitmap(int X, int Y, Bitmap const *bitmap)
Draws a bitmap at specified position.
Delegate onChange
Slider changed event delegate.