From 324622e11ea890c8582a9ebf14307872e89af158 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 21 Oct 2021 05:11:19 -0400 Subject: [PATCH 1/3] fix touchend and slide processing to make it consistent. --- radio/src/thirdparty/libopenui/mainwindow.cpp | 141 +++++ radio/src/thirdparty/libopenui/window.cpp | 564 ++++++++++++++++++ radio/src/thirdparty/libopenui/window.h | 451 ++++++++++++++ 3 files changed, 1156 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/mainwindow.cpp create mode 100644 radio/src/thirdparty/libopenui/window.cpp create mode 100644 radio/src/thirdparty/libopenui/window.h diff --git a/radio/src/thirdparty/libopenui/mainwindow.cpp b/radio/src/thirdparty/libopenui/mainwindow.cpp new file mode 100644 index 000000000..de96d7fa9 --- /dev/null +++ b/radio/src/thirdparty/libopenui/mainwindow.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "mainwindow.h" +#include "keyboard_base.h" + +#if defined(HARDWARE_TOUCH) +#include "touch.h" +#endif + +MainWindow * MainWindow::_instance = nullptr; + +#if defined(HARDWARE_TOUCH) +TouchState touchState; +Keyboard * Keyboard::activeKeyboard = nullptr; +#endif + +void MainWindow::emptyTrash() +{ + for (auto window: trash) { + delete window; + } + trash.clear(); +} + +void MainWindow::checkEvents() +{ +#if defined(HARDWARE_TOUCH) + if (touchPanelEventOccured()) { + touchPanelRead(); + } + + if (touchState.event == TE_DOWN) { + onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); + slidingWindow = nullptr; + } + else if (touchState.event == TE_UP) { + touchState.event = TE_NONE; + onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); + } + else if (touchState.event == TE_SLIDE) { + if (touchState.deltaX || touchState.deltaY) { + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); + touchState.lastDeltaX = touchState.deltaX; + touchState.lastDeltaY = touchState.deltaY; + touchState.deltaX = 0; + touchState.deltaY = 0; + } + } + else if (touchState.event == TE_SLIDE_END && slidingWindow) { + if (touchState.lastDeltaX > SLIDE_SPEED_REDUCTION) + touchState.lastDeltaX -= SLIDE_SPEED_REDUCTION; + else if (touchState.lastDeltaX < -SLIDE_SPEED_REDUCTION) + touchState.lastDeltaX += SLIDE_SPEED_REDUCTION; + else + touchState.lastDeltaX = 0; + if (touchState.lastDeltaY > SLIDE_SPEED_REDUCTION) + touchState.lastDeltaY -= SLIDE_SPEED_REDUCTION; + else if (touchState.lastDeltaY < -SLIDE_SPEED_REDUCTION) + touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; + else + touchState.lastDeltaY = 0; + + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); + slidingWindow = nullptr; + } +#endif + + Window::checkEvents(); +} + +void MainWindow::invalidate(const rect_t & rect) +{ + if (invalidatedRect.w) { + auto left = limit(0, rect.left(), invalidatedRect.left()); + auto right = limit(invalidatedRect.right(), rect.right(), LCD_W); + auto top = limit(0, rect.top(), invalidatedRect.top()); + auto bottom = limit(rect.bottom(), invalidatedRect.bottom(), LCD_H); + invalidatedRect = {left, top, right - left, bottom - top}; + } + else { + invalidatedRect = rect; + } +} + +bool MainWindow::refresh() +{ + if (invalidatedRect.w) { + if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { + TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); + lcdCopy(lcd->getData(), lcdFront->getData()); + } + else { + TRACE_WINDOWS("Refresh full screen"); + } + lcd->setOffset(0, 0); + lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); + fullPaint(lcd); + invalidatedRect.w = 0; + return true; + } + else { + return false; + } +} + +void MainWindow::run(bool trash) +{ + auto start = ticksNow(); + + checkEvents(); + + if (trash) { + emptyTrash(); + } + + if (refresh()) { + lcdRefresh(); + } + + auto delta = ticksNow() - start; + if (delta > 10 * SYSTEM_TICKS_1MS) { + TRACE_WINDOWS("MainWindow::run took %dms", (ticksNow() - start) / SYSTEM_TICKS_1MS); + } +} diff --git a/radio/src/thirdparty/libopenui/window.cpp b/radio/src/thirdparty/libopenui/window.cpp new file mode 100644 index 000000000..46f5d4ce4 --- /dev/null +++ b/radio/src/thirdparty/libopenui/window.cpp @@ -0,0 +1,564 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "window.h" +#include "touch.h" + +Window * Window::focusWindow = nullptr; +Window * Window::slidingWindow = nullptr; +Window * Window::touchedWindow = nullptr; +std::list Window::trash; + +Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): + parent(parent), + rect(rect), + innerWidth(rect.w), + innerHeight(rect.h), + windowFlags(windowFlags), + textFlags(textFlags) +{ + if (parent) { + parent->addChild(this, windowFlags & PUSH_FRONT); + if (!(windowFlags & TRANSPARENT)) { + invalidate(); + } + } +} + +Window::~Window() +{ + TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); + + if (focusWindow == this) { + focusWindow = nullptr; + } + + deleteChildren(); +} + +void Window::attach(Window * newParent) +{ + if (parent) detach(); + parent = newParent; + if (newParent) newParent->addChild(this); +} + +void Window::detach() +{ + if (parent) { + parent->removeChild(this); + parent = nullptr; + } +} + +void Window::deleteLater(bool detach, bool trash) +{ + if (_deleted) + return; + + TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); + + _deleted = true; + + if (static_cast(focusWindow) == static_cast(this)) { + focusWindow = nullptr; + } + + if (detach) + this->detach(); + else + parent = nullptr; + + deleteChildren(); + + if (closeHandler) { + closeHandler(); + } + + if (trash) { + Window::trash.push_back(this); + } +} + +void Window::clear() +{ + scrollPositionX = 0; + scrollPositionY = 0; + innerWidth = rect.w; + innerHeight = rect.h; + deleteChildren(); + invalidate(); +} + +void Window::deleteChildren() +{ + for (auto window: children) { + window->deleteLater(false); + } + children.clear(); +} + +void Window::clearFocus() +{ + if (focusWindow) { + focusWindow->onFocusLost(); + focusWindow = nullptr; + } +} + +void Window::setFocus(uint8_t flag, Window * from) +{ + if (deleted()) + return; + + TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); + + if (focusWindow != this) { + // scroll before calling focusHandler so that the window can adjust the scroll position if needed + Window * parent = this->parent; + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + if (parent) { + parent->scrollTo(this); + invalidate(); + } + + clearFocus(); + focusWindow = this; + if (focusHandler) { + focusHandler(true); + } + } +} + +void Window::setScrollPositionX(coord_t value) +{ + auto newScrollPosition = max(0, min(innerWidth - width(), value)); + if (newScrollPosition != scrollPositionX) { + scrollPositionX = newScrollPosition; + invalidate(); + } +} + +void Window::setScrollPositionY(coord_t value) +{ + auto newScrollPosition = min(innerHeight - height(), value); + + if (newScrollPosition < 0 && innerHeight != INFINITE_HEIGHT) { + newScrollPosition = 0; + } + + if (newScrollPosition != scrollPositionY) { + scrollPositionY = newScrollPosition; + invalidate(); + } +} + +void Window::scrollTo(Window * child) +{ + coord_t offsetX = 0; + coord_t offsetY = 0; + + Window * parentWindow = child->getParent(); + while (parentWindow && parentWindow != this) { + offsetX += parentWindow->left(); + offsetY += parentWindow->top(); + parentWindow = parentWindow->getParent(); + } + + const rect_t scrollRect = { + offsetX + child->left(), + offsetY + child->top(), + min(child->width(), width()), + min(child->height(), height()) + }; + + scrollTo(scrollRect); +} + +void Window::scrollTo(const rect_t & rect) +{ + if (rect.top() < scrollPositionY) { + setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.top() - 5); + } + else if (rect.bottom() > scrollPositionY + height() - 5) { + setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.bottom() - height() + 5); + } + + if (rect.left() < scrollPositionX) { + setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.left() - 5); + } + else if (rect.right() > scrollPositionX + width() - 5) { + setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.right() - width() + 5); + } +} + +bool Window::hasOpaqueRect(const rect_t & testRect) const +{ + if (!rect.contains(testRect)) + return false; + + if (windowFlags & OPAQUE) { + return true; + } + + rect_t relativeRect = {testRect.x - rect.x, testRect.y - rect.y, testRect.w, testRect.h}; + for (auto child: children) { + if (child->hasOpaqueRect(relativeRect)) + return true; + } + + return false; +} + +void Window::fullPaint(BitmapBuffer * dc) +{ + bool paintNeeded = true; + std::list::iterator firstChild; + + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + + if (windowFlags & PAINT_CHILDREN_FIRST) { + paintChildren(dc, children.begin()); + dc->setOffset(x, y); + dc->setClippingRect(xmin, xmax, ymin, ymax); + } + else { + firstChild = children.end(); + rect_t relativeRect = {xmin - x, ymin - y, xmax - xmin, ymax - ymin}; + while (firstChild != children.begin()) { + auto child = *(--firstChild); + if (child->hasOpaqueRect(relativeRect)) { + paintNeeded = false; + break; + } + } + } + + if (paintNeeded) { + TRACE_WINDOWS_INDENT("%s%s", getWindowDebugString().c_str(), hasFocus() ? " (*)" : ""); + paint(dc); +#if defined(WINDOWS_INSPECT_BORDER_COLOR) + dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR); +#endif + } + else { + TRACE_WINDOWS_INDENT("%s (skipped)", getWindowDebugString().c_str()); + } + + if (!(windowFlags & NO_SCROLLBAR)) { + drawVerticalScrollbar(dc); + } + + if (!(windowFlags & PAINT_CHILDREN_FIRST)) { + paintChildren(dc, firstChild); + } +} + +bool Window::isChildFullSize(const Window * child) const +{ + return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width(); +} + +bool Window::isChildVisible(const Window * window) const +{ + for (auto rit = children.rbegin(); rit != children.rend(); rit++) { + auto child = *rit; + if (child == window) { + return true; + } + if ((child->getWindowFlags() & OPAQUE) & isChildFullSize(child)) { + return false; + } + } + return false; +} + +void Window::setInsideParentScrollingArea() +{ + Window * parent = getParent(); + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + if (parent) { + parent->scrollTo(this); + invalidate(); + } +} + +void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) +{ + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + + for (; it != children.end(); it++) { + auto child = *it; + + coord_t child_xmin = x + child->rect.x; + if (child_xmin >= xmax) + continue; + coord_t child_ymin = y + child->rect.y; + if (child_ymin >= ymax) + continue; + coord_t child_xmax = child_xmin + child->rect.w; + if (child_xmax <= xmin) + continue; + coord_t child_ymax = child_ymin + child->rect.h; + if (child_ymax <= ymin) + continue; + + dc->setOffset(x + child->rect.x - child->scrollPositionX, y + child->rect.y - child->scrollPositionY); + dc->setClippingRect(max(xmin, x + child->rect.left()), + min(xmax, x + child->rect.right()), + max(ymin, y + child->rect.top()), + min(ymax, y + child->rect.bottom())); + child->fullPaint(dc); + } +} + +#if defined(HARDWARE_TOUCH) +coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) +{ + coord_t result = 0; + if (relativeScrollPosition > pageSize / 2) { + // closer to next page + result = (pageSize - relativeScrollPosition); + } + else { + // closer to previous page + result = (0 - relativeScrollPosition); + } + + // do not get too slow + if (abs(result) > 32) + result /= 2; + + return result; +} +#endif + +void Window::checkEvents() +{ + auto copy = children; + for (auto child: copy) { + if (!child->deleted()) { + child->checkEvents(); + } + } + + if (this == Window::focusWindow) { + event_t event = getWindowEvent(); + if (event) { + this->onEvent(event); + } + } + + if (windowFlags & REFRESH_ALWAYS) { + invalidate(); + } + +#if defined(HARDWARE_TOUCH) + if (touchState.event != TE_SLIDE && touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) { + if (pageWidth) { + coord_t relativeScrollPosition = getScrollPositionX() % pageWidth; + if (relativeScrollPosition) { + setScrollPositionX(getScrollPositionX() + getSnapStep(relativeScrollPosition, pageWidth)); + } + } + if (pageHeight) { + coord_t relativeScrollPosition = getScrollPositionY() % pageHeight; + if (relativeScrollPosition) { + setScrollPositionY(getScrollPositionY() + getSnapStep(relativeScrollPosition, pageHeight)); + } + } + } +#endif +} + +void Window::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString("Window").c_str(), event); + if (parent) { + parent->onEvent(event); + } +} + +#if defined(HARDWARE_TOUCH) +bool Window::onTouchStart(coord_t x, coord_t y) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (child->rect.contains(x, y)) { + if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + // remember the first window that processed the touch start event. We will give that window + // the opportunity to process the touchend first, and also after a slide end so the touched window + // always gets a touchEnd if it processed the touch start. + if (touchedWindow == nullptr) { + touchedWindow = child; + } + return true; + } + } + } + + return false; +} + +bool Window::forwardTouchEnd(coord_t x, coord_t y) +{ + // give the touched window the opportunity to process the touch first before it is + // forwarded: See on touchstart + if (touchedWindow != nullptr) { + auto savedTouchWindow = touchedWindow; + touchedWindow = nullptr; + // transalte x and y through the parent hierarchy + auto myParent = savedTouchWindow->parent; + while(myParent != nullptr) { + x = x - myParent->rect.x + myParent->scrollPositionX; + y = y - myParent->rect.y + myParent->scrollPositionY; + myParent = myParent->parent; + } + + if (savedTouchWindow->onTouchEnd( + x - savedTouchWindow->rect.x + savedTouchWindow->scrollPositionX, + y - savedTouchWindow->rect.y + savedTouchWindow->scrollPositionY)) { + return true; + } + } + + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (child->rect.contains(x, y)) { + if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + return true; + } + } + } + + return false; +} + +bool Window::onTouchEnd(coord_t x, coord_t y) +{ + return forwardTouchEnd(x, y) ? true : (windowFlags & OPAQUE); +} + +bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + startX += getScrollPositionX(); + startY += getScrollPositionY(); + + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (child->rect.contains(startX, startY)) { + if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { + return true; + } + } + } + + if (slidingWindow && slidingWindow != this) { + return false; + } + + if (slideY && innerHeight > rect.h) { + setScrollPositionY(scrollPositionY - slideY); + slidingWindow = this; + return true; + } + + if (slideX && innerWidth > rect.w) { + setScrollPositionX(scrollPositionX - slideX); + slidingWindow = this; + return true; + } + + return false; +} +#endif + +void Window::adjustInnerHeight() +{ + coord_t bottomMax = 0; + for (auto child: children) { + bottomMax = max(bottomMax, child->rect.y + child->rect.h); + } + setInnerHeight(bottomMax); +} + +coord_t Window::adjustHeight() +{ + coord_t old = rect.h; + adjustInnerHeight(); + rect.h = innerHeight; + return rect.h - old; +} + +void Window::moveWindowsTop(coord_t y, coord_t delta) +{ + if (getWindowFlags() & FORWARD_SCROLL) { + parent->moveWindowsTop(bottom(), delta); + } + + for (auto child: children) { + if (child->rect.y >= y) { + child->rect.y += delta; + invalidate(); + } + } +} + +void Window::invalidate(const rect_t & rect) +{ + if (isVisible()) { + parent->invalidate({this->rect.x + rect.x - parent->scrollPositionX, this->rect.y + rect.y - parent->scrollPositionY, rect.w, rect.h}); + } +} + +void Window::drawVerticalScrollbar(BitmapBuffer * dc) +{ + if (innerHeight > rect.h) { + coord_t yofs = divRoundClosest(rect.h * scrollPositionY, innerHeight); + coord_t yhgt = divRoundClosest(rect.h * rect.h, innerHeight); + if (yhgt < 15) + yhgt = 15; + if (yhgt + yofs > rect.h) + yhgt = rect.h - yofs; + dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, COLOR_THEME_PRIMARY3); + } +} + +void Window::drawHorizontalScrollbar(BitmapBuffer * dc) +{ + if (innerWidth > rect.w) { + coord_t xofs = divRoundClosest(rect.w * scrollPositionX, innerWidth); + coord_t xwdth = divRoundClosest(rect.w * rect.w, innerWidth); + if (xwdth < 15) + xwdth = 15; + if (xwdth + xofs > rect.w) + xwdth = rect.w - xofs; + dc->drawSolidFilledRect(scrollPositionX + xofs, rect.h - SCROLLBAR_WIDTH, xwdth, SCROLLBAR_WIDTH, COLOR_THEME_PRIMARY3); + } +} diff --git a/radio/src/thirdparty/libopenui/window.h b/radio/src/thirdparty/libopenui/window.h new file mode 100644 index 000000000..2b9b045e6 --- /dev/null +++ b/radio/src/thirdparty/libopenui/window.h @@ -0,0 +1,451 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "bitmapbuffer.h" +#include "libopenui_defines.h" +#include "libopenui_helpers.h" +#include "libopenui_config.h" + +typedef uint32_t WindowFlags; + +#if !defined(_GNUC_) + #undef OPAQUE + #undef TRANSPARENT +#endif + +constexpr int INFINITE_HEIGHT = INT32_MAX; + +constexpr WindowFlags OPAQUE = 1u << 0u; +constexpr WindowFlags TRANSPARENT = 1u << 1u; +constexpr WindowFlags NO_SCROLLBAR = 1u << 2u; +constexpr WindowFlags NO_FOCUS = 1u << 3u; +constexpr WindowFlags FORWARD_SCROLL = 1u << 4u; +constexpr WindowFlags REFRESH_ALWAYS = 1u << 5u; +constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6u; +constexpr WindowFlags PUSH_FRONT = 1u << 7u; +constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; + +enum SetFocusFlag +{ + SET_FOCUS_DEFAULT, + SET_FOCUS_FORWARD, + SET_FOCUS_BACKWARD, + SET_FOCUS_FIRST +}; + +class Window +{ + friend class GridLayout; + + public: + Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); + + virtual ~Window(); + +#if defined(DEBUG_WINDOWS) + virtual std::string getName() const + { + return "Window"; + } + + std::string getRectString() const + { + char result[32]; + sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); + return result; + } + + std::string getIndentString() const + { + std::string result; + auto tmp = parent; + while (tmp) { + result += " "; + tmp = tmp->getParent(); + } + return result; + } + + std::string getWindowDebugString(const char * name = nullptr) const + { + return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); + } +#endif + + Window * getParent() const + { + return parent; + } + + bool isChild(Window * window) const + { + return window == this || (parent && parent->isChild(window)); + } + + Window *getFullScreenWindow() + { + if (width() == LCD_W && height() == LCD_H) return this; + if (parent) return parent->getFullScreenWindow(); + return nullptr; + } + + WindowFlags getWindowFlags() const + { + return windowFlags; + } + + void setWindowFlags(WindowFlags flags) + { + windowFlags = flags; + } + + LcdFlags getTextFlags() const + { + return textFlags; + } + + void setTextFlags(LcdFlags flags) + { + textFlags = flags; + } + + void setCloseHandler(std::function handler) + { + closeHandler = std::move(handler); + } + + void setFocusHandler(std::function handler) + { + focusHandler = std::move(handler); + } + + const std::list getChildren() + { + return children; + } + + virtual void deleteLater(bool detach = true, bool trash = true); + + void clear(); + + void deleteChildren(); + + bool hasFocus() const + { + return focusWindow == this; + } + + static Window * getFocus() + { + return focusWindow; + } + + void scrollTo(const rect_t & rect); + + void scrollTo(Window * child); + + static void clearFocus(); + + virtual void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr); + + void setRect(rect_t value) + { + rect = value; + invalidate(); + } + + void setWidth(coord_t value) + { + rect.w = value; + invalidate(); + } + + void setWindowCentered() + { + rect.x = (parent->width() - width()) / 2; + rect.y = (parent->height() - height()) / 2; + } + + void setHeight(coord_t value) + { + rect.h = value; + if (windowFlags & FORWARD_SCROLL) + innerHeight = value; + else if (innerHeight <= value) { + setScrollPositionY(0); + } + invalidate(); + } + + void setLeft(coord_t x) + { + rect.x = x; + invalidate(); + } + + void setTop(coord_t y) + { + rect.y = y; + invalidate(); + } + + coord_t left() const + { + return rect.x; + } + + coord_t right() const + { + return rect.x + rect.w; + } + + coord_t top() const + { + return rect.y; + } + + coord_t bottom() const + { + return rect.y + rect.h; + } + + coord_t width() const + { + return rect.w; + } + + coord_t height() const + { + return rect.h; + } + + rect_t getRect() const + { + return rect; + } + + coord_t getInnerWidth() const + { + return innerWidth; + } + + void setInnerWidth(coord_t w) + { + innerWidth = w; + if (width() >= w) { + scrollPositionX = 0; + } + } + + void setPageWidth(coord_t w) + { + pageWidth = w; + } + + void setPageHeight(coord_t h) + { + pageHeight = h; + } + + uint8_t getPageCount() const + { + if (pageWidth) + return innerWidth / pageWidth; + else if (pageHeight) + return innerHeight / pageHeight; + else + return 1; + } + + uint8_t getPageIndex() const + { + if (pageWidth) + return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; + else if (pageHeight) + return (getScrollPositionY() + (pageHeight / 2)) / pageHeight; + else + return 0; + } + + coord_t getInnerHeight() const + { + return innerHeight; + } + + void setInnerHeight(coord_t h) + { + innerHeight = h; + if (windowFlags & FORWARD_SCROLL) { + rect.h = innerHeight; + parent->adjustInnerHeight(); + } + else if (height() >= h) { + setScrollPositionY(0); + } + else { + coord_t maxScrollPosition = h - height(); + if (scrollPositionY > maxScrollPosition) { + setScrollPositionY(maxScrollPosition); + } + } + invalidate(); + } + + coord_t getScrollPositionX() const + { + return scrollPositionX; + } + + coord_t getScrollPositionY() const + { + return scrollPositionY; + } + + virtual void setScrollPositionX(coord_t value); + + virtual void setScrollPositionY(coord_t value); + + bool isChildVisible(const Window * window) const; + + bool isChildFullSize(const Window * window) const; + + bool isVisible() const + { + return parent && parent->isChildVisible(this); + } + + bool isInsideParentScrollingArea() const + { + return parent && right() >= parent->getScrollPositionX() && left() <= parent->getScrollPositionX() + parent->width(); + } + + void setInsideParentScrollingArea(); + + void drawVerticalScrollbar(BitmapBuffer * dc); + + void drawHorizontalScrollbar(BitmapBuffer * dc); + + virtual void onEvent(event_t event); + + void adjustInnerHeight(); + + coord_t adjustHeight(); + + void moveWindowsTop(coord_t y, coord_t delta); + + void invalidate() + { + invalidate({0, 0, rect.w, rect.h}); + } + + void bringToTop() + { + attach(parent); // does a detach + attach + } + + virtual void checkEvents(); + + void attach(Window * window); + + void detach(); + + bool deleted() const + { + return _deleted; + } + + protected: + Window * parent; + std::list children; + rect_t rect; + coord_t innerWidth; + coord_t innerHeight; + coord_t pageWidth = 0; + coord_t pageHeight = 0; + coord_t scrollPositionX = 0; + coord_t scrollPositionY = 0; + WindowFlags windowFlags; + LcdFlags textFlags; + bool _deleted = false; + + static Window * focusWindow; + static Window * slidingWindow; + static Window * touchedWindow; + static std::list trash; + + std::function closeHandler; + std::function focusHandler; + + void addChild(Window * window, bool front = false) + { + if (front) + children.push_front(window); + else + children.push_back(window); + } + + void removeChild(Window * window) + { + children.remove(window); + invalidate(); + } + + virtual void invalidate(const rect_t & rect); + + void paintChildren(BitmapBuffer * dc, std::list::iterator it); + + void fullPaint(BitmapBuffer * dc); + + virtual void paint(BitmapBuffer *) + { + } + + virtual void onFocusLost() + { + TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); + if (focusHandler) { + focusHandler(false); + } + invalidate(); + } + +#if defined(HARDWARE_TOUCH) + static coord_t getSnapStep(coord_t relativeScrollPosition, coord_t pageSize); + + virtual bool onTouchStart(coord_t x, coord_t y); + + virtual bool onTouchEnd(coord_t x, coord_t y); + + virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); +#endif + + bool forwardTouchEnd(coord_t x, coord_t y); + + bool hasOpaqueRect(const rect_t & testRect) const; +}; + From 8ea6f964fa7d3f255adccbb5a6a8f3e2cb267a4b Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 21 Oct 2021 05:30:49 -0400 Subject: [PATCH 2/3] put files in wrong place --- radio/src/thirdparty/libopenui/mainwindow.cpp | 141 ----- radio/src/thirdparty/libopenui/window.cpp | 564 ------------------ radio/src/thirdparty/libopenui/window.h | 451 -------------- 3 files changed, 1156 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/mainwindow.cpp delete mode 100644 radio/src/thirdparty/libopenui/window.cpp delete mode 100644 radio/src/thirdparty/libopenui/window.h diff --git a/radio/src/thirdparty/libopenui/mainwindow.cpp b/radio/src/thirdparty/libopenui/mainwindow.cpp deleted file mode 100644 index de96d7fa9..000000000 --- a/radio/src/thirdparty/libopenui/mainwindow.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "mainwindow.h" -#include "keyboard_base.h" - -#if defined(HARDWARE_TOUCH) -#include "touch.h" -#endif - -MainWindow * MainWindow::_instance = nullptr; - -#if defined(HARDWARE_TOUCH) -TouchState touchState; -Keyboard * Keyboard::activeKeyboard = nullptr; -#endif - -void MainWindow::emptyTrash() -{ - for (auto window: trash) { - delete window; - } - trash.clear(); -} - -void MainWindow::checkEvents() -{ -#if defined(HARDWARE_TOUCH) - if (touchPanelEventOccured()) { - touchPanelRead(); - } - - if (touchState.event == TE_DOWN) { - onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); - slidingWindow = nullptr; - } - else if (touchState.event == TE_UP) { - touchState.event = TE_NONE; - onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); - } - else if (touchState.event == TE_SLIDE) { - if (touchState.deltaX || touchState.deltaY) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); - touchState.lastDeltaX = touchState.deltaX; - touchState.lastDeltaY = touchState.deltaY; - touchState.deltaX = 0; - touchState.deltaY = 0; - } - } - else if (touchState.event == TE_SLIDE_END && slidingWindow) { - if (touchState.lastDeltaX > SLIDE_SPEED_REDUCTION) - touchState.lastDeltaX -= SLIDE_SPEED_REDUCTION; - else if (touchState.lastDeltaX < -SLIDE_SPEED_REDUCTION) - touchState.lastDeltaX += SLIDE_SPEED_REDUCTION; - else - touchState.lastDeltaX = 0; - if (touchState.lastDeltaY > SLIDE_SPEED_REDUCTION) - touchState.lastDeltaY -= SLIDE_SPEED_REDUCTION; - else if (touchState.lastDeltaY < -SLIDE_SPEED_REDUCTION) - touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; - else - touchState.lastDeltaY = 0; - - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); - slidingWindow = nullptr; - } -#endif - - Window::checkEvents(); -} - -void MainWindow::invalidate(const rect_t & rect) -{ - if (invalidatedRect.w) { - auto left = limit(0, rect.left(), invalidatedRect.left()); - auto right = limit(invalidatedRect.right(), rect.right(), LCD_W); - auto top = limit(0, rect.top(), invalidatedRect.top()); - auto bottom = limit(rect.bottom(), invalidatedRect.bottom(), LCD_H); - invalidatedRect = {left, top, right - left, bottom - top}; - } - else { - invalidatedRect = rect; - } -} - -bool MainWindow::refresh() -{ - if (invalidatedRect.w) { - if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { - TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); - lcdCopy(lcd->getData(), lcdFront->getData()); - } - else { - TRACE_WINDOWS("Refresh full screen"); - } - lcd->setOffset(0, 0); - lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); - fullPaint(lcd); - invalidatedRect.w = 0; - return true; - } - else { - return false; - } -} - -void MainWindow::run(bool trash) -{ - auto start = ticksNow(); - - checkEvents(); - - if (trash) { - emptyTrash(); - } - - if (refresh()) { - lcdRefresh(); - } - - auto delta = ticksNow() - start; - if (delta > 10 * SYSTEM_TICKS_1MS) { - TRACE_WINDOWS("MainWindow::run took %dms", (ticksNow() - start) / SYSTEM_TICKS_1MS); - } -} diff --git a/radio/src/thirdparty/libopenui/window.cpp b/radio/src/thirdparty/libopenui/window.cpp deleted file mode 100644 index 46f5d4ce4..000000000 --- a/radio/src/thirdparty/libopenui/window.cpp +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "window.h" -#include "touch.h" - -Window * Window::focusWindow = nullptr; -Window * Window::slidingWindow = nullptr; -Window * Window::touchedWindow = nullptr; -std::list Window::trash; - -Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): - parent(parent), - rect(rect), - innerWidth(rect.w), - innerHeight(rect.h), - windowFlags(windowFlags), - textFlags(textFlags) -{ - if (parent) { - parent->addChild(this, windowFlags & PUSH_FRONT); - if (!(windowFlags & TRANSPARENT)) { - invalidate(); - } - } -} - -Window::~Window() -{ - TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); - - if (focusWindow == this) { - focusWindow = nullptr; - } - - deleteChildren(); -} - -void Window::attach(Window * newParent) -{ - if (parent) detach(); - parent = newParent; - if (newParent) newParent->addChild(this); -} - -void Window::detach() -{ - if (parent) { - parent->removeChild(this); - parent = nullptr; - } -} - -void Window::deleteLater(bool detach, bool trash) -{ - if (_deleted) - return; - - TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); - - _deleted = true; - - if (static_cast(focusWindow) == static_cast(this)) { - focusWindow = nullptr; - } - - if (detach) - this->detach(); - else - parent = nullptr; - - deleteChildren(); - - if (closeHandler) { - closeHandler(); - } - - if (trash) { - Window::trash.push_back(this); - } -} - -void Window::clear() -{ - scrollPositionX = 0; - scrollPositionY = 0; - innerWidth = rect.w; - innerHeight = rect.h; - deleteChildren(); - invalidate(); -} - -void Window::deleteChildren() -{ - for (auto window: children) { - window->deleteLater(false); - } - children.clear(); -} - -void Window::clearFocus() -{ - if (focusWindow) { - focusWindow->onFocusLost(); - focusWindow = nullptr; - } -} - -void Window::setFocus(uint8_t flag, Window * from) -{ - if (deleted()) - return; - - TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); - - if (focusWindow != this) { - // scroll before calling focusHandler so that the window can adjust the scroll position if needed - Window * parent = this->parent; - while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { - parent = parent->parent; - } - if (parent) { - parent->scrollTo(this); - invalidate(); - } - - clearFocus(); - focusWindow = this; - if (focusHandler) { - focusHandler(true); - } - } -} - -void Window::setScrollPositionX(coord_t value) -{ - auto newScrollPosition = max(0, min(innerWidth - width(), value)); - if (newScrollPosition != scrollPositionX) { - scrollPositionX = newScrollPosition; - invalidate(); - } -} - -void Window::setScrollPositionY(coord_t value) -{ - auto newScrollPosition = min(innerHeight - height(), value); - - if (newScrollPosition < 0 && innerHeight != INFINITE_HEIGHT) { - newScrollPosition = 0; - } - - if (newScrollPosition != scrollPositionY) { - scrollPositionY = newScrollPosition; - invalidate(); - } -} - -void Window::scrollTo(Window * child) -{ - coord_t offsetX = 0; - coord_t offsetY = 0; - - Window * parentWindow = child->getParent(); - while (parentWindow && parentWindow != this) { - offsetX += parentWindow->left(); - offsetY += parentWindow->top(); - parentWindow = parentWindow->getParent(); - } - - const rect_t scrollRect = { - offsetX + child->left(), - offsetY + child->top(), - min(child->width(), width()), - min(child->height(), height()) - }; - - scrollTo(scrollRect); -} - -void Window::scrollTo(const rect_t & rect) -{ - if (rect.top() < scrollPositionY) { - setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.top() - 5); - } - else if (rect.bottom() > scrollPositionY + height() - 5) { - setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.bottom() - height() + 5); - } - - if (rect.left() < scrollPositionX) { - setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.left() - 5); - } - else if (rect.right() > scrollPositionX + width() - 5) { - setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.right() - width() + 5); - } -} - -bool Window::hasOpaqueRect(const rect_t & testRect) const -{ - if (!rect.contains(testRect)) - return false; - - if (windowFlags & OPAQUE) { - return true; - } - - rect_t relativeRect = {testRect.x - rect.x, testRect.y - rect.y, testRect.w, testRect.h}; - for (auto child: children) { - if (child->hasOpaqueRect(relativeRect)) - return true; - } - - return false; -} - -void Window::fullPaint(BitmapBuffer * dc) -{ - bool paintNeeded = true; - std::list::iterator firstChild; - - coord_t xmin, xmax, ymin, ymax; - dc->getClippingRect(xmin, xmax, ymin, ymax); - coord_t x = dc->getOffsetX(); - coord_t y = dc->getOffsetY(); - - if (windowFlags & PAINT_CHILDREN_FIRST) { - paintChildren(dc, children.begin()); - dc->setOffset(x, y); - dc->setClippingRect(xmin, xmax, ymin, ymax); - } - else { - firstChild = children.end(); - rect_t relativeRect = {xmin - x, ymin - y, xmax - xmin, ymax - ymin}; - while (firstChild != children.begin()) { - auto child = *(--firstChild); - if (child->hasOpaqueRect(relativeRect)) { - paintNeeded = false; - break; - } - } - } - - if (paintNeeded) { - TRACE_WINDOWS_INDENT("%s%s", getWindowDebugString().c_str(), hasFocus() ? " (*)" : ""); - paint(dc); -#if defined(WINDOWS_INSPECT_BORDER_COLOR) - dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR); -#endif - } - else { - TRACE_WINDOWS_INDENT("%s (skipped)", getWindowDebugString().c_str()); - } - - if (!(windowFlags & NO_SCROLLBAR)) { - drawVerticalScrollbar(dc); - } - - if (!(windowFlags & PAINT_CHILDREN_FIRST)) { - paintChildren(dc, firstChild); - } -} - -bool Window::isChildFullSize(const Window * child) const -{ - return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width(); -} - -bool Window::isChildVisible(const Window * window) const -{ - for (auto rit = children.rbegin(); rit != children.rend(); rit++) { - auto child = *rit; - if (child == window) { - return true; - } - if ((child->getWindowFlags() & OPAQUE) & isChildFullSize(child)) { - return false; - } - } - return false; -} - -void Window::setInsideParentScrollingArea() -{ - Window * parent = getParent(); - while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { - parent = parent->parent; - } - if (parent) { - parent->scrollTo(this); - invalidate(); - } -} - -void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) -{ - coord_t x = dc->getOffsetX(); - coord_t y = dc->getOffsetY(); - coord_t xmin, xmax, ymin, ymax; - dc->getClippingRect(xmin, xmax, ymin, ymax); - - for (; it != children.end(); it++) { - auto child = *it; - - coord_t child_xmin = x + child->rect.x; - if (child_xmin >= xmax) - continue; - coord_t child_ymin = y + child->rect.y; - if (child_ymin >= ymax) - continue; - coord_t child_xmax = child_xmin + child->rect.w; - if (child_xmax <= xmin) - continue; - coord_t child_ymax = child_ymin + child->rect.h; - if (child_ymax <= ymin) - continue; - - dc->setOffset(x + child->rect.x - child->scrollPositionX, y + child->rect.y - child->scrollPositionY); - dc->setClippingRect(max(xmin, x + child->rect.left()), - min(xmax, x + child->rect.right()), - max(ymin, y + child->rect.top()), - min(ymax, y + child->rect.bottom())); - child->fullPaint(dc); - } -} - -#if defined(HARDWARE_TOUCH) -coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) -{ - coord_t result = 0; - if (relativeScrollPosition > pageSize / 2) { - // closer to next page - result = (pageSize - relativeScrollPosition); - } - else { - // closer to previous page - result = (0 - relativeScrollPosition); - } - - // do not get too slow - if (abs(result) > 32) - result /= 2; - - return result; -} -#endif - -void Window::checkEvents() -{ - auto copy = children; - for (auto child: copy) { - if (!child->deleted()) { - child->checkEvents(); - } - } - - if (this == Window::focusWindow) { - event_t event = getWindowEvent(); - if (event) { - this->onEvent(event); - } - } - - if (windowFlags & REFRESH_ALWAYS) { - invalidate(); - } - -#if defined(HARDWARE_TOUCH) - if (touchState.event != TE_SLIDE && touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) { - if (pageWidth) { - coord_t relativeScrollPosition = getScrollPositionX() % pageWidth; - if (relativeScrollPosition) { - setScrollPositionX(getScrollPositionX() + getSnapStep(relativeScrollPosition, pageWidth)); - } - } - if (pageHeight) { - coord_t relativeScrollPosition = getScrollPositionY() % pageHeight; - if (relativeScrollPosition) { - setScrollPositionY(getScrollPositionY() + getSnapStep(relativeScrollPosition, pageHeight)); - } - } - } -#endif -} - -void Window::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString("Window").c_str(), event); - if (parent) { - parent->onEvent(event); - } -} - -#if defined(HARDWARE_TOUCH) -bool Window::onTouchStart(coord_t x, coord_t y) -{ - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(x, y)) { - if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { - // remember the first window that processed the touch start event. We will give that window - // the opportunity to process the touchend first, and also after a slide end so the touched window - // always gets a touchEnd if it processed the touch start. - if (touchedWindow == nullptr) { - touchedWindow = child; - } - return true; - } - } - } - - return false; -} - -bool Window::forwardTouchEnd(coord_t x, coord_t y) -{ - // give the touched window the opportunity to process the touch first before it is - // forwarded: See on touchstart - if (touchedWindow != nullptr) { - auto savedTouchWindow = touchedWindow; - touchedWindow = nullptr; - // transalte x and y through the parent hierarchy - auto myParent = savedTouchWindow->parent; - while(myParent != nullptr) { - x = x - myParent->rect.x + myParent->scrollPositionX; - y = y - myParent->rect.y + myParent->scrollPositionY; - myParent = myParent->parent; - } - - if (savedTouchWindow->onTouchEnd( - x - savedTouchWindow->rect.x + savedTouchWindow->scrollPositionX, - y - savedTouchWindow->rect.y + savedTouchWindow->scrollPositionY)) { - return true; - } - } - - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(x, y)) { - if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { - return true; - } - } - } - - return false; -} - -bool Window::onTouchEnd(coord_t x, coord_t y) -{ - return forwardTouchEnd(x, y) ? true : (windowFlags & OPAQUE); -} - -bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) -{ - startX += getScrollPositionX(); - startY += getScrollPositionY(); - - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(startX, startY)) { - if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { - return true; - } - } - } - - if (slidingWindow && slidingWindow != this) { - return false; - } - - if (slideY && innerHeight > rect.h) { - setScrollPositionY(scrollPositionY - slideY); - slidingWindow = this; - return true; - } - - if (slideX && innerWidth > rect.w) { - setScrollPositionX(scrollPositionX - slideX); - slidingWindow = this; - return true; - } - - return false; -} -#endif - -void Window::adjustInnerHeight() -{ - coord_t bottomMax = 0; - for (auto child: children) { - bottomMax = max(bottomMax, child->rect.y + child->rect.h); - } - setInnerHeight(bottomMax); -} - -coord_t Window::adjustHeight() -{ - coord_t old = rect.h; - adjustInnerHeight(); - rect.h = innerHeight; - return rect.h - old; -} - -void Window::moveWindowsTop(coord_t y, coord_t delta) -{ - if (getWindowFlags() & FORWARD_SCROLL) { - parent->moveWindowsTop(bottom(), delta); - } - - for (auto child: children) { - if (child->rect.y >= y) { - child->rect.y += delta; - invalidate(); - } - } -} - -void Window::invalidate(const rect_t & rect) -{ - if (isVisible()) { - parent->invalidate({this->rect.x + rect.x - parent->scrollPositionX, this->rect.y + rect.y - parent->scrollPositionY, rect.w, rect.h}); - } -} - -void Window::drawVerticalScrollbar(BitmapBuffer * dc) -{ - if (innerHeight > rect.h) { - coord_t yofs = divRoundClosest(rect.h * scrollPositionY, innerHeight); - coord_t yhgt = divRoundClosest(rect.h * rect.h, innerHeight); - if (yhgt < 15) - yhgt = 15; - if (yhgt + yofs > rect.h) - yhgt = rect.h - yofs; - dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, COLOR_THEME_PRIMARY3); - } -} - -void Window::drawHorizontalScrollbar(BitmapBuffer * dc) -{ - if (innerWidth > rect.w) { - coord_t xofs = divRoundClosest(rect.w * scrollPositionX, innerWidth); - coord_t xwdth = divRoundClosest(rect.w * rect.w, innerWidth); - if (xwdth < 15) - xwdth = 15; - if (xwdth + xofs > rect.w) - xwdth = rect.w - xofs; - dc->drawSolidFilledRect(scrollPositionX + xofs, rect.h - SCROLLBAR_WIDTH, xwdth, SCROLLBAR_WIDTH, COLOR_THEME_PRIMARY3); - } -} diff --git a/radio/src/thirdparty/libopenui/window.h b/radio/src/thirdparty/libopenui/window.h deleted file mode 100644 index 2b9b045e6..000000000 --- a/radio/src/thirdparty/libopenui/window.h +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include "bitmapbuffer.h" -#include "libopenui_defines.h" -#include "libopenui_helpers.h" -#include "libopenui_config.h" - -typedef uint32_t WindowFlags; - -#if !defined(_GNUC_) - #undef OPAQUE - #undef TRANSPARENT -#endif - -constexpr int INFINITE_HEIGHT = INT32_MAX; - -constexpr WindowFlags OPAQUE = 1u << 0u; -constexpr WindowFlags TRANSPARENT = 1u << 1u; -constexpr WindowFlags NO_SCROLLBAR = 1u << 2u; -constexpr WindowFlags NO_FOCUS = 1u << 3u; -constexpr WindowFlags FORWARD_SCROLL = 1u << 4u; -constexpr WindowFlags REFRESH_ALWAYS = 1u << 5u; -constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6u; -constexpr WindowFlags PUSH_FRONT = 1u << 7u; -constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; - -enum SetFocusFlag -{ - SET_FOCUS_DEFAULT, - SET_FOCUS_FORWARD, - SET_FOCUS_BACKWARD, - SET_FOCUS_FIRST -}; - -class Window -{ - friend class GridLayout; - - public: - Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); - - virtual ~Window(); - -#if defined(DEBUG_WINDOWS) - virtual std::string getName() const - { - return "Window"; - } - - std::string getRectString() const - { - char result[32]; - sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); - return result; - } - - std::string getIndentString() const - { - std::string result; - auto tmp = parent; - while (tmp) { - result += " "; - tmp = tmp->getParent(); - } - return result; - } - - std::string getWindowDebugString(const char * name = nullptr) const - { - return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); - } -#endif - - Window * getParent() const - { - return parent; - } - - bool isChild(Window * window) const - { - return window == this || (parent && parent->isChild(window)); - } - - Window *getFullScreenWindow() - { - if (width() == LCD_W && height() == LCD_H) return this; - if (parent) return parent->getFullScreenWindow(); - return nullptr; - } - - WindowFlags getWindowFlags() const - { - return windowFlags; - } - - void setWindowFlags(WindowFlags flags) - { - windowFlags = flags; - } - - LcdFlags getTextFlags() const - { - return textFlags; - } - - void setTextFlags(LcdFlags flags) - { - textFlags = flags; - } - - void setCloseHandler(std::function handler) - { - closeHandler = std::move(handler); - } - - void setFocusHandler(std::function handler) - { - focusHandler = std::move(handler); - } - - const std::list getChildren() - { - return children; - } - - virtual void deleteLater(bool detach = true, bool trash = true); - - void clear(); - - void deleteChildren(); - - bool hasFocus() const - { - return focusWindow == this; - } - - static Window * getFocus() - { - return focusWindow; - } - - void scrollTo(const rect_t & rect); - - void scrollTo(Window * child); - - static void clearFocus(); - - virtual void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr); - - void setRect(rect_t value) - { - rect = value; - invalidate(); - } - - void setWidth(coord_t value) - { - rect.w = value; - invalidate(); - } - - void setWindowCentered() - { - rect.x = (parent->width() - width()) / 2; - rect.y = (parent->height() - height()) / 2; - } - - void setHeight(coord_t value) - { - rect.h = value; - if (windowFlags & FORWARD_SCROLL) - innerHeight = value; - else if (innerHeight <= value) { - setScrollPositionY(0); - } - invalidate(); - } - - void setLeft(coord_t x) - { - rect.x = x; - invalidate(); - } - - void setTop(coord_t y) - { - rect.y = y; - invalidate(); - } - - coord_t left() const - { - return rect.x; - } - - coord_t right() const - { - return rect.x + rect.w; - } - - coord_t top() const - { - return rect.y; - } - - coord_t bottom() const - { - return rect.y + rect.h; - } - - coord_t width() const - { - return rect.w; - } - - coord_t height() const - { - return rect.h; - } - - rect_t getRect() const - { - return rect; - } - - coord_t getInnerWidth() const - { - return innerWidth; - } - - void setInnerWidth(coord_t w) - { - innerWidth = w; - if (width() >= w) { - scrollPositionX = 0; - } - } - - void setPageWidth(coord_t w) - { - pageWidth = w; - } - - void setPageHeight(coord_t h) - { - pageHeight = h; - } - - uint8_t getPageCount() const - { - if (pageWidth) - return innerWidth / pageWidth; - else if (pageHeight) - return innerHeight / pageHeight; - else - return 1; - } - - uint8_t getPageIndex() const - { - if (pageWidth) - return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; - else if (pageHeight) - return (getScrollPositionY() + (pageHeight / 2)) / pageHeight; - else - return 0; - } - - coord_t getInnerHeight() const - { - return innerHeight; - } - - void setInnerHeight(coord_t h) - { - innerHeight = h; - if (windowFlags & FORWARD_SCROLL) { - rect.h = innerHeight; - parent->adjustInnerHeight(); - } - else if (height() >= h) { - setScrollPositionY(0); - } - else { - coord_t maxScrollPosition = h - height(); - if (scrollPositionY > maxScrollPosition) { - setScrollPositionY(maxScrollPosition); - } - } - invalidate(); - } - - coord_t getScrollPositionX() const - { - return scrollPositionX; - } - - coord_t getScrollPositionY() const - { - return scrollPositionY; - } - - virtual void setScrollPositionX(coord_t value); - - virtual void setScrollPositionY(coord_t value); - - bool isChildVisible(const Window * window) const; - - bool isChildFullSize(const Window * window) const; - - bool isVisible() const - { - return parent && parent->isChildVisible(this); - } - - bool isInsideParentScrollingArea() const - { - return parent && right() >= parent->getScrollPositionX() && left() <= parent->getScrollPositionX() + parent->width(); - } - - void setInsideParentScrollingArea(); - - void drawVerticalScrollbar(BitmapBuffer * dc); - - void drawHorizontalScrollbar(BitmapBuffer * dc); - - virtual void onEvent(event_t event); - - void adjustInnerHeight(); - - coord_t adjustHeight(); - - void moveWindowsTop(coord_t y, coord_t delta); - - void invalidate() - { - invalidate({0, 0, rect.w, rect.h}); - } - - void bringToTop() - { - attach(parent); // does a detach + attach - } - - virtual void checkEvents(); - - void attach(Window * window); - - void detach(); - - bool deleted() const - { - return _deleted; - } - - protected: - Window * parent; - std::list children; - rect_t rect; - coord_t innerWidth; - coord_t innerHeight; - coord_t pageWidth = 0; - coord_t pageHeight = 0; - coord_t scrollPositionX = 0; - coord_t scrollPositionY = 0; - WindowFlags windowFlags; - LcdFlags textFlags; - bool _deleted = false; - - static Window * focusWindow; - static Window * slidingWindow; - static Window * touchedWindow; - static std::list trash; - - std::function closeHandler; - std::function focusHandler; - - void addChild(Window * window, bool front = false) - { - if (front) - children.push_front(window); - else - children.push_back(window); - } - - void removeChild(Window * window) - { - children.remove(window); - invalidate(); - } - - virtual void invalidate(const rect_t & rect); - - void paintChildren(BitmapBuffer * dc, std::list::iterator it); - - void fullPaint(BitmapBuffer * dc); - - virtual void paint(BitmapBuffer *) - { - } - - virtual void onFocusLost() - { - TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); - if (focusHandler) { - focusHandler(false); - } - invalidate(); - } - -#if defined(HARDWARE_TOUCH) - static coord_t getSnapStep(coord_t relativeScrollPosition, coord_t pageSize); - - virtual bool onTouchStart(coord_t x, coord_t y); - - virtual bool onTouchEnd(coord_t x, coord_t y); - - virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); -#endif - - bool forwardTouchEnd(coord_t x, coord_t y); - - bool hasOpaqueRect(const rect_t & testRect) const; -}; - From 582f953e500a9e37fe0990a75e06d1e7cc63e11b Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 21 Oct 2021 05:32:03 -0400 Subject: [PATCH 3/3] put files in right place --- .../thirdparty/libopenui/src/mainwindow.cpp | 6 ++--- radio/src/thirdparty/libopenui/src/window.cpp | 27 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/window.h | 1 + 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 7cbe46111..de96d7fa9 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -76,9 +76,9 @@ void MainWindow::checkEvents() touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; else touchState.lastDeltaY = 0; - if (touchState.lastDeltaX || touchState.lastDeltaY) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); - } + + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); + slidingWindow = nullptr; } #endif diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7f0222f4a..46f5d4ce4 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -22,6 +22,7 @@ Window * Window::focusWindow = nullptr; Window * Window::slidingWindow = nullptr; +Window * Window::touchedWindow = nullptr; std::list Window::trash; Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): @@ -411,6 +412,12 @@ bool Window::onTouchStart(coord_t x, coord_t y) auto child = *it; if (child->rect.contains(x, y)) { if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + // remember the first window that processed the touch start event. We will give that window + // the opportunity to process the touchend first, and also after a slide end so the touched window + // always gets a touchEnd if it processed the touch start. + if (touchedWindow == nullptr) { + touchedWindow = child; + } return true; } } @@ -421,6 +428,26 @@ bool Window::onTouchStart(coord_t x, coord_t y) bool Window::forwardTouchEnd(coord_t x, coord_t y) { + // give the touched window the opportunity to process the touch first before it is + // forwarded: See on touchstart + if (touchedWindow != nullptr) { + auto savedTouchWindow = touchedWindow; + touchedWindow = nullptr; + // transalte x and y through the parent hierarchy + auto myParent = savedTouchWindow->parent; + while(myParent != nullptr) { + x = x - myParent->rect.x + myParent->scrollPositionX; + y = y - myParent->rect.y + myParent->scrollPositionY; + myParent = myParent->parent; + } + + if (savedTouchWindow->onTouchEnd( + x - savedTouchWindow->rect.x + savedTouchWindow->scrollPositionX, + y - savedTouchWindow->rect.y + savedTouchWindow->scrollPositionY)) { + return true; + } + } + for (auto it = children.rbegin(); it != children.rend(); ++it) { auto child = *it; if (child->rect.contains(x, y)) { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 6902eb4e4..2b9b045e6 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -395,6 +395,7 @@ class Window static Window * focusWindow; static Window * slidingWindow; + static Window * touchedWindow; static std::list trash; std::function closeHandler;