From 831def7aaba4950c91f90fdff9f8364a4cef1ebb Mon Sep 17 00:00:00 2001 From: XITRIX Date: Mon, 1 Feb 2021 01:09:54 +0300 Subject: [PATCH 01/59] Basic touch event system --- library/include/borealis/core/application.hpp | 1 + library/include/borealis/core/platform.hpp | 7 +++ library/include/borealis/core/touch.hpp | 49 +++++++++++++++ .../borealis/platforms/glfw/glfw_platform.hpp | 3 + .../borealis/platforms/glfw/glfw_touch.hpp | 43 +++++++++++++ library/include/borealis/views/box.hpp | 1 + library/include/borealis/views/view.hpp | 9 +++ library/lib/core/application.cpp | 19 ++++++ library/lib/platforms/glfw/glfw_platform.cpp | 6 ++ library/lib/platforms/glfw/glfw_touch.cpp | 62 +++++++++++++++++++ library/lib/views/box.cpp | 24 +++++++ library/lib/views/view.cpp | 20 ++++++ library/meson.build | 1 + 13 files changed, 245 insertions(+) create mode 100644 library/include/borealis/core/touch.hpp create mode 100644 library/include/borealis/platforms/glfw/glfw_touch.hpp create mode 100644 library/lib/platforms/glfw/glfw_touch.cpp diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index 1b871028..757e4ce8 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -204,6 +204,7 @@ class Application inline static ControllerState oldControllerState = {}; inline static ControllerState controllerState = {}; + inline static TouchState touchState = {}; inline static unsigned blockInputsTokens = 0; // any value > 0 means inputs are blocked diff --git a/library/include/borealis/core/platform.hpp b/library/include/borealis/core/platform.hpp index 5fb7be39..f8194c21 100644 --- a/library/include/borealis/core/platform.hpp +++ b/library/include/borealis/core/platform.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace brls @@ -66,6 +67,12 @@ class Platform */ virtual InputManager* getInputManager() = 0; + /** + * Returns the InputManager for the platform. + * Cannot return nullptr. + */ + virtual TouchManager* getTouchManager() = 0; + /** * Selects and returns the best platform. */ diff --git a/library/include/borealis/core/touch.hpp b/library/include/borealis/core/touch.hpp new file mode 100644 index 00000000..6b89a9fd --- /dev/null +++ b/library/include/borealis/core/touch.hpp @@ -0,0 +1,49 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +namespace brls +{ + +enum TouchClickState { + START, + STAY, + END, + NONE, +}; + +typedef struct TouchState +{ + TouchClickState state; + double x, y; +} TouchState; + +class TouchManager +{ + public: + virtual ~TouchManager() { } + + /** + * Called once every frame to fill the given TouchState struct with the touch state. + */ + virtual void updateTouchState(TouchState* state) = 0; +}; + +}; // namespace brls \ No newline at end of file diff --git a/library/include/borealis/platforms/glfw/glfw_platform.hpp b/library/include/borealis/platforms/glfw/glfw_platform.hpp index e4c31e73..29fdc883 100644 --- a/library/include/borealis/platforms/glfw/glfw_platform.hpp +++ b/library/include/borealis/platforms/glfw/glfw_platform.hpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace brls { @@ -38,11 +39,13 @@ class GLFWPlatform : public Platform AudioPlayer* getAudioPlayer() override; VideoContext* getVideoContext() override; InputManager* getInputManager() override; + TouchManager* getTouchManager() override; private: NullAudioPlayer* audioPlayer = nullptr; GLFWVideoContext* videoContext = nullptr; GLFWInputManager* inputManager = nullptr; + GLFWTouchManager* touchManager = nullptr; }; } // namespace brls diff --git a/library/include/borealis/platforms/glfw/glfw_touch.hpp b/library/include/borealis/platforms/glfw/glfw_touch.hpp new file mode 100644 index 00000000..1ca7c4ba --- /dev/null +++ b/library/include/borealis/platforms/glfw/glfw_touch.hpp @@ -0,0 +1,43 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include + +#define GLFW_INCLUDE_NONE +#include + +namespace brls +{ + +// Input manager for GLFW coursor +class GLFWTouchManager : public TouchManager +{ + public: + GLFWTouchManager(GLFWwindow* window); + + void updateTouchState(TouchState* state) override; + + private: + GLFWwindow* window; + TouchState oldTouch; +}; + +}; \ No newline at end of file diff --git a/library/include/borealis/views/box.hpp b/library/include/borealis/views/box.hpp index 854381ff..3fa162ad 100644 --- a/library/include/borealis/views/box.hpp +++ b/library/include/borealis/views/box.hpp @@ -68,6 +68,7 @@ class Box : public View void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; View* getDefaultFocus() override; + View* getFocus(double x, double y) override; View* getNextFocus(FocusDirection direction, View* currentView) override; void willAppear(bool resetState) override; void willDisappear(bool resetState) override; diff --git a/library/include/borealis/views/view.hpp b/library/include/borealis/views/view.hpp index 4f4750df..9842c4aa 100644 --- a/library/include/borealis/views/view.hpp +++ b/library/include/borealis/views/view.hpp @@ -1140,6 +1140,15 @@ class View */ virtual View* getDefaultFocus(); + /** + * Returns the view to focus with the corresponding screen coordinates in the view or its children, + * or nullptr if it hasn't been found. + * + * Research is done recursively by traversing the tree starting from this view. + * This view's parents are not traversed. + */ + virtual View* getFocus(double x, double y); + /** * Returns the next view to focus given the requested direction * and the currently focused view (as parent user data) diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 6793a1de..e2d62ac3 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -219,6 +219,25 @@ bool Application::mainLoop() return false; } + // Touch + TouchManager* touchManager = Application::platform->getTouchManager(); + touchManager->updateTouchState(&Application::touchState); + + if (Application::touchState.state == END) + { + Logger::info("Touched at X: " + std::to_string(Application::touchState.x) + ", Y: " + std::to_string(Application::touchState.y)); + + Activity* last = Application::activitiesStack[Application::activitiesStack.size() - 1]; + View* nextFocus = last->getContentView()->getFocus(Application::touchState.x, Application::touchState.y); + + if (nextFocus) { + enum Sound focusSound = nextFocus->getFocusSound(); + Application::getAudioPlayer()->play(focusSound); + Application::giveFocus(nextFocus); + } + } + + // Input InputManager* inputManager = Application::platform->getInputManager(); inputManager->updateControllerState(&Application::controllerState); diff --git a/library/lib/platforms/glfw/glfw_platform.cpp b/library/lib/platforms/glfw/glfw_platform.cpp index 6964463a..87e2d17c 100644 --- a/library/lib/platforms/glfw/glfw_platform.cpp +++ b/library/lib/platforms/glfw/glfw_platform.cpp @@ -49,6 +49,7 @@ GLFWPlatform::GLFWPlatform(std::string windowTitle, uint32_t windowWidth, uint32 this->audioPlayer = new NullAudioPlayer(); this->videoContext = new GLFWVideoContext(windowTitle, windowWidth, windowHeight); this->inputManager = new GLFWInputManager(this->videoContext->getGLFWWindow()); + this->touchManager = new GLFWTouchManager(this->videoContext->getGLFWWindow()); // Misc glfwSetTime(0.0); @@ -90,6 +91,11 @@ InputManager* GLFWPlatform::getInputManager() return this->inputManager; } +TouchManager* GLFWPlatform::getTouchManager() +{ + return this->touchManager; +} + GLFWPlatform::~GLFWPlatform() { delete this->audioPlayer; diff --git a/library/lib/platforms/glfw/glfw_touch.cpp b/library/lib/platforms/glfw/glfw_touch.cpp new file mode 100644 index 00000000..50ddc368 --- /dev/null +++ b/library/lib/platforms/glfw/glfw_touch.cpp @@ -0,0 +1,62 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +#define GLFW_INCLUDE_NONE +#include + +namespace brls +{ + +GLFWTouchManager::GLFWTouchManager(GLFWwindow* window) + : window(window) +{ + +} + +void GLFWTouchManager::updateTouchState(TouchState* state) +{ + // Get gamepad state + double x, y; + glfwGetCursorPos(this->window, &x, &y); + + state->x = x / Application::windowScale; + state->y = y / Application::windowScale; + + int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); + if (clickState == GLFW_PRESS) { + if (oldTouch.state == START || oldTouch.state == STAY) { + state->state = STAY; + } else { + state->state = START; + } + } else { + if (oldTouch.state == END || oldTouch.state == NONE) { + state->state = NONE; + } else { + state->state = END; + } + } + oldTouch = *state; +} + +}; \ No newline at end of file diff --git a/library/lib/views/box.cpp b/library/lib/views/box.cpp index 66171d7c..7efc0aac 100644 --- a/library/lib/views/box.cpp +++ b/library/lib/views/box.cpp @@ -319,6 +319,30 @@ View* Box::getDefaultFocus() return nullptr; } +View* Box::getFocus(double x, double y) +{ + // Check if touch fits in view frame + if (getX() <= x && + getWidth() + getX() >= x && + getY() <= y && + getHeight() + getY() >= y) + { + Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); + for (View* child : this->children) + { + View* result = child->getFocus(x, y); + + if (result) + return result; + } + + Logger::debug(describe() + ": OK"); + return this; + } + + return nullptr; +} + View* Box::getNextFocus(FocusDirection direction, View* currentView) { void* parentUserData = currentView->getParentUserData(); diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index 16656168..120464db 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -2039,6 +2039,26 @@ View* View::getDefaultFocus() return nullptr; } +View* View::getFocus(double x, double y) +{ + // Check if can focus ourself first + if (!this->isFocusable()) + return nullptr; + + Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); + + if (getX() <= x && + getWidth() + getX() >= x && + getY() <= y && + getHeight() + getY() >= y) + { + Logger::debug(describe() + ": OK"); + return getDefaultFocus(); + } + + return nullptr; +} + void View::bindXMLDocument(tinyxml2::XMLDocument* document) { this->boundDocuments.push_back(document); diff --git a/library/meson.build b/library/meson.build index cbf72fa3..e68e4384 100644 --- a/library/meson.build +++ b/library/meson.build @@ -16,6 +16,7 @@ borealis_files = files( 'lib/platforms/glfw/glfw_platform.cpp', 'lib/platforms/glfw/glfw_video.cpp', 'lib/platforms/glfw/glfw_input.cpp', + 'lib/platforms/glfw/glfw_touch.cpp', 'lib/platforms/switch/swkbd.cpp', # TODO: remove it from build.meson, it's switch only From cdfe9dc50d0be329a0d54a30d7d9eb2d8eb65cb6 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Mon, 1 Feb 2021 01:51:33 +0300 Subject: [PATCH 02/59] Added Switch implementation --- library/include/borealis/core/touch.hpp | 2 +- .../platforms/switch/switch_platform.hpp | 3 + .../platforms/switch/switch_touch.hpp | 41 ++++++++++++ .../lib/platforms/switch/switch_platform.cpp | 6 ++ library/lib/platforms/switch/switch_touch.cpp | 66 +++++++++++++++++++ 5 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 library/include/borealis/platforms/switch/switch_touch.hpp create mode 100644 library/lib/platforms/switch/switch_touch.cpp diff --git a/library/include/borealis/core/touch.hpp b/library/include/borealis/core/touch.hpp index 6b89a9fd..e1184513 100644 --- a/library/include/borealis/core/touch.hpp +++ b/library/include/borealis/core/touch.hpp @@ -31,7 +31,7 @@ enum TouchClickState { typedef struct TouchState { - TouchClickState state; + TouchClickState state = NONE; double x, y; } TouchState; diff --git a/library/include/borealis/platforms/switch/switch_platform.hpp b/library/include/borealis/platforms/switch/switch_platform.hpp index 6565d8d9..bd8802a3 100644 --- a/library/include/borealis/platforms/switch/switch_platform.hpp +++ b/library/include/borealis/platforms/switch/switch_platform.hpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace brls { @@ -37,10 +38,12 @@ class SwitchPlatform : public GLFWPlatform // TODO: don't inherit from GLFWPlatf AudioPlayer* getAudioPlayer() override; InputManager* getInputManager() override; + TouchManager* getTouchManager() override; private: SwitchAudioPlayer* audioPlayer; InputManager* inputManager; + SwitchTouchManager* touchManager; }; } // namespace brls diff --git a/library/include/borealis/platforms/switch/switch_touch.hpp b/library/include/borealis/platforms/switch/switch_touch.hpp new file mode 100644 index 00000000..3f328a13 --- /dev/null +++ b/library/include/borealis/platforms/switch/switch_touch.hpp @@ -0,0 +1,41 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include + +#include + +namespace brls +{ + +// Input manager for Switch touches +class SwitchTouchManager : public TouchManager +{ + public: + SwitchTouchManager(); + + void updateTouchState(TouchState* state) override; + + private: + TouchState oldTouch; +}; + +}; \ No newline at end of file diff --git a/library/lib/platforms/switch/switch_platform.cpp b/library/lib/platforms/switch/switch_platform.cpp index 7d53939e..bae53400 100644 --- a/library/lib/platforms/switch/switch_platform.cpp +++ b/library/lib/platforms/switch/switch_platform.cpp @@ -28,6 +28,7 @@ SwitchPlatform::SwitchPlatform(std::string windowTitle, uint32_t windowWidth, ui { this->audioPlayer = new SwitchAudioPlayer(); this->inputManager = new SwitchInputManager(); + this->touchManager = new SwitchTouchManager(); } std::string SwitchPlatform::getName() @@ -50,6 +51,11 @@ InputManager* SwitchPlatform::getInputManager() return this->inputManager; } +TouchManager* SwitchPlatform::getTouchManager() +{ + return this->touchManager; +} + SwitchPlatform::~SwitchPlatform() { delete this->audioPlayer; diff --git a/library/lib/platforms/switch/switch_touch.cpp b/library/lib/platforms/switch/switch_touch.cpp new file mode 100644 index 00000000..422d3488 --- /dev/null +++ b/library/lib/platforms/switch/switch_touch.cpp @@ -0,0 +1,66 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +namespace brls +{ + +SwitchTouchManager::SwitchTouchManager() +{ + +} + +void SwitchTouchManager::updateTouchState(TouchState* state) +{ + // Get gamepad state + double x = oldTouch.x; + double y = oldTouch.y; + + HidTouchScreenState hidState={0}; + if (hidGetTouchScreenStates(&hidState, 1)) + { + if (hidState.count > 0) { + x = hidState.touches[0].x; + y = hidState.touches[0].y; + } + } + + state->x = x / Application::windowScale; + state->y = y / Application::windowScale; + + if (hidState.count > 0) { + if (oldTouch.state == START || oldTouch.state == STAY) { + state->state = STAY; + } else { + state->state = START; + } + } else { + if (oldTouch.state == END || oldTouch.state == NONE) { + state->state = NONE; + } else { + state->state = END; + } + } + oldTouch = *state; +} + +}; \ No newline at end of file From 72b07de960733a191f0c986c513be0a1ef4fe0cb Mon Sep 17 00:00:00 2001 From: XITRIX Date: Tue, 2 Feb 2021 02:10:56 +0300 Subject: [PATCH 03/59] Implemented gesture recognizion system --- library/include/borealis/core/application.hpp | 2 + .../borealis/touch/gesture_recognizer.hpp | 40 +++++++++++ .../borealis/touch/tap_gesture_recognizer.hpp | 40 +++++++++++ library/include/borealis/views/box.hpp | 2 +- library/include/borealis/views/view.hpp | 18 ++++- library/lib/core/application.cpp | 22 +++--- library/lib/touch/tap_gesture_recognizer.cpp | 69 +++++++++++++++++++ library/lib/views/box.cpp | 9 +-- library/lib/views/button.cpp | 7 ++ library/lib/views/sidebar.cpp | 5 ++ library/lib/views/view.cpp | 43 ++++++++++-- library/meson.build | 2 + 12 files changed, 235 insertions(+), 24 deletions(-) create mode 100644 library/include/borealis/touch/gesture_recognizer.hpp create mode 100644 library/include/borealis/touch/tap_gesture_recognizer.hpp create mode 100644 library/lib/touch/tap_gesture_recognizer.cpp diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index 757e4ce8..b162d7df 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -206,6 +206,8 @@ class Application inline static ControllerState controllerState = {}; inline static TouchState touchState = {}; + inline static View* firstResponder; // View that was recognized as target for touch events + inline static unsigned blockInputsTokens = 0; // any value > 0 means inputs are blocked inline static std::string commonFooter = ""; diff --git a/library/include/borealis/touch/gesture_recognizer.hpp b/library/include/borealis/touch/gesture_recognizer.hpp new file mode 100644 index 00000000..c22b6b84 --- /dev/null +++ b/library/include/borealis/touch/gesture_recognizer.hpp @@ -0,0 +1,40 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include + +#include + +namespace brls +{ + +typedef std::function GestureRespond; + +class GestureRecognizer +{ +public: + virtual ~GestureRecognizer() { } + virtual bool recognitionLoop(TouchState touch, bool locked) { } + bool enabled = true; + bool success = false; +}; + +}; \ No newline at end of file diff --git a/library/include/borealis/touch/tap_gesture_recognizer.hpp b/library/include/borealis/touch/tap_gesture_recognizer.hpp new file mode 100644 index 00000000..b49f0e19 --- /dev/null +++ b/library/include/borealis/touch/tap_gesture_recognizer.hpp @@ -0,0 +1,40 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include "gesture_recognizer.hpp" + +namespace brls +{ + +class TapGestureRecognizer: public GestureRecognizer +{ +public: + TapGestureRecognizer(GestureRespond respond, int target = 1); + bool recognitionLoop(TouchState touch, bool locked) override; +private: + GestureRespond respond; + double x; + double y; + int counter; + int target; +}; + +}; \ No newline at end of file diff --git a/library/include/borealis/views/box.hpp b/library/include/borealis/views/box.hpp index 3fa162ad..b6bcd9a6 100644 --- a/library/include/borealis/views/box.hpp +++ b/library/include/borealis/views/box.hpp @@ -68,7 +68,7 @@ class Box : public View void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; View* getDefaultFocus() override; - View* getFocus(double x, double y) override; + View* hitTest(double x, double y) override; View* getNextFocus(FocusDirection direction, View* currentView) override; void willAppear(bool resetState) override; void willDisappear(bool resetState) override; diff --git a/library/include/borealis/views/view.hpp b/library/include/borealis/views/view.hpp index 9842c4aa..ab3b7ac6 100644 --- a/library/include/borealis/views/view.hpp +++ b/library/include/borealis/views/view.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -202,6 +203,7 @@ class View bool wireframeEnabled = false; std::vector actions; + std::vector gestureRecognizers; /** * Parent user data, typically the index of the view @@ -1025,6 +1027,15 @@ class View return this->actions; } + const std::vector& getGestureRecognizers() + { + return this->gestureRecognizers; + } + + void addGestureRecognizer(GestureRecognizer* recognizer); + + virtual void gestureRecognizerRequest(TouchState touch, bool locked = false); + /** * Called each frame * Do not override it to draw your view, @@ -1140,6 +1151,11 @@ class View */ virtual View* getDefaultFocus(); + /** + * Returns true if point hits inside this view + */ + virtual bool pointInside(double x, double y); + /** * Returns the view to focus with the corresponding screen coordinates in the view or its children, * or nullptr if it hasn't been found. @@ -1147,7 +1163,7 @@ class View * Research is done recursively by traversing the tree starting from this view. * This view's parents are not traversed. */ - virtual View* getFocus(double x, double y); + virtual View* hitTest(double x, double y); /** * Returns the next view to focus given the requested direction diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index e2d62ac3..0d0f0dff 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -223,20 +223,22 @@ bool Application::mainLoop() TouchManager* touchManager = Application::platform->getTouchManager(); touchManager->updateTouchState(&Application::touchState); - if (Application::touchState.state == END) + switch (Application::touchState.state) { + case START: Logger::info("Touched at X: " + std::to_string(Application::touchState.x) + ", Y: " + std::to_string(Application::touchState.y)); - - Activity* last = Application::activitiesStack[Application::activitiesStack.size() - 1]; - View* nextFocus = last->getContentView()->getFocus(Application::touchState.x, Application::touchState.y); - - if (nextFocus) { - enum Sound focusSound = nextFocus->getFocusSound(); - Application::getAudioPlayer()->play(focusSound); - Application::giveFocus(nextFocus); - } + Application::firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] + ->getContentView()->hitTest(Application::touchState.x, Application::touchState.y); + break; + case NONE: + Application::firstResponder = nullptr; + break; } + if (Application::firstResponder) + { + Application::firstResponder->gestureRecognizerRequest(Application::touchState); + } // Input InputManager* inputManager = Application::platform->getInputManager(); diff --git a/library/lib/touch/tap_gesture_recognizer.cpp b/library/lib/touch/tap_gesture_recognizer.cpp new file mode 100644 index 00000000..ba28d112 --- /dev/null +++ b/library/lib/touch/tap_gesture_recognizer.cpp @@ -0,0 +1,69 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include + +#include + +namespace brls +{ + +TapGestureRecognizer::TapGestureRecognizer(GestureRespond respond, int target) + : target(target), respond(respond) +{ + this->counter = 0; +} + +bool TapGestureRecognizer::recognitionLoop(TouchState touch, bool locked) +{ + if (!enabled) return false; + + switch (touch.state) + { + case START: + this->success = true; + this->x = touch.x; + this->y = touch.y; + break; + case STAY: + if (touch.x != this->x || touch.y != this->y) { + this->success = false; + counter = 0; + } + break; + case END: + if (touch.x == this->x && touch.y == this->y) + this->counter++; + + if (counter >= target) + { + if (respond && !locked) + respond(); + counter = 0; + } + break; + case NONE: + this->success = false; + break; + } + + return this->success; +} + +}; \ No newline at end of file diff --git a/library/lib/views/box.cpp b/library/lib/views/box.cpp index 7efc0aac..02c7703d 100644 --- a/library/lib/views/box.cpp +++ b/library/lib/views/box.cpp @@ -319,18 +319,15 @@ View* Box::getDefaultFocus() return nullptr; } -View* Box::getFocus(double x, double y) +View* Box::hitTest(double x, double y) { // Check if touch fits in view frame - if (getX() <= x && - getWidth() + getX() >= x && - getY() <= y && - getHeight() + getY() >= y) + if (pointInside(x, y)) { Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); for (View* child : this->children) { - View* result = child->getFocus(x, y); + View* result = child->hitTest(x, y); if (result) return result; diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index 59b05b56..acc7d292 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -21,6 +21,7 @@ #include #include +#include namespace brls { @@ -91,6 +92,12 @@ Button::Button() }); this->applyStyle(); + + this->addGestureRecognizer(new TapGestureRecognizer([this]() + { + Logger::debug(describe() + ": Clicked!"); + Application::giveFocus(this); + })); } void Button::applyStyle() diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index df90cf5b..04d44ccc 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -21,6 +21,7 @@ #include #include #include +#include using namespace brls::i18n::literals; @@ -80,6 +81,10 @@ SidebarItem::SidebarItem() return true; }, false, SOUND_CLICK_SIDEBAR); + + this->addGestureRecognizer(new TapGestureRecognizer([this](){ + Application::giveFocus(this); + })); } void SidebarItem::setActive(bool active) diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index 120464db..1e5208cf 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -97,6 +97,28 @@ NVGpaint View::a(NVGpaint paint) return newPaint; } +void View::addGestureRecognizer(GestureRecognizer* recognizer) +{ + this->gestureRecognizers.push_back(recognizer); +} + +void View::gestureRecognizerRequest(TouchState touch, bool locked) +{ + bool lock = false; + + for (GestureRecognizer* recognizer : getGestureRecognizers()) + { + lock |= recognizer->recognitionLoop(touch, locked); + } + + if (getGestureRecognizers().size() == 0) + { + if (parent) + parent->gestureRecognizerRequest(touch, lock); + return; + } +} + void View::frame(FrameContext* ctx) { if (this->visibility != Visibility::VISIBLE) @@ -1289,6 +1311,9 @@ View::~View() for (tinyxml2::XMLDocument* document : this->boundDocuments) delete document; + + for (GestureRecognizer* recognizer : this->gestureRecognizers) + delete recognizer; } std::string View::getStringXMLAttributeValue(std::string value) @@ -2039,7 +2064,16 @@ View* View::getDefaultFocus() return nullptr; } -View* View::getFocus(double x, double y) +bool View::pointInside(double x, double y) +{ + return + getX() <= x && + getWidth() + getX() >= x && + getY() <= y && + getHeight() + getY() >= y; +} + +View* View::hitTest(double x, double y) { // Check if can focus ourself first if (!this->isFocusable()) @@ -2047,13 +2081,10 @@ View* View::getFocus(double x, double y) Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); - if (getX() <= x && - getWidth() + getX() >= x && - getY() <= y && - getHeight() + getY() >= y) + if (pointInside(x, y)) { Logger::debug(describe() + ": OK"); - return getDefaultFocus(); + return this; } return nullptr; diff --git a/library/meson.build b/library/meson.build index e68e4384..559cb00b 100644 --- a/library/meson.build +++ b/library/meson.build @@ -13,6 +13,8 @@ borealis_files = files( 'lib/core/repeating_task.cpp', 'lib/core/platform.cpp', + 'lib/touch/tap_gesture_recognizer.cpp', + 'lib/platforms/glfw/glfw_platform.cpp', 'lib/platforms/glfw/glfw_video.cpp', 'lib/platforms/glfw/glfw_input.cpp', From 3cd8f2726e684535b4d285d255304f3d13faf69d Mon Sep 17 00:00:00 2001 From: XITRIX Date: Thu, 4 Feb 2021 00:22:36 +0300 Subject: [PATCH 04/59] Added scroll with PanGestureRecognizer --- demo/captioned_image.cpp | 4 + library/borealis.mk | 1 + library/include/borealis.hpp | 4 + .../borealis/touch/gesture_recognizer.hpp | 2 - .../borealis/touch/pan_gesture_recognizer.hpp | 70 ++++++++++++++ .../borealis/touch/tap_gesture_recognizer.hpp | 18 ++-- library/lib/core/application.cpp | 2 +- library/lib/touch/pan_gesture_recognizer.cpp | 96 +++++++++++++++++++ library/lib/touch/tap_gesture_recognizer.cpp | 13 +-- library/lib/views/button.cpp | 1 - library/lib/views/scrolling_frame.cpp | 25 +++++ library/lib/views/view.cpp | 10 +- library/meson.build | 1 + 13 files changed, 223 insertions(+), 24 deletions(-) create mode 100644 library/include/borealis/touch/pan_gesture_recognizer.hpp create mode 100644 library/lib/touch/pan_gesture_recognizer.cpp diff --git a/demo/captioned_image.cpp b/demo/captioned_image.cpp index 10671412..a58733e6 100644 --- a/demo/captioned_image.cpp +++ b/demo/captioned_image.cpp @@ -44,6 +44,10 @@ CaptionedImage::CaptionedImage() this->forwardXMLAttribute("imageHeight", this->image, "height"); this->forwardXMLAttribute("caption", this->label, "text"); + + this->addGestureRecognizer(new brls::TapGestureRecognizer([this](){ + brls::Application::giveFocus(this); + })); } void CaptionedImage::onChildFocusGained(brls::View* directChild, brls::View* focusedView) diff --git a/library/borealis.mk b/library/borealis.mk index 5d227590..50ce1678 100644 --- a/library/borealis.mk +++ b/library/borealis.mk @@ -9,6 +9,7 @@ include $(TOPDIR)/$(current_dir)/lib/extern/switch-libpulsar/deps.mk SOURCES := $(SOURCES) \ $(current_dir)/lib/core \ $(current_dir)/lib/views \ + $(current_dir)/lib/touch \ $(current_dir)/lib/platforms/glfw \ $(current_dir)/lib/platforms/switch \ $(current_dir)/lib/extern/glad \ diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index 0ff65c5e..5d4ab7c3 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -52,3 +52,7 @@ #include #include #include + +//Gestures +#include +#include diff --git a/library/include/borealis/touch/gesture_recognizer.hpp b/library/include/borealis/touch/gesture_recognizer.hpp index c22b6b84..596e604f 100644 --- a/library/include/borealis/touch/gesture_recognizer.hpp +++ b/library/include/borealis/touch/gesture_recognizer.hpp @@ -26,8 +26,6 @@ namespace brls { -typedef std::function GestureRespond; - class GestureRecognizer { public: diff --git a/library/include/borealis/touch/pan_gesture_recognizer.hpp b/library/include/borealis/touch/pan_gesture_recognizer.hpp new file mode 100644 index 00000000..08e3eba8 --- /dev/null +++ b/library/include/borealis/touch/pan_gesture_recognizer.hpp @@ -0,0 +1,70 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include "gesture_recognizer.hpp" + +namespace brls +{ + +struct PanGestureCtx; +typedef std::function PanGestureRespond; + +enum class PanAxis +{ + HORIZONTAL, + VERTICAL, + NONE, +}; + +struct PanGestureCtx +{ + TouchClickState state; + double startX; + double startY; + double currentX; + double currentY; + double deltaX; + double deltaY; +}; + +class PanGestureRecognizer: public GestureRecognizer +{ +public: + PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); + bool recognitionLoop(TouchState touch, bool locked) override; + + PanAxis getAxis() const + { + return this->axis; + } +private: + PanGestureRespond respond; + PanAxis axis; + double x; + double y; + double startX; + double startY; + double deltaX; + double deltaY; + bool recognized; +}; + +}; \ No newline at end of file diff --git a/library/include/borealis/touch/tap_gesture_recognizer.hpp b/library/include/borealis/touch/tap_gesture_recognizer.hpp index b49f0e19..14b6ac89 100644 --- a/library/include/borealis/touch/tap_gesture_recognizer.hpp +++ b/library/include/borealis/touch/tap_gesture_recognizer.hpp @@ -21,20 +21,24 @@ #include "gesture_recognizer.hpp" +#define MAX_DELTA_MOVEMENT 10 + namespace brls { +typedef std::function TapGestureRespond; + class TapGestureRecognizer: public GestureRecognizer { public: - TapGestureRecognizer(GestureRespond respond, int target = 1); - bool recognitionLoop(TouchState touch, bool locked) override; + TapGestureRecognizer(TapGestureRespond respond, int target = 1); + bool recognitionLoop(TouchState touch, bool locked) override; private: - GestureRespond respond; - double x; - double y; - int counter; - int target; + TapGestureRespond respond; + double x; + double y; + int counter; + int target; }; }; \ No newline at end of file diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 0d0f0dff..b9d0595e 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -226,7 +226,7 @@ bool Application::mainLoop() switch (Application::touchState.state) { case START: - Logger::info("Touched at X: " + std::to_string(Application::touchState.x) + ", Y: " + std::to_string(Application::touchState.y)); + Logger::debug("Touched at X: " + std::to_string(Application::touchState.x) + ", Y: " + std::to_string(Application::touchState.y)); Application::firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] ->getContentView()->hitTest(Application::touchState.x, Application::touchState.y); break; diff --git a/library/lib/touch/pan_gesture_recognizer.cpp b/library/lib/touch/pan_gesture_recognizer.cpp new file mode 100644 index 00000000..8d5b44eb --- /dev/null +++ b/library/lib/touch/pan_gesture_recognizer.cpp @@ -0,0 +1,96 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include + +namespace brls +{ + +PanGestureRecognizer::PanGestureRecognizer(PanGestureRespond respond, PanAxis axis) + : respond(respond), axis(axis) +{ +} + +bool PanGestureRecognizer::recognitionLoop(TouchState touch, bool locked) +{ + if (!enabled) return false; + + TouchClickState state; + switch (touch.state) + { + case START: + this->recognized = false; + this->success = true; + this->startX = touch.x; + this->startY = touch.y; + this->x = touch.x; + this->y = touch.y; + break; + case STAY: + case END: + deltaX = touch.x - this->x; + deltaY = touch.y - this->y; + + this->x = touch.x; + this->y = touch.y; + + state = touch.state; + + if (!this->recognized && !locked && (deltaX != 0 || deltaY != 0)) + { + switch (axis) + { + case PanAxis::HORIZONTAL: + success = fabs(deltaX) > fabs(deltaY); + break; + case PanAxis::VERTICAL: + success = fabs(deltaX) < fabs(deltaY); + break; + case PanAxis::NONE: + success = true; + break; + } + this->recognized = true; + state = START; + } + + if (respond && recognized && success && !locked) + { + PanGestureCtx result + { + .state = state, + .startX = this->startX, + .startY = this->startY, + .currentX = touch.x, + .currentY = touch.y, + .deltaX = deltaX, + .deltaY = deltaY + }; + this->respond(result); + } + break; + case NONE: + this->success = false; + break; + } + + return this->success; +} + +}; \ No newline at end of file diff --git a/library/lib/touch/tap_gesture_recognizer.cpp b/library/lib/touch/tap_gesture_recognizer.cpp index ba28d112..2843bff8 100644 --- a/library/lib/touch/tap_gesture_recognizer.cpp +++ b/library/lib/touch/tap_gesture_recognizer.cpp @@ -17,14 +17,12 @@ along with this program. If not, see . */ -#include - #include namespace brls { -TapGestureRecognizer::TapGestureRecognizer(GestureRespond respond, int target) +TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, int target) : target(target), respond(respond) { this->counter = 0; @@ -42,19 +40,22 @@ bool TapGestureRecognizer::recognitionLoop(TouchState touch, bool locked) this->y = touch.y; break; case STAY: - if (touch.x != this->x || touch.y != this->y) { + if (fabs(touch.x - this->x) > MAX_DELTA_MOVEMENT || + fabs(touch.y - this->y) > MAX_DELTA_MOVEMENT) { this->success = false; counter = 0; } break; case END: - if (touch.x == this->x && touch.y == this->y) + if (this->success && + fabs(touch.x - this->x) <= MAX_DELTA_MOVEMENT && + fabs(touch.y - this->y) <= MAX_DELTA_MOVEMENT) this->counter++; if (counter >= target) { if (respond && !locked) - respond(); + this->respond(); counter = 0; } break; diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index acc7d292..7ba2cc9b 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -95,7 +95,6 @@ Button::Button() this->addGestureRecognizer(new TapGestureRecognizer([this]() { - Logger::debug(describe() + ": Clicked!"); Application::giveFocus(this); })); } diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 473c3da7..b7c90168 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace brls { @@ -67,6 +68,30 @@ ScrollingFrame::ScrollingFrame() { "natural", ScrollingBehavior::NATURAL }, { "centered", ScrollingBehavior::CENTERED }, }); + + addGestureRecognizer(new PanGestureRecognizer([this](PanGestureCtx pan) + { + float contentHeight = this->getContentHeight(); + + static float startY; + if (pan.state == START) + startY = this->scrollY * contentHeight; + + float newScroll = (startY - (pan.currentY - pan.startY)) / contentHeight; + + // Top boundary + if (newScroll < 0.0f) + newScroll = 0.0f; + + float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; + + // // Bottom boundary + if (newScroll > bottomLimit) + newScroll = bottomLimit; + + //Start animation + this->startScrolling(true, newScroll); + }, PanAxis::NONE)); } void ScrollingFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index 1e5208cf..c0f9efd8 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -104,19 +104,15 @@ void View::addGestureRecognizer(GestureRecognizer* recognizer) void View::gestureRecognizerRequest(TouchState touch, bool locked) { - bool lock = false; + bool lock = locked; for (GestureRecognizer* recognizer : getGestureRecognizers()) { lock |= recognizer->recognitionLoop(touch, locked); } - if (getGestureRecognizers().size() == 0) - { - if (parent) - parent->gestureRecognizerRequest(touch, lock); - return; - } + if (parent) + parent->gestureRecognizerRequest(touch, lock); } void View::frame(FrameContext* ctx) diff --git a/library/meson.build b/library/meson.build index 559cb00b..155fa024 100644 --- a/library/meson.build +++ b/library/meson.build @@ -14,6 +14,7 @@ borealis_files = files( 'lib/core/platform.cpp', 'lib/touch/tap_gesture_recognizer.cpp', + 'lib/touch/pan_gesture_recognizer.cpp', 'lib/platforms/glfw/glfw_platform.cpp', 'lib/platforms/glfw/glfw_video.cpp', From c66fe0607074e0e02bb1cc5a814b06d0614f067e Mon Sep 17 00:00:00 2001 From: XITRIX Date: Thu, 4 Feb 2021 02:00:10 +0300 Subject: [PATCH 05/59] Naming fixes --- library/include/borealis/core/platform.hpp | 2 +- library/include/borealis/core/touch.hpp | 4 ++-- .../borealis/touch/pan_gesture_recognizer.hpp | 2 +- library/lib/core/application.cpp | 4 ++-- library/lib/platforms/glfw/glfw_touch.cpp | 12 ++++++------ library/lib/platforms/switch/switch_touch.cpp | 12 ++++++------ library/lib/touch/pan_gesture_recognizer.cpp | 12 ++++++------ library/lib/touch/tap_gesture_recognizer.cpp | 8 ++++---- library/lib/views/scrolling_frame.cpp | 2 +- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/library/include/borealis/core/platform.hpp b/library/include/borealis/core/platform.hpp index f8194c21..55183d88 100644 --- a/library/include/borealis/core/platform.hpp +++ b/library/include/borealis/core/platform.hpp @@ -68,7 +68,7 @@ class Platform virtual InputManager* getInputManager() = 0; /** - * Returns the InputManager for the platform. + * Returns the TouchManager for the platform. * Cannot return nullptr. */ virtual TouchManager* getTouchManager() = 0; diff --git a/library/include/borealis/core/touch.hpp b/library/include/borealis/core/touch.hpp index e1184513..f9755631 100644 --- a/library/include/borealis/core/touch.hpp +++ b/library/include/borealis/core/touch.hpp @@ -22,7 +22,7 @@ namespace brls { -enum TouchClickState { +enum class TouchEvent { START, STAY, END, @@ -31,7 +31,7 @@ enum TouchClickState { typedef struct TouchState { - TouchClickState state = NONE; + TouchEvent state = TouchEvent::NONE; double x, y; } TouchState; diff --git a/library/include/borealis/touch/pan_gesture_recognizer.hpp b/library/include/borealis/touch/pan_gesture_recognizer.hpp index 08e3eba8..4cbdc548 100644 --- a/library/include/borealis/touch/pan_gesture_recognizer.hpp +++ b/library/include/borealis/touch/pan_gesture_recognizer.hpp @@ -36,7 +36,7 @@ enum class PanAxis struct PanGestureCtx { - TouchClickState state; + TouchEvent state; double startX; double startY; double currentX; diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index b9d0595e..f0e6ea0c 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -225,12 +225,12 @@ bool Application::mainLoop() switch (Application::touchState.state) { - case START: + case TouchEvent::START: Logger::debug("Touched at X: " + std::to_string(Application::touchState.x) + ", Y: " + std::to_string(Application::touchState.y)); Application::firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] ->getContentView()->hitTest(Application::touchState.x, Application::touchState.y); break; - case NONE: + case TouchEvent::NONE: Application::firstResponder = nullptr; break; } diff --git a/library/lib/platforms/glfw/glfw_touch.cpp b/library/lib/platforms/glfw/glfw_touch.cpp index 50ddc368..f4ddaaba 100644 --- a/library/lib/platforms/glfw/glfw_touch.cpp +++ b/library/lib/platforms/glfw/glfw_touch.cpp @@ -44,16 +44,16 @@ void GLFWTouchManager::updateTouchState(TouchState* state) int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); if (clickState == GLFW_PRESS) { - if (oldTouch.state == START || oldTouch.state == STAY) { - state->state = STAY; + if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { + state->state = TouchEvent::STAY; } else { - state->state = START; + state->state = TouchEvent::START; } } else { - if (oldTouch.state == END || oldTouch.state == NONE) { - state->state = NONE; + if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) { + state->state = TouchEvent::NONE; } else { - state->state = END; + state->state = TouchEvent::END; } } oldTouch = *state; diff --git a/library/lib/platforms/switch/switch_touch.cpp b/library/lib/platforms/switch/switch_touch.cpp index 422d3488..c20c62f1 100644 --- a/library/lib/platforms/switch/switch_touch.cpp +++ b/library/lib/platforms/switch/switch_touch.cpp @@ -48,16 +48,16 @@ void SwitchTouchManager::updateTouchState(TouchState* state) state->y = y / Application::windowScale; if (hidState.count > 0) { - if (oldTouch.state == START || oldTouch.state == STAY) { - state->state = STAY; + if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { + state->state = TouchEvent::STAY; } else { - state->state = START; + state->state = TouchEvent::START; } } else { - if (oldTouch.state == END || oldTouch.state == NONE) { - state->state = NONE; + if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) { + state->state = TouchEvent::NONE; } else { - state->state = END; + state->state = TouchEvent::END; } } oldTouch = *state; diff --git a/library/lib/touch/pan_gesture_recognizer.cpp b/library/lib/touch/pan_gesture_recognizer.cpp index 8d5b44eb..6147ab4e 100644 --- a/library/lib/touch/pan_gesture_recognizer.cpp +++ b/library/lib/touch/pan_gesture_recognizer.cpp @@ -31,10 +31,10 @@ bool PanGestureRecognizer::recognitionLoop(TouchState touch, bool locked) { if (!enabled) return false; - TouchClickState state; + TouchEvent state; switch (touch.state) { - case START: + case TouchEvent::START: this->recognized = false; this->success = true; this->startX = touch.x; @@ -42,8 +42,8 @@ bool PanGestureRecognizer::recognitionLoop(TouchState touch, bool locked) this->x = touch.x; this->y = touch.y; break; - case STAY: - case END: + case TouchEvent::STAY: + case TouchEvent::END: deltaX = touch.x - this->x; deltaY = touch.y - this->y; @@ -67,7 +67,7 @@ bool PanGestureRecognizer::recognitionLoop(TouchState touch, bool locked) break; } this->recognized = true; - state = START; + state = TouchEvent::START; } if (respond && recognized && success && !locked) @@ -85,7 +85,7 @@ bool PanGestureRecognizer::recognitionLoop(TouchState touch, bool locked) this->respond(result); } break; - case NONE: + case TouchEvent::NONE: this->success = false; break; } diff --git a/library/lib/touch/tap_gesture_recognizer.cpp b/library/lib/touch/tap_gesture_recognizer.cpp index 2843bff8..9407051d 100644 --- a/library/lib/touch/tap_gesture_recognizer.cpp +++ b/library/lib/touch/tap_gesture_recognizer.cpp @@ -34,19 +34,19 @@ bool TapGestureRecognizer::recognitionLoop(TouchState touch, bool locked) switch (touch.state) { - case START: + case TouchEvent::START: this->success = true; this->x = touch.x; this->y = touch.y; break; - case STAY: + case TouchEvent::STAY: if (fabs(touch.x - this->x) > MAX_DELTA_MOVEMENT || fabs(touch.y - this->y) > MAX_DELTA_MOVEMENT) { this->success = false; counter = 0; } break; - case END: + case TouchEvent::END: if (this->success && fabs(touch.x - this->x) <= MAX_DELTA_MOVEMENT && fabs(touch.y - this->y) <= MAX_DELTA_MOVEMENT) @@ -59,7 +59,7 @@ bool TapGestureRecognizer::recognitionLoop(TouchState touch, bool locked) counter = 0; } break; - case NONE: + case TouchEvent::NONE: this->success = false; break; } diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index b7c90168..5825bd27 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -74,7 +74,7 @@ ScrollingFrame::ScrollingFrame() float contentHeight = this->getContentHeight(); static float startY; - if (pan.state == START) + if (pan.state == TouchEvent::START) startY = this->scrollY * contentHeight; float newScroll = (startY - (pan.currentY - pan.startY)) / contentHeight; From 35277b26ef13a6e923b33c24d43d4beffc37d065 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sat, 13 Feb 2021 02:29:53 +0300 Subject: [PATCH 06/59] Improvements in recognition states and interruption --- library/borealis.mk | 1 - library/include/borealis/core/application.hpp | 7 +- .../borealis/touch/gesture_recognizer.hpp | 19 ++++- .../borealis/touch/pan_gesture_recognizer.hpp | 33 ++++---- .../borealis/touch/tap_gesture_recognizer.hpp | 4 +- library/include/borealis/views/view.hpp | 4 +- library/lib/touch/gesture_recognizer.cpp | 33 ++++++++ library/lib/touch/pan_gesture_recognizer.cpp | 78 ++++++++++--------- library/lib/touch/tap_gesture_recognizer.cpp | 31 ++++---- library/lib/views/scrolling_frame.cpp | 8 +- library/lib/views/view.cpp | 19 +++-- 11 files changed, 150 insertions(+), 87 deletions(-) create mode 100644 library/lib/touch/gesture_recognizer.cpp diff --git a/library/borealis.mk b/library/borealis.mk index 501b1e25..1f92e3dd 100644 --- a/library/borealis.mk +++ b/library/borealis.mk @@ -9,7 +9,6 @@ SOURCES := $(SOURCES) \ $(current_dir)/lib/core \ $(current_dir)/lib/views \ $(current_dir)/lib/touch \ - $(current_dir)/lib/platforms/glfw \ $(current_dir)/lib/platforms/switch \ $(current_dir)/lib/extern/glad \ $(current_dir)/lib/extern/nanovg-deko3d/source \ diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index a0efaf6d..5d0846ca 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -210,6 +210,11 @@ class Application */ static std::string getLocale(); + /** + * View that was recognized as target for touch events. + */ + inline static View* firstResponder; + private: inline static bool inited = false; inline static bool quitRequested = false; @@ -236,8 +241,6 @@ class Application inline static ControllerState controllerState = {}; inline static TouchState touchState = {}; - inline static View* firstResponder; // View that was recognized as target for touch events - inline static unsigned blockInputsTokens = 0; // any value > 0 means inputs are blocked inline static std::string commonFooter = ""; diff --git a/library/include/borealis/touch/gesture_recognizer.hpp b/library/include/borealis/touch/gesture_recognizer.hpp index 596e604f..18dd1bb1 100644 --- a/library/include/borealis/touch/gesture_recognizer.hpp +++ b/library/include/borealis/touch/gesture_recognizer.hpp @@ -26,13 +26,28 @@ namespace brls { +class View; + +enum class GestureState +{ + INTERRUPTED, + UNSURE, + START, + STAY, + END, + FAILED, +}; + class GestureRecognizer { public: virtual ~GestureRecognizer() { } - virtual bool recognitionLoop(TouchState touch, bool locked) { } + virtual GestureState recognitionLoop(TouchState touch, View* view) { } + void interrupt(bool onlyIfUnsureState); bool enabled = true; - bool success = false; + GestureState getState() const { return state; } +protected: + GestureState state; }; }; \ No newline at end of file diff --git a/library/include/borealis/touch/pan_gesture_recognizer.hpp b/library/include/borealis/touch/pan_gesture_recognizer.hpp index 4cbdc548..38a11235 100644 --- a/library/include/borealis/touch/pan_gesture_recognizer.hpp +++ b/library/include/borealis/touch/pan_gesture_recognizer.hpp @@ -21,11 +21,13 @@ #include "gesture_recognizer.hpp" +#define MAX_DELTA_MOVEMENT 10 + namespace brls { -struct PanGestureCtx; -typedef std::function PanGestureRespond; +class PanGestureRecognizer; +typedef std::function PanGestureRespond; enum class PanAxis { @@ -34,37 +36,28 @@ enum class PanAxis NONE, }; -struct PanGestureCtx -{ - TouchEvent state; - double startX; - double startY; - double currentX; - double currentY; - double deltaX; - double deltaY; -}; - class PanGestureRecognizer: public GestureRecognizer { public: PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); - bool recognitionLoop(TouchState touch, bool locked) override; + GestureState recognitionLoop(TouchState touch, View* view) override; - PanAxis getAxis() const - { - return this->axis; - } + double getX() const { return x; } + double getY() const { return y; } + double getStartX() const { return startX; } + double getStartY() const { return startY; } + double getDeltaX() const { return deltaX; } + double getDeltaY() const { return deltaY; } + PanAxis getAxis() const { return this->axis; } private: PanGestureRespond respond; - PanAxis axis; double x; double y; double startX; double startY; double deltaX; double deltaY; - bool recognized; + PanAxis axis; }; }; \ No newline at end of file diff --git a/library/include/borealis/touch/tap_gesture_recognizer.hpp b/library/include/borealis/touch/tap_gesture_recognizer.hpp index 14b6ac89..d7cdcc54 100644 --- a/library/include/borealis/touch/tap_gesture_recognizer.hpp +++ b/library/include/borealis/touch/tap_gesture_recognizer.hpp @@ -21,8 +21,6 @@ #include "gesture_recognizer.hpp" -#define MAX_DELTA_MOVEMENT 10 - namespace brls { @@ -32,7 +30,7 @@ class TapGestureRecognizer: public GestureRecognizer { public: TapGestureRecognizer(TapGestureRespond respond, int target = 1); - bool recognitionLoop(TouchState touch, bool locked) override; + GestureState recognitionLoop(TouchState touch, View* view) override; private: TapGestureRespond respond; double x; diff --git a/library/include/borealis/views/view.hpp b/library/include/borealis/views/view.hpp index 2647d294..0ce4d102 100644 --- a/library/include/borealis/views/view.hpp +++ b/library/include/borealis/views/view.hpp @@ -1043,9 +1043,11 @@ class View return this->gestureRecognizers; } + void interruptGestures(bool onlyIfUnsureState); + void addGestureRecognizer(GestureRecognizer* recognizer); - virtual void gestureRecognizerRequest(TouchState touch, bool locked = false); + virtual void gestureRecognizerRequest(TouchState touch); /** * Called each frame diff --git a/library/lib/touch/gesture_recognizer.cpp b/library/lib/touch/gesture_recognizer.cpp new file mode 100644 index 00000000..7a1b6746 --- /dev/null +++ b/library/lib/touch/gesture_recognizer.cpp @@ -0,0 +1,33 @@ +/* + Borealis, a Nintendo Switch UI Library + Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include + +namespace brls +{ + +void GestureRecognizer::interrupt(bool onlyIfUnsureState) +{ + if (onlyIfUnsureState && this->state != GestureState::UNSURE) + return; + + this->state = GestureState::INTERRUPTED; +} + +} \ No newline at end of file diff --git a/library/lib/touch/pan_gesture_recognizer.cpp b/library/lib/touch/pan_gesture_recognizer.cpp index 6147ab4e..1afde39a 100644 --- a/library/lib/touch/pan_gesture_recognizer.cpp +++ b/library/lib/touch/pan_gesture_recognizer.cpp @@ -27,16 +27,21 @@ PanGestureRecognizer::PanGestureRecognizer(PanGestureRespond respond, PanAxis ax { } -bool PanGestureRecognizer::recognitionLoop(TouchState touch, bool locked) +GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) { - if (!enabled) return false; + if (!enabled) return GestureState::FAILED; + + if (touch.state != TouchEvent::START) + { + if (this->state == GestureState::INTERRUPTED || + this->state == GestureState::FAILED) + return this->state; + } - TouchEvent state; switch (touch.state) { case TouchEvent::START: - this->recognized = false; - this->success = true; + this->state = GestureState::UNSURE; this->startX = touch.x; this->startY = touch.y; this->x = touch.x; @@ -44,53 +49,54 @@ bool PanGestureRecognizer::recognitionLoop(TouchState touch, bool locked) break; case TouchEvent::STAY: case TouchEvent::END: - deltaX = touch.x - this->x; - deltaY = touch.y - this->y; + + this->deltaX = touch.x - this->x; + this->deltaY = touch.y - this->y; this->x = touch.x; this->y = touch.y; - state = touch.state; - - if (!this->recognized && !locked && (deltaX != 0 || deltaY != 0)) + if (this->state == GestureState::UNSURE) { - switch (axis) + if ((fabs(this->startX - touch.x) > MAX_DELTA_MOVEMENT || fabs(this->startY - touch.y) > MAX_DELTA_MOVEMENT)) { - case PanAxis::HORIZONTAL: - success = fabs(deltaX) > fabs(deltaY); - break; - case PanAxis::VERTICAL: - success = fabs(deltaX) < fabs(deltaY); - break; - case PanAxis::NONE: - success = true; - break; + switch (axis) + { + case PanAxis::HORIZONTAL: + if (fabs(deltaX) > fabs(deltaY)) + this->state = GestureState::START; + break; + case PanAxis::VERTICAL: + if (fabs(deltaX) < fabs(deltaY)) + this->state = GestureState::START; + break; + case PanAxis::NONE: + this->state = GestureState::START; + break; + } } - this->recognized = true; - state = TouchEvent::START; + } + else + { + if (touch.state == TouchEvent::STAY) + this->state = GestureState::STAY; + else + this->state = GestureState::END; } - if (respond && recognized && success && !locked) + if (this->state == GestureState::START || + this->state == GestureState::STAY || + this->state == GestureState::END) { - PanGestureCtx result - { - .state = state, - .startX = this->startX, - .startY = this->startY, - .currentX = touch.x, - .currentY = touch.y, - .deltaX = deltaX, - .deltaY = deltaY - }; - this->respond(result); + this->respond(this); } break; case TouchEvent::NONE: - this->success = false; + this->state = GestureState::FAILED; break; } - return this->success; + return this->state; } }; \ No newline at end of file diff --git a/library/lib/touch/tap_gesture_recognizer.cpp b/library/lib/touch/tap_gesture_recognizer.cpp index 9407051d..a479a96c 100644 --- a/library/lib/touch/tap_gesture_recognizer.cpp +++ b/library/lib/touch/tap_gesture_recognizer.cpp @@ -28,43 +28,48 @@ TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, int target this->counter = 0; } -bool TapGestureRecognizer::recognitionLoop(TouchState touch, bool locked) +GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) { - if (!enabled) return false; + if (!enabled) return GestureState::FAILED; + + if (touch.state != TouchEvent::START) + { + if (this->state == GestureState::INTERRUPTED || + this->state == GestureState::FAILED) + return this->state; + } switch (touch.state) { case TouchEvent::START: - this->success = true; + this->state = GestureState::UNSURE; this->x = touch.x; this->y = touch.y; break; case TouchEvent::STAY: - if (fabs(touch.x - this->x) > MAX_DELTA_MOVEMENT || - fabs(touch.y - this->y) > MAX_DELTA_MOVEMENT) { - this->success = false; + if (touch.x < view->getX() || touch.x > view->getX() + view->getWidth() || + touch.y < view->getY() || touch.y > view->getY() + view->getHeight()) + { + this->state = GestureState::FAILED; counter = 0; } break; case TouchEvent::END: - if (this->success && - fabs(touch.x - this->x) <= MAX_DELTA_MOVEMENT && - fabs(touch.y - this->y) <= MAX_DELTA_MOVEMENT) - this->counter++; + this->counter++; if (counter >= target) { - if (respond && !locked) + if (respond) this->respond(); counter = 0; } break; case TouchEvent::NONE: - this->success = false; + this->state = GestureState::FAILED; break; } - return this->success; + return this->state; } }; \ No newline at end of file diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 7edd1f11..cc6a252b 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -67,15 +67,15 @@ ScrollingFrame::ScrollingFrame() this->setMaximumAllowedXMLElements(1); - addGestureRecognizer(new PanGestureRecognizer([this](PanGestureCtx pan) + addGestureRecognizer(new PanGestureRecognizer([this](PanGestureRecognizer* pan) { float contentHeight = this->getContentHeight(); static float startY; - if (pan.state == TouchEvent::START) + if (pan->getState() == GestureState::START) startY = this->scrollY * contentHeight; - float newScroll = (startY - (pan.currentY - pan.startY)) / contentHeight; + float newScroll = (startY - (pan->getY() - pan->getStartY())) / contentHeight; // Top boundary if (newScroll < 0.0f) @@ -89,7 +89,7 @@ ScrollingFrame::ScrollingFrame() //Start animation this->startScrolling(true, newScroll); - }, PanAxis::NONE)); + }, PanAxis::VERTICAL)); } void ScrollingFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index f6751c02..e342efe8 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -97,22 +97,31 @@ NVGpaint View::a(NVGpaint paint) return newPaint; } +void View::interruptGestures(bool onlyIfUnsureState) +{ + for (GestureRecognizer* recognizer : getGestureRecognizers()) + recognizer->interrupt(onlyIfUnsureState); + + if (parent) + parent->interruptGestures(onlyIfUnsureState); +} + void View::addGestureRecognizer(GestureRecognizer* recognizer) { this->gestureRecognizers.push_back(recognizer); } -void View::gestureRecognizerRequest(TouchState touch, bool locked) +void View::gestureRecognizerRequest(TouchState touch) { - bool lock = locked; - for (GestureRecognizer* recognizer : getGestureRecognizers()) { - lock |= recognizer->recognitionLoop(touch, locked); + GestureState state = recognizer->recognitionLoop(touch, this); + if (state == GestureState::START) + Application::firstResponder->interruptGestures(true); } if (parent) - parent->gestureRecognizerRequest(touch, lock); + parent->gestureRecognizerRequest(touch); } void View::frame(FrameContext* ctx) From 67a9501b04a8b01ca9d49e47d063d1bd15bcf455 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sat, 13 Feb 2021 17:27:08 +0300 Subject: [PATCH 07/59] Some review changes and focus hiding an touch --- library/borealis.mk | 2 +- library/include/borealis.hpp | 4 +- library/include/borealis/core/application.hpp | 1 + library/include/borealis/core/input.hpp | 18 +++++ library/include/borealis/core/platform.hpp | 7 -- library/include/borealis/core/touch.hpp | 49 -------------- .../{ => core}/touch/gesture_recognizer.hpp | 2 +- .../touch/pan_gesture_recognizer.hpp | 0 .../touch/tap_gesture_recognizer.hpp | 0 .../borealis/platforms/glfw/glfw_input.hpp | 3 + .../borealis/platforms/glfw/glfw_platform.hpp | 3 - .../borealis/platforms/glfw/glfw_touch.hpp | 43 ------------ .../platforms/switch/switch_input.hpp | 4 ++ .../platforms/switch/switch_platform.hpp | 3 - .../platforms/switch/switch_touch.hpp | 41 ------------ library/include/borealis/views/view.hpp | 2 +- library/lib/core/application.cpp | 13 +++- .../{ => core}/touch/gesture_recognizer.cpp | 2 +- .../touch/pan_gesture_recognizer.cpp | 0 .../touch/tap_gesture_recognizer.cpp | 0 library/lib/platforms/glfw/glfw_input.cpp | 28 ++++++++ library/lib/platforms/glfw/glfw_touch.cpp | 62 ----------------- library/lib/platforms/switch/switch_input.cpp | 35 ++++++++++ .../lib/platforms/switch/switch_platform.cpp | 6 -- library/lib/platforms/switch/switch_touch.cpp | 66 ------------------- library/lib/views/button.cpp | 2 +- library/lib/views/scrolling_frame.cpp | 2 +- library/lib/views/sidebar.cpp | 2 +- library/lib/views/view.cpp | 3 + library/meson.build | 5 +- 30 files changed, 113 insertions(+), 295 deletions(-) delete mode 100644 library/include/borealis/core/touch.hpp rename library/include/borealis/{ => core}/touch/gesture_recognizer.hpp (97%) rename library/include/borealis/{ => core}/touch/pan_gesture_recognizer.hpp (100%) rename library/include/borealis/{ => core}/touch/tap_gesture_recognizer.hpp (100%) delete mode 100644 library/include/borealis/platforms/glfw/glfw_touch.hpp delete mode 100644 library/include/borealis/platforms/switch/switch_touch.hpp rename library/lib/{ => core}/touch/gesture_recognizer.cpp (94%) rename library/lib/{ => core}/touch/pan_gesture_recognizer.cpp (100%) rename library/lib/{ => core}/touch/tap_gesture_recognizer.cpp (100%) delete mode 100644 library/lib/platforms/glfw/glfw_touch.cpp delete mode 100644 library/lib/platforms/switch/switch_touch.cpp diff --git a/library/borealis.mk b/library/borealis.mk index 1f92e3dd..c15a4cc7 100644 --- a/library/borealis.mk +++ b/library/borealis.mk @@ -7,8 +7,8 @@ include $(TOPDIR)/$(current_dir)/lib/extern/switch-libpulsar/deps.mk SOURCES := $(SOURCES) \ $(current_dir)/lib/core \ + $(current_dir)/lib/core/touch \ $(current_dir)/lib/views \ - $(current_dir)/lib/touch \ $(current_dir)/lib/platforms/switch \ $(current_dir)/lib/extern/glad \ $(current_dir)/lib/extern/nanovg-deko3d/source \ diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index ebcae2ac..fac5baea 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -54,5 +54,5 @@ #include //Gestures -#include -#include +#include +#include diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index 5d0846ca..c7f996f4 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -214,6 +214,7 @@ class Application * View that was recognized as target for touch events. */ inline static View* firstResponder; + inline static bool focusTouchMode = false; private: inline static bool inited = false; diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index 3d0f37b5..2f0257a8 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -74,6 +74,19 @@ typedef struct ControllerState float axes[_AXES_MAX]; // from 0.0f to 1.0f } ControllerState; +enum class TouchEvent { + START, + STAY, + END, + NONE, +}; + +typedef struct TouchState +{ + TouchEvent state = TouchEvent::NONE; + double x, y; +} TouchState; + // Interface responsible for reporting input state to the application - button presses, // axis position and touch screen state class InputManager @@ -85,6 +98,11 @@ class InputManager * Called once every frame to fill the given ControllerState struct with the controller state. */ virtual void updateControllerState(ControllerState* state) = 0; + + /** + * Called once every frame to fill the given TouchState struct with the touch state. + */ + virtual void updateTouchState(TouchState* state) = 0; }; }; // namespace brls diff --git a/library/include/borealis/core/platform.hpp b/library/include/borealis/core/platform.hpp index bf030907..772bf3d8 100644 --- a/library/include/borealis/core/platform.hpp +++ b/library/include/borealis/core/platform.hpp @@ -23,7 +23,6 @@ #include #include #include -#include #include namespace brls @@ -94,12 +93,6 @@ class Platform */ virtual InputManager* getInputManager() = 0; - /** - * Returns the TouchManager for the platform. - * Cannot return nullptr. - */ - virtual TouchManager* getTouchManager() = 0; - /** * Returns the FontLoader for the platform. * Cannot return nullptr. diff --git a/library/include/borealis/core/touch.hpp b/library/include/borealis/core/touch.hpp deleted file mode 100644 index f9755631..00000000 --- a/library/include/borealis/core/touch.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#pragma once - -namespace brls -{ - -enum class TouchEvent { - START, - STAY, - END, - NONE, -}; - -typedef struct TouchState -{ - TouchEvent state = TouchEvent::NONE; - double x, y; -} TouchState; - -class TouchManager -{ - public: - virtual ~TouchManager() { } - - /** - * Called once every frame to fill the given TouchState struct with the touch state. - */ - virtual void updateTouchState(TouchState* state) = 0; -}; - -}; // namespace brls \ No newline at end of file diff --git a/library/include/borealis/touch/gesture_recognizer.hpp b/library/include/borealis/core/touch/gesture_recognizer.hpp similarity index 97% rename from library/include/borealis/touch/gesture_recognizer.hpp rename to library/include/borealis/core/touch/gesture_recognizer.hpp index 18dd1bb1..11f40599 100644 --- a/library/include/borealis/touch/gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/gesture_recognizer.hpp @@ -21,7 +21,7 @@ #include -#include +#include namespace brls { diff --git a/library/include/borealis/touch/pan_gesture_recognizer.hpp b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp similarity index 100% rename from library/include/borealis/touch/pan_gesture_recognizer.hpp rename to library/include/borealis/core/touch/pan_gesture_recognizer.hpp diff --git a/library/include/borealis/touch/tap_gesture_recognizer.hpp b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp similarity index 100% rename from library/include/borealis/touch/tap_gesture_recognizer.hpp rename to library/include/borealis/core/touch/tap_gesture_recognizer.hpp diff --git a/library/include/borealis/platforms/glfw/glfw_input.hpp b/library/include/borealis/platforms/glfw/glfw_input.hpp index 6c2d3762..9d3e6234 100644 --- a/library/include/borealis/platforms/glfw/glfw_input.hpp +++ b/library/include/borealis/platforms/glfw/glfw_input.hpp @@ -34,8 +34,11 @@ class GLFWInputManager : public InputManager void updateControllerState(ControllerState* state) override; + void updateTouchState(TouchState* state) override; + private: GLFWwindow* window; + TouchState oldTouch; }; }; diff --git a/library/include/borealis/platforms/glfw/glfw_platform.hpp b/library/include/borealis/platforms/glfw/glfw_platform.hpp index fdcbccbf..09363fe4 100644 --- a/library/include/borealis/platforms/glfw/glfw_platform.hpp +++ b/library/include/borealis/platforms/glfw/glfw_platform.hpp @@ -22,7 +22,6 @@ #include #include #include -#include namespace brls { @@ -43,14 +42,12 @@ class GLFWPlatform : public Platform AudioPlayer* getAudioPlayer() override; VideoContext* getVideoContext() override; InputManager* getInputManager() override; - TouchManager* getTouchManager() override; FontLoader* getFontLoader() override; private: NullAudioPlayer* audioPlayer = nullptr; GLFWVideoContext* videoContext = nullptr; GLFWInputManager* inputManager = nullptr; - GLFWTouchManager* touchManager = nullptr; GLFWFontLoader* fontLoader = nullptr; }; diff --git a/library/include/borealis/platforms/glfw/glfw_touch.hpp b/library/include/borealis/platforms/glfw/glfw_touch.hpp deleted file mode 100644 index 1ca7c4ba..00000000 --- a/library/include/borealis/platforms/glfw/glfw_touch.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#pragma once - -#include - -#define GLFW_INCLUDE_NONE -#include - -namespace brls -{ - -// Input manager for GLFW coursor -class GLFWTouchManager : public TouchManager -{ - public: - GLFWTouchManager(GLFWwindow* window); - - void updateTouchState(TouchState* state) override; - - private: - GLFWwindow* window; - TouchState oldTouch; -}; - -}; \ No newline at end of file diff --git a/library/include/borealis/platforms/switch/switch_input.hpp b/library/include/borealis/platforms/switch/switch_input.hpp index 97148d07..3be2c883 100644 --- a/library/include/borealis/platforms/switch/switch_input.hpp +++ b/library/include/borealis/platforms/switch/switch_input.hpp @@ -1,6 +1,7 @@ /* Borealis, a Nintendo Switch UI Library Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,8 +34,11 @@ class SwitchInputManager : public InputManager void updateControllerState(ControllerState* state); + void updateTouchState(TouchState* state); + private: PadState padState; + TouchState oldTouch; }; } // namespace brls diff --git a/library/include/borealis/platforms/switch/switch_platform.hpp b/library/include/borealis/platforms/switch/switch_platform.hpp index 5671637d..2d4ef519 100644 --- a/library/include/borealis/platforms/switch/switch_platform.hpp +++ b/library/include/borealis/platforms/switch/switch_platform.hpp @@ -23,7 +23,6 @@ #include #include #include -#include namespace brls { @@ -45,7 +44,6 @@ class SwitchPlatform : public Platform VideoContext* getVideoContext() override; AudioPlayer* getAudioPlayer() override; InputManager* getInputManager() override; - TouchManager* getTouchManager() override; FontLoader* getFontLoader() override; void appletCallback(AppletHookType hookType); @@ -59,7 +57,6 @@ class SwitchPlatform : public Platform SwitchAudioPlayer* audioPlayer; SwitchInputManager* inputManager; SwitchVideoContext* videoContext; - SwitchTouchManager* touchManager; SwitchFontLoader* fontLoader; }; diff --git a/library/include/borealis/platforms/switch/switch_touch.hpp b/library/include/borealis/platforms/switch/switch_touch.hpp deleted file mode 100644 index 3f328a13..00000000 --- a/library/include/borealis/platforms/switch/switch_touch.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#pragma once - -#include - -#include - -namespace brls -{ - -// Input manager for Switch touches -class SwitchTouchManager : public TouchManager -{ - public: - SwitchTouchManager(); - - void updateTouchState(TouchState* state) override; - - private: - TouchState oldTouch; -}; - -}; \ No newline at end of file diff --git a/library/include/borealis/views/view.hpp b/library/include/borealis/views/view.hpp index 0ce4d102..3e49f221 100644 --- a/library/include/borealis/views/view.hpp +++ b/library/include/borealis/views/view.hpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 42ec524d..94fe9587 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -167,13 +167,14 @@ bool Application::mainLoop() } // Touch - TouchManager* touchManager = Application::platform->getTouchManager(); - touchManager->updateTouchState(&Application::touchState); + InputManager* inputManager = Application::platform->getInputManager(); + inputManager->updateTouchState(&Application::touchState); switch (Application::touchState.state) { case TouchEvent::START: Logger::debug("Touched at X: " + std::to_string(Application::touchState.x) + ", Y: " + std::to_string(Application::touchState.y)); + Application::focusTouchMode = true; Application::firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] ->getContentView()->hitTest(Application::touchState.x, Application::touchState.y); break; @@ -188,7 +189,6 @@ bool Application::mainLoop() } // Input - InputManager* inputManager = Application::platform->getInputManager(); inputManager->updateControllerState(&Application::controllerState); // Trigger controller events @@ -319,6 +319,13 @@ void Application::onControllerButtonPressed(enum ControllerButton button, bool r if (repeating && Application::repetitionOldFocus == Application::currentFocus) return; + // If touch input mode enabled, disable it and move focus on last view + if (Application::focusTouchMode) { + Application::focusTouchMode = false; + Application::currentFocus->onFocusGained(); + return; + } + Application::repetitionOldFocus = Application::currentFocus; // Actions diff --git a/library/lib/touch/gesture_recognizer.cpp b/library/lib/core/touch/gesture_recognizer.cpp similarity index 94% rename from library/lib/touch/gesture_recognizer.cpp rename to library/lib/core/touch/gesture_recognizer.cpp index 7a1b6746..c8fb1117 100644 --- a/library/lib/touch/gesture_recognizer.cpp +++ b/library/lib/core/touch/gesture_recognizer.cpp @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#include +#include namespace brls { diff --git a/library/lib/touch/pan_gesture_recognizer.cpp b/library/lib/core/touch/pan_gesture_recognizer.cpp similarity index 100% rename from library/lib/touch/pan_gesture_recognizer.cpp rename to library/lib/core/touch/pan_gesture_recognizer.cpp diff --git a/library/lib/touch/tap_gesture_recognizer.cpp b/library/lib/core/touch/tap_gesture_recognizer.cpp similarity index 100% rename from library/lib/touch/tap_gesture_recognizer.cpp rename to library/lib/core/touch/tap_gesture_recognizer.cpp diff --git a/library/lib/platforms/glfw/glfw_input.cpp b/library/lib/platforms/glfw/glfw_input.cpp index 23267cee..627fcf06 100644 --- a/library/lib/platforms/glfw/glfw_input.cpp +++ b/library/lib/platforms/glfw/glfw_input.cpp @@ -1,6 +1,7 @@ /* Borealis, a Nintendo Switch UI Library Copyright (C) 2021 natinusala + Copyright (C) 2021 XITRIX This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,6 +18,7 @@ */ #include +#include #include #define GLFW_INCLUDE_NONE @@ -112,4 +114,30 @@ void GLFWInputManager::updateControllerState(ControllerState* state) } } +void GLFWInputManager::updateTouchState(TouchState* state) +{ + // Get gamepad state + double x, y; + glfwGetCursorPos(this->window, &x, &y); + + state->x = x / Application::windowScale; + state->y = y / Application::windowScale; + + int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); + if (clickState == GLFW_PRESS) { + if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { + state->state = TouchEvent::STAY; + } else { + state->state = TouchEvent::START; + } + } else { + if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) { + state->state = TouchEvent::NONE; + } else { + state->state = TouchEvent::END; + } + } + oldTouch = *state; +} + }; diff --git a/library/lib/platforms/glfw/glfw_touch.cpp b/library/lib/platforms/glfw/glfw_touch.cpp deleted file mode 100644 index f4ddaaba..00000000 --- a/library/lib/platforms/glfw/glfw_touch.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include - -#define GLFW_INCLUDE_NONE -#include - -namespace brls -{ - -GLFWTouchManager::GLFWTouchManager(GLFWwindow* window) - : window(window) -{ - -} - -void GLFWTouchManager::updateTouchState(TouchState* state) -{ - // Get gamepad state - double x, y; - glfwGetCursorPos(this->window, &x, &y); - - state->x = x / Application::windowScale; - state->y = y / Application::windowScale; - - int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); - if (clickState == GLFW_PRESS) { - if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { - state->state = TouchEvent::STAY; - } else { - state->state = TouchEvent::START; - } - } else { - if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) { - state->state = TouchEvent::NONE; - } else { - state->state = TouchEvent::END; - } - } - oldTouch = *state; -} - -}; \ No newline at end of file diff --git a/library/lib/platforms/switch/switch_input.cpp b/library/lib/platforms/switch/switch_input.cpp index 1b35eb19..45039111 100644 --- a/library/lib/platforms/switch/switch_input.cpp +++ b/library/lib/platforms/switch/switch_input.cpp @@ -16,6 +16,7 @@ along with this program. If not, see . */ +#include #include namespace brls @@ -68,4 +69,38 @@ void SwitchInputManager::updateControllerState(ControllerState* state) } } +void SwitchInputManager::updateTouchState(TouchState* state) +{ + // Get gamepad state + double x = oldTouch.x; + double y = oldTouch.y; + + HidTouchScreenState hidState={0}; + if (hidGetTouchScreenStates(&hidState, 1)) + { + if (hidState.count > 0) { + x = hidState.touches[0].x; + y = hidState.touches[0].y; + } + } + + state->x = x / Application::windowScale; + state->y = y / Application::windowScale; + + if (hidState.count > 0) { + if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { + state->state = TouchEvent::STAY; + } else { + state->state = TouchEvent::START; + } + } else { + if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) { + state->state = TouchEvent::NONE; + } else { + state->state = TouchEvent::END; + } + } + oldTouch = *state; +} + } // namespace brls diff --git a/library/lib/platforms/switch/switch_platform.cpp b/library/lib/platforms/switch/switch_platform.cpp index 4d0fea15..c61046cc 100644 --- a/library/lib/platforms/switch/switch_platform.cpp +++ b/library/lib/platforms/switch/switch_platform.cpp @@ -52,7 +52,6 @@ SwitchPlatform::SwitchPlatform() // Init platform impls this->audioPlayer = new SwitchAudioPlayer(); this->inputManager = new SwitchInputManager(); - this->touchManager = new SwitchTouchManager(); this->fontLoader = new SwitchFontLoader(); // Get locale @@ -111,11 +110,6 @@ InputManager* SwitchPlatform::getInputManager() { return this->inputManager; } - -TouchManager* SwitchPlatform::getTouchManager() -{ - return this->touchManager; -} FontLoader* SwitchPlatform::getFontLoader() { diff --git a/library/lib/platforms/switch/switch_touch.cpp b/library/lib/platforms/switch/switch_touch.cpp deleted file mode 100644 index c20c62f1..00000000 --- a/library/lib/platforms/switch/switch_touch.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include - -namespace brls -{ - -SwitchTouchManager::SwitchTouchManager() -{ - -} - -void SwitchTouchManager::updateTouchState(TouchState* state) -{ - // Get gamepad state - double x = oldTouch.x; - double y = oldTouch.y; - - HidTouchScreenState hidState={0}; - if (hidGetTouchScreenStates(&hidState, 1)) - { - if (hidState.count > 0) { - x = hidState.touches[0].x; - y = hidState.touches[0].y; - } - } - - state->x = x / Application::windowScale; - state->y = y / Application::windowScale; - - if (hidState.count > 0) { - if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { - state->state = TouchEvent::STAY; - } else { - state->state = TouchEvent::START; - } - } else { - if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) { - state->state = TouchEvent::NONE; - } else { - state->state = TouchEvent::END; - } - } - oldTouch = *state; -} - -}; \ No newline at end of file diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index 7ba2cc9b..066b02cb 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include namespace brls { diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index cc6a252b..378e991c 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include namespace brls { diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index a3363b20..1ecb6408 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include using namespace brls::literals; diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index e342efe8..d1908b02 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -472,6 +472,9 @@ void View::setAlpha(float alpha) // TODO: Slight glow all around void View::drawHighlight(NVGcontext* vg, Theme theme, float alpha, Style style, bool background) { + if (Application::focusTouchMode) + return; + nvgSave(vg); nvgResetScissor(vg); diff --git a/library/meson.build b/library/meson.build index c9fa6744..a85e03ed 100644 --- a/library/meson.build +++ b/library/meson.build @@ -15,13 +15,12 @@ borealis_files = files( 'lib/core/font.cpp', 'lib/core/util.cpp', - 'lib/touch/tap_gesture_recognizer.cpp', - 'lib/touch/pan_gesture_recognizer.cpp', + 'lib/core/touch/tap_gesture_recognizer.cpp', + 'lib/core/touch/pan_gesture_recognizer.cpp', 'lib/platforms/glfw/glfw_platform.cpp', 'lib/platforms/glfw/glfw_video.cpp', 'lib/platforms/glfw/glfw_input.cpp', - 'lib/platforms/glfw/glfw_touch.cpp', 'lib/platforms/glfw/glfw_font.cpp', 'lib/platforms/switch/swkbd.cpp', # TODO: remove it from build.meson, it's switch only From 380415bd537e3b8cb2b84d6150e152cd3168445d Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sat, 13 Feb 2021 17:47:27 +0300 Subject: [PATCH 08/59] Replaced double with float --- library/include/borealis/core/input.hpp | 4 ++-- .../core/touch/pan_gesture_recognizer.hpp | 24 +++++++++---------- .../core/touch/tap_gesture_recognizer.hpp | 4 ++-- library/lib/platforms/switch/switch_input.cpp | 14 +++++------ 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index 2f0257a8..82a8e7d1 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -83,8 +83,8 @@ enum class TouchEvent { typedef struct TouchState { - TouchEvent state = TouchEvent::NONE; - double x, y; + TouchEvent state; + float x, y; } TouchState; // Interface responsible for reporting input state to the application - button presses, diff --git a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp index 38a11235..e6238c7b 100644 --- a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp @@ -42,21 +42,21 @@ class PanGestureRecognizer: public GestureRecognizer PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); GestureState recognitionLoop(TouchState touch, View* view) override; - double getX() const { return x; } - double getY() const { return y; } - double getStartX() const { return startX; } - double getStartY() const { return startY; } - double getDeltaX() const { return deltaX; } - double getDeltaY() const { return deltaY; } + float getX() const { return x; } + float getY() const { return y; } + float getStartX() const { return startX; } + float getStartY() const { return startY; } + float getDeltaX() const { return deltaX; } + float getDeltaY() const { return deltaY; } PanAxis getAxis() const { return this->axis; } private: PanGestureRespond respond; - double x; - double y; - double startX; - double startY; - double deltaX; - double deltaY; + float x; + float y; + float startX; + float startY; + float deltaX; + float deltaY; PanAxis axis; }; diff --git a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp index d7cdcc54..a638ece6 100644 --- a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp @@ -33,8 +33,8 @@ class TapGestureRecognizer: public GestureRecognizer GestureState recognitionLoop(TouchState touch, View* view) override; private: TapGestureRespond respond; - double x; - double y; + float x; + float y; int counter; int target; }; diff --git a/library/lib/platforms/switch/switch_input.cpp b/library/lib/platforms/switch/switch_input.cpp index 45039111..aa911e1f 100644 --- a/library/lib/platforms/switch/switch_input.cpp +++ b/library/lib/platforms/switch/switch_input.cpp @@ -72,20 +72,20 @@ void SwitchInputManager::updateControllerState(ControllerState* state) void SwitchInputManager::updateTouchState(TouchState* state) { // Get gamepad state - double x = oldTouch.x; - double y = oldTouch.y; + float x = oldTouch.x; + float y = oldTouch.y; HidTouchScreenState hidState={0}; if (hidGetTouchScreenStates(&hidState, 1)) { if (hidState.count > 0) { - x = hidState.touches[0].x; - y = hidState.touches[0].y; + x = hidState.touches[0].x / Application::windowScale; + y = hidState.touches[0].y / Application::windowScale; } } - - state->x = x / Application::windowScale; - state->y = y / Application::windowScale; + + state->x = x; + state->y = y; if (hidState.count > 0) { if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { From 2cc081d830370192ab4691c8127498940bf06eaa Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sat, 13 Feb 2021 23:28:33 +0300 Subject: [PATCH 09/59] Improved touch interactions --- demo/captioned_image.cpp | 2 +- .../core/touch/tap_gesture_recognizer.hpp | 9 +++--- .../lib/core/touch/tap_gesture_recognizer.cpp | 29 +++++++++++-------- library/lib/views/button.cpp | 21 ++++++++++++-- library/lib/views/scrolling_frame.cpp | 3 +- library/lib/views/sidebar.cpp | 13 +++++++-- library/lib/views/view.cpp | 2 +- 7 files changed, 56 insertions(+), 23 deletions(-) diff --git a/demo/captioned_image.cpp b/demo/captioned_image.cpp index a58733e6..ee0d1d84 100644 --- a/demo/captioned_image.cpp +++ b/demo/captioned_image.cpp @@ -45,7 +45,7 @@ CaptionedImage::CaptionedImage() this->forwardXMLAttribute("caption", this->label, "text"); - this->addGestureRecognizer(new brls::TapGestureRecognizer([this](){ + this->addGestureRecognizer(new brls::TapGestureRecognizer([this](auto _){ brls::Application::giveFocus(this); })); } diff --git a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp index a638ece6..b06f43d6 100644 --- a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp @@ -24,19 +24,20 @@ namespace brls { -typedef std::function TapGestureRespond; +class TapGestureRecognizer; +typedef std::function TapGestureRespond; class TapGestureRecognizer: public GestureRecognizer { public: - TapGestureRecognizer(TapGestureRespond respond, int target = 1); + TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); GestureState recognitionLoop(TouchState touch, View* view) override; private: TapGestureRespond respond; float x; float y; - int counter; - int target; + bool callbackOnEndOnly; + GestureState lastState; }; }; \ No newline at end of file diff --git a/library/lib/core/touch/tap_gesture_recognizer.cpp b/library/lib/core/touch/tap_gesture_recognizer.cpp index a479a96c..4d7c5a45 100644 --- a/library/lib/core/touch/tap_gesture_recognizer.cpp +++ b/library/lib/core/touch/tap_gesture_recognizer.cpp @@ -22,10 +22,9 @@ namespace brls { -TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, int target) - : target(target), respond(respond) +TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly) + : respond(respond), callbackOnEndOnly(callbackOnEndOnly) { - this->counter = 0; } GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) @@ -36,7 +35,13 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) { if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) + { + if (respond && !this->callbackOnEndOnly && this->state != lastState) + this->respond(this); + + lastState = this->state; return this->state; + } } switch (touch.state) @@ -45,30 +50,30 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) this->state = GestureState::UNSURE; this->x = touch.x; this->y = touch.y; + + if (respond && !this->callbackOnEndOnly) + this->respond(this); break; case TouchEvent::STAY: if (touch.x < view->getX() || touch.x > view->getX() + view->getWidth() || touch.y < view->getY() || touch.y > view->getY() + view->getHeight()) { this->state = GestureState::FAILED; - counter = 0; + if (respond && !this->callbackOnEndOnly) + this->respond(this); } break; case TouchEvent::END: - this->counter++; - - if (counter >= target) - { - if (respond) - this->respond(); - counter = 0; - } + this->state = GestureState::END; + if (respond) + this->respond(this); break; case TouchEvent::NONE: this->state = GestureState::FAILED; break; } + lastState = this->state; return this->state; } diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index 066b02cb..3c3c86f5 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -93,10 +93,27 @@ Button::Button() this->applyStyle(); - this->addGestureRecognizer(new TapGestureRecognizer([this]() + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) { Application::giveFocus(this); - })); + for (auto& action : getActions()) + { + if (action.button != static_cast(BUTTON_A)) + continue; + + if (action.available) { + this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); + + if (recogniser->getState() != GestureState::END) + return; + + if (action.actionListener(this)) + { + Application::getAudioPlayer()->play(action.sound); + } + } + } + }, false)); } void Button::applyStyle() diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 378e991c..f051d73f 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -243,7 +243,8 @@ void ScrollingFrame::scrollAnimationTick() void ScrollingFrame::onChildFocusGained(View* directChild, View* focusedView) { // Start scrolling - this->updateScrolling(true); + if (!Application::focusTouchMode) + this->updateScrolling(true); Box::onChildFocusGained(directChild, focusedView); } diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index 1ecb6408..507e56d6 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -82,9 +82,18 @@ SidebarItem::SidebarItem() }, false, SOUND_CLICK_SIDEBAR); - this->addGestureRecognizer(new TapGestureRecognizer([this](){ + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) + { + if (this->active) + return; + + this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); + if (recogniser->getState() != GestureState::END) + return; + Application::giveFocus(this); - })); + Application::getAudioPlayer()->play(SOUND_CLICK_SIDEBAR); + }, false)); } void SidebarItem::setActive(bool active) diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index d1908b02..811abdec 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -212,7 +212,7 @@ void View::playClickAnimation(bool reverse) menu_animation_ctx_entry_t entry; entry.cb = [this, reverse](void* userdata) { - if (reverse) + if (reverse || Application::focusTouchMode) return; this->playClickAnimation(true); From 1f0b0e9d6bf0095ebc983b93509498155563b047 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sun, 14 Feb 2021 00:25:19 +0300 Subject: [PATCH 10/59] Meson fix --- library/include/borealis/core/touch/gesture_recognizer.hpp | 3 +-- library/lib/core/touch/gesture_recognizer.cpp | 5 +++++ library/lib/platforms/glfw/glfw_platform.cpp | 6 ------ library/meson.build | 1 + 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/library/include/borealis/core/touch/gesture_recognizer.hpp b/library/include/borealis/core/touch/gesture_recognizer.hpp index 11f40599..919b6757 100644 --- a/library/include/borealis/core/touch/gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/gesture_recognizer.hpp @@ -41,8 +41,7 @@ enum class GestureState class GestureRecognizer { public: - virtual ~GestureRecognizer() { } - virtual GestureState recognitionLoop(TouchState touch, View* view) { } + virtual GestureState recognitionLoop(TouchState touch, View* view); void interrupt(bool onlyIfUnsureState); bool enabled = true; GestureState getState() const { return state; } diff --git a/library/lib/core/touch/gesture_recognizer.cpp b/library/lib/core/touch/gesture_recognizer.cpp index c8fb1117..1ee69286 100644 --- a/library/lib/core/touch/gesture_recognizer.cpp +++ b/library/lib/core/touch/gesture_recognizer.cpp @@ -22,6 +22,11 @@ namespace brls { +GestureState GestureRecognizer::recognitionLoop(TouchState touch, View* view) +{ + return GestureState::FAILED; +} + void GestureRecognizer::interrupt(bool onlyIfUnsureState) { if (onlyIfUnsureState && this->state != GestureState::UNSURE) diff --git a/library/lib/platforms/glfw/glfw_platform.cpp b/library/lib/platforms/glfw/glfw_platform.cpp index d7954b37..649c94df 100644 --- a/library/lib/platforms/glfw/glfw_platform.cpp +++ b/library/lib/platforms/glfw/glfw_platform.cpp @@ -58,7 +58,6 @@ void GLFWPlatform::createWindow(std::string windowTitle, uint32_t windowWidth, u { this->videoContext = new GLFWVideoContext(windowTitle, windowWidth, windowHeight); this->inputManager = new GLFWInputManager(this->videoContext->getGLFWWindow()); - this->touchManager = new GLFWTouchManager(this->videoContext->getGLFWWindow()); } std::string GLFWPlatform::getName() @@ -96,11 +95,6 @@ InputManager* GLFWPlatform::getInputManager() { return this->inputManager; } - -TouchManager* GLFWPlatform::getTouchManager() -{ - return this->touchManager; -} FontLoader* GLFWPlatform::getFontLoader() { diff --git a/library/meson.build b/library/meson.build index a85e03ed..7734259c 100644 --- a/library/meson.build +++ b/library/meson.build @@ -15,6 +15,7 @@ borealis_files = files( 'lib/core/font.cpp', 'lib/core/util.cpp', + 'lib/core/touch/gesture_recognizer.cpp', 'lib/core/touch/tap_gesture_recognizer.cpp', 'lib/core/touch/pan_gesture_recognizer.cpp', From e06f7ef3cc2fac7854b5e415661870fc7c9509bd Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sun, 14 Feb 2021 02:42:00 +0300 Subject: [PATCH 11/59] Improved touch mode disabling --- library/include/borealis/core/application.hpp | 8 +++++- library/lib/core/application.cpp | 27 +++++++++++++------ library/lib/views/scrolling_frame.cpp | 9 +++---- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index c7f996f4..e409f6d4 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -214,7 +214,13 @@ class Application * View that was recognized as target for touch events. */ inline static View* firstResponder; - inline static bool focusTouchMode = false; + inline static bool focusTouchMode = false; + + /** + * If touch input mode enabled, disable it and move focus on last view + * Returns true if touch mode was disabled. + */ + static bool dismissTouchMode(); private: inline static bool inited = false; diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 94fe9587..3c65a79b 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -251,6 +251,9 @@ void Application::quit() void Application::navigate(FocusDirection direction) { + if (Application::dismissTouchMode()) + return; + View* currentFocus = Application::currentFocus; // Do nothing if there is no current focus @@ -319,15 +322,8 @@ void Application::onControllerButtonPressed(enum ControllerButton button, bool r if (repeating && Application::repetitionOldFocus == Application::currentFocus) return; - // If touch input mode enabled, disable it and move focus on last view - if (Application::focusTouchMode) { - Application::focusTouchMode = false; - Application::currentFocus->onFocusGained(); - return; - } - Application::repetitionOldFocus = Application::currentFocus; - + // Actions if (Application::handleAction(button)) return; @@ -354,6 +350,16 @@ void Application::onControllerButtonPressed(enum ControllerButton button, bool r } } +bool Application::dismissTouchMode() +{ + if (Application::focusTouchMode) { + Application::focusTouchMode = false; + Application::currentFocus->onFocusGained(); + return true; + } + return false; +} + View* Application::getCurrentFocus() { return Application::currentFocus; @@ -361,6 +367,10 @@ View* Application::getCurrentFocus() bool Application::handleAction(char button) { + if (button == BUTTON_A && + Application::dismissTouchMode()) + return false; + if (Application::activitiesStack.empty()) return false; @@ -382,6 +392,7 @@ bool Application::handleAction(char button) if (action.available) { + if (action.actionListener(hintParent)) { if (button == BUTTON_A) diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index f051d73f..5f300bb7 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -76,17 +76,16 @@ ScrollingFrame::ScrollingFrame() startY = this->scrollY * contentHeight; float newScroll = (startY - (pan->getY() - pan->getStartY())) / contentHeight; - - // Top boundary - if (newScroll < 0.0f) - newScroll = 0.0f; - float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; // // Bottom boundary if (newScroll > bottomLimit) newScroll = bottomLimit; + // Top boundary + if (newScroll < 0.0f) + newScroll = 0.0f; + //Start animation this->startScrolling(true, newScroll); }, PanAxis::VERTICAL)); From 009e9ad22e980cc1cc737f0603ea84cb3fdd0863 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sun, 14 Feb 2021 14:19:55 +0300 Subject: [PATCH 12/59] made focusTouchMode private set --- library/include/borealis/core/application.hpp | 10 ++++++++-- library/lib/views/scrolling_frame.cpp | 2 +- library/lib/views/view.cpp | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index e409f6d4..a8f9583b 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -213,14 +213,18 @@ class Application /** * View that was recognized as target for touch events. */ - inline static View* firstResponder; - inline static bool focusTouchMode = false; + inline static View* firstResponder; /** * If touch input mode enabled, disable it and move focus on last view * Returns true if touch mode was disabled. */ static bool dismissTouchMode(); + + inline static bool getFocusTouchMode() + { + return focusTouchMode; + } private: inline static bool inited = false; @@ -247,6 +251,8 @@ class Application inline static ControllerState oldControllerState = {}; inline static ControllerState controllerState = {}; inline static TouchState touchState = {}; + + inline static bool focusTouchMode = false; inline static unsigned blockInputsTokens = 0; // any value > 0 means inputs are blocked diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 5f300bb7..08a16c3d 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -242,7 +242,7 @@ void ScrollingFrame::scrollAnimationTick() void ScrollingFrame::onChildFocusGained(View* directChild, View* focusedView) { // Start scrolling - if (!Application::focusTouchMode) + if (!Application::getFocusTouchMode()) this->updateScrolling(true); Box::onChildFocusGained(directChild, focusedView); diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index 811abdec..8d548129 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -212,7 +212,7 @@ void View::playClickAnimation(bool reverse) menu_animation_ctx_entry_t entry; entry.cb = [this, reverse](void* userdata) { - if (reverse || Application::focusTouchMode) + if (reverse || Application::getFocusTouchMode()) return; this->playClickAnimation(true); @@ -472,7 +472,7 @@ void View::setAlpha(float alpha) // TODO: Slight glow all around void View::drawHighlight(NVGcontext* vg, Theme theme, float alpha, Style style, bool background) { - if (Application::focusTouchMode) + if (Application::getFocusTouchMode()) return; nvgSave(vg); From a5342fb8fb3367ce658ab747074b3dde5b378c79 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sun, 14 Feb 2021 18:59:09 +0300 Subject: [PATCH 13/59] Added scroll acceleration --- .../core/touch/pan_gesture_recognizer.hpp | 21 ++++++- .../borealis/views/scrolling_frame.hpp | 1 + .../lib/core/touch/pan_gesture_recognizer.cpp | 34 ++++++++++- library/lib/views/scrolling_frame.cpp | 61 ++++++++++++++----- resources/i18n/en-US/demo.json | 1 + resources/i18n/en-US/scroll.json | 3 + resources/xml/activity/main.xml | 4 ++ resources/xml/tabs/scroll_test.xml | 29 +++++++++ 8 files changed, 133 insertions(+), 21 deletions(-) create mode 100644 resources/i18n/en-US/scroll.json create mode 100644 resources/xml/tabs/scroll_test.xml diff --git a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp index e6238c7b..eb4af548 100644 --- a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp @@ -21,8 +21,6 @@ #include "gesture_recognizer.hpp" -#define MAX_DELTA_MOVEMENT 10 - namespace brls { @@ -36,6 +34,20 @@ enum class PanAxis NONE, }; +struct position +{ + float x; + float y; +}; + +struct pan_acceleration +{ + float distanceX; + float distanceY; + float timeX; + float timeY; +}; + class PanGestureRecognizer: public GestureRecognizer { public: @@ -49,6 +61,7 @@ class PanGestureRecognizer: public GestureRecognizer float getDeltaX() const { return deltaX; } float getDeltaY() const { return deltaY; } PanAxis getAxis() const { return this->axis; } + pan_acceleration getAcceleration() const { return this->acceleration; } private: PanGestureRespond respond; float x; @@ -58,6 +71,8 @@ class PanGestureRecognizer: public GestureRecognizer float deltaX; float deltaY; PanAxis axis; + pan_acceleration acceleration; + std::vector posHistory; }; -}; \ No newline at end of file +}; diff --git a/library/include/borealis/views/scrolling_frame.hpp b/library/include/borealis/views/scrolling_frame.hpp index 9e1f451a..1e92c2da 100644 --- a/library/include/borealis/views/scrolling_frame.hpp +++ b/library/include/borealis/views/scrolling_frame.hpp @@ -84,6 +84,7 @@ class ScrollingFrame : public Box void prebakeScrolling(); bool updateScrolling(bool animated); void startScrolling(bool animated, float newScroll); + void animateScrolling(float newScroll, float time); void scrollAnimationTick(); float getScrollingAreaTopBoundary(); diff --git a/library/lib/core/touch/pan_gesture_recognizer.cpp b/library/lib/core/touch/pan_gesture_recognizer.cpp index 1afde39a..d28532b3 100644 --- a/library/lib/core/touch/pan_gesture_recognizer.cpp +++ b/library/lib/core/touch/pan_gesture_recognizer.cpp @@ -19,6 +19,11 @@ #include +#define FPS 60.0f // TODO: get real FPS +#define MAX_DELTA_MOVEMENT 10 +#define HISTORY_LIMIT 3 +#define PAN_SCROLL_ACCELERATION -5000 + namespace brls { @@ -41,6 +46,7 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) switch (touch.state) { case TouchEvent::START: + this->posHistory.clear(); this->state = GestureState::UNSURE; this->startX = touch.x; this->startY = touch.y; @@ -58,7 +64,8 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) if (this->state == GestureState::UNSURE) { - if ((fabs(this->startX - touch.x) > MAX_DELTA_MOVEMENT || fabs(this->startY - touch.y) > MAX_DELTA_MOVEMENT)) + if (fabs(this->startX - touch.x) > MAX_DELTA_MOVEMENT || + fabs(this->startY - touch.y) > MAX_DELTA_MOVEMENT) { switch (axis) { @@ -83,6 +90,23 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) else this->state = GestureState::END; } + + if (this->state == GestureState::END) + { + float time = posHistory.size() / FPS; + + float distanceX = posHistory[posHistory.size()].x - posHistory[0].x; + float distanceY = posHistory[posHistory.size()].y - posHistory[0].y; + + float velocityX = distanceX / time; + float velocityY = distanceY / time; + + acceleration.timeX = -fabs(velocityX) / PAN_SCROLL_ACCELERATION; + acceleration.timeY = -fabs(velocityY) / PAN_SCROLL_ACCELERATION; + + acceleration.distanceX = velocityX * acceleration.timeX / 2; + acceleration.distanceY = velocityY * acceleration.timeY / 2; + } if (this->state == GestureState::START || this->state == GestureState::STAY || @@ -90,13 +114,19 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) { this->respond(this); } + break; case TouchEvent::NONE: this->state = GestureState::FAILED; break; } + + posHistory.insert(posHistory.begin(), position{ .x = this->x, .y = this->y }); + while (posHistory.size() > HISTORY_LIMIT) { + posHistory.pop_back(); + } return this->state; } -}; \ No newline at end of file +}; diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 08a16c3d..b797aca7 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -87,7 +87,27 @@ ScrollingFrame::ScrollingFrame() newScroll = 0.0f; //Start animation - this->startScrolling(true, newScroll); + if (pan->getState() != GestureState::END) + startScrolling(true, newScroll); + else + { + newScroll = (this->scrollY * contentHeight + pan->getAcceleration().distanceY) / contentHeight; + + float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; + + // // Bottom boundary + if (newScroll > bottomLimit) + newScroll = bottomLimit; + + // Top boundary + if (newScroll < 0.0f) + newScroll = 0.0f; + + if (newScroll == this->scrollY) + return; + + animateScrolling(newScroll, pan->getAcceleration().timeY * 1000.0f); + } }, PanAxis::VERTICAL)); } @@ -193,30 +213,39 @@ void ScrollingFrame::startScrolling(bool animated, float newScroll) if (newScroll == this->scrollY) return; - menu_animation_ctx_tag tag = (menu_animation_ctx_tag) & this->scrollY; - menu_animation_kill_by_tag(&tag); - if (animated) { Style style = Application::getStyle(); - - menu_animation_ctx_entry_t entry; - entry.cb = [](void* userdata) {}; - entry.duration = style["brls/animations/highlight"]; - entry.easing_enum = EASING_OUT_QUAD; - entry.subject = &this->scrollY; - entry.tag = tag; - entry.target_value = newScroll; - entry.tick = [this](void* userdata) { this->scrollAnimationTick(); }; - entry.userdata = nullptr; - - menu_animation_push(&entry); + animateScrolling(newScroll, style["brls/animations/highlight"]); } else { + menu_animation_ctx_tag tag = (menu_animation_ctx_tag) & this->scrollY; + menu_animation_kill_by_tag(&tag); + this->scrollY = newScroll; + this->invalidate(); } +} + +void ScrollingFrame::animateScrolling(float newScroll, float time) +{ + menu_animation_ctx_tag tag = (menu_animation_ctx_tag) & this->scrollY; + menu_animation_kill_by_tag(&tag); + + menu_animation_ctx_entry_t entry; + entry.cb = [](void* userdata) {}; + entry.duration = time; + entry.easing_enum = EASING_OUT_QUAD; + entry.subject = &this->scrollY; + entry.tag = tag; + entry.target_value = newScroll; + entry.tick = [this](void* userdata) { this->scrollAnimationTick(); }; + entry.userdata = nullptr; + + menu_animation_push(&entry); + this->invalidate(); } diff --git a/resources/i18n/en-US/demo.json b/resources/i18n/en-US/demo.json index 3d67f225..a9d71b98 100644 --- a/resources/i18n/en-US/demo.json +++ b/resources/i18n/en-US/demo.json @@ -3,6 +3,7 @@ "tabs": { "components": "Basic components", + "scroll": "Scroll test", "layout": "Layout and alignment", "recycling": "Recycling lists", "popups": "Popups, notifications and dialogs", diff --git a/resources/i18n/en-US/scroll.json b/resources/i18n/en-US/scroll.json new file mode 100644 index 00000000..44f02621 --- /dev/null +++ b/resources/i18n/en-US/scroll.json @@ -0,0 +1,3 @@ +{ + "text": "Очень длинный-длинный текст :)\nПредисловие: Наконец появилась возможность добраться до интернета, сейчас мы находимся в Панамском канале и здесь есть wifi. Я на судне уже больше месяца и пока я здесь, я писал все интересное что здесь происходит и вот наконец есть возможность этим поделиться. Фотографий пока не будет, их я выложу или позже, или уже когда вернусь домой. Итак, понеслась:\n\n\nПервые впечатления\n\nПереночевав в гостинице в Гуаякиле, мы сели к агенту в машину и поехали на судно в Пуэрто Боливар. Доехали вопреки ожиданиям быстро, примерно за 3-4 часа. Погода была пасмурная и даже не смотря на то, что мы находимся недалеко от экватора, было прохладно. Почти все время, пока мы ехали, по обе стороны дороги были банановые плантации, но все равно в голове не укладывается: эти бананы грузят на суда в нескольких портах Эквадора десятками тысяч тонн каждый день, круглый год. Это ж несчастные бананы должны расти быстрее чем грибы.\n\nДороги.\nДороги в Эквадоре практически идеальные, хотя населенные пункты выглядят очень бедно. На дорогах много интересных машин, например очень много грузовиков - древних Фордов, которые я никогда раньше не видел. А еще несколько раз на глаза попадались старенькие Жигули :) А еще если кого-то обгоняешь и есть встречная машина, она обязательно включает фары. На больших машинах - грузовиках и автобусах, обязательно красуется местный тюнинг: машины разукрашенные, либо в наклейках, и обязательно везде огромное множество светодиодов, как будто новогодние елки едут и переливаются всеми цветами.\n\nСудно.\nНа первый взгляд судно неплохое, в относительно хорошем состоянии, хотя и 92 года постройки. Экипаж 19 человек - 11 русских и 8 филиппинцев, включая повара. Говорят, периодически становится тоскливо от егошних кулинарных изысков. Филиппинцы здесь рядовой состав, за ними постоянно нужно следить чтобы не натворили чего, среди них только один матрос по-настоящему ответственный и с руками из нужного места, все понимает с полуслова. Остальные - типичные Равшаны да Джамшуты. А еще один из них - гомосек О___о, в добавок к этому он опасный человек, в том плане, что легко впадает в состояние ступора и отключает мозг: был случай как он закрыл одного матроса в трюме, тот орал и тарабанил внутри, это заметил боцман, начал орать на этого персонажа, который, в свою очередь испуганно выпучив глаза, трясущимися руками продолжал закручивать барашки. В итоге боцман его отодвинул и выпустил матроса из трюма. Общение на английском языке, но из-за акцента не всегда с первого раз понятно что филиппинцы говорят, особенно по рации. Напимер, говорит он тебе: Бикарпуль! Бикарпуль! А потом, когда уже поздно, выясняется что это было \"Be careful!\"\n\nРабота.\nСразу, как только мы заселились, я не успел разложить вещи, как в мою голову ворвался такой поток информации, что ни в сказке сказать, ни топором не вырубить. Во-первых, на судне абсолютно все бумаги - мануалы, журналы, и так далее - все на английском языке. Даже блокнотик, в который записываются отчеты по грузовым операциям - и тот на английском. Бумаги... ооооо... Их тысячи, лежат в сотнях папок, плюс огромное количество документов на компьютерах. Это мне просто разорвало мозг в клочья, потому что с этим объемом информации надо ознакомиться и научиться работать в кротчайшие сроки. Постоянная беготня, постоянная суета, совсем не легко. А также надо как можно быстрее разобраться со всем оборудованием на мостике, а там его мама не горюй. В общем, пока что, свободного времени нет вообще. Абсолютно. Только ночью с 00:00 до 06:00 можно поспать. Но это продлится не долго, буквально 1-2 недели, потом океанский переход до Европы, можно будет уже спокойно стоять вахты, а в свободное время читать книги компании Seatrade, на случай если в Европе придет проверка и будет задавать вопросы.\n\nНу и немного о приятном.\nНеплохая одноместная каюта. Внутри несколько шкафов и полок, удобная кровать, койка напередохнуть, стол, стул, умывальник и внутрисудовой телефон. Также выдали 2 белых офицерских комбинезона с символикой компании, каску и персональную рацию. В моем распоряжении офицерский душ со стиральной машинкой и офицерская столовая. Во время швартовых операций мое место на мостике. Хотя и матросы на палубе не сильно напрягаются - там установлены гидравлические лебедки, не надо швартовные концы тягать вручную.\n\nНа этом пока все, фоток пока что нет, потому что просто некогда этим заниматься. Даже этот текст я пишу потому что просто появилось свободных 2 часа, отпустили отдохнуть, так как впереди 6-часовая вахта, а сразу после нее отшвартовка и идем в Гуаякиль, а во время отшвартовки и пока лоцман на судне - мое место на мостике. Так что, предстоит основательно заколебаться.\n\n8 августа 2013г. Пуэрто Боливар, Эквадор.\n\n\n\nРабота.\n\nЯ на судне уже почти 3 недели, все потихоньку начинает раскладываться по полочкам и становится понятным. Из Пуэрто Боливара мы пошли в Гуаякиль (Эквадор), а оттуда в Паиту (Перу). В этих портах мы загружались бананами и контейнерами. Потом мы взяли курс на Европу. Чтобы не делать огромный крюк вокруг Южной Америки, мы пошли в Панамский канал.\n\nПанамский канал.\nВозле входа в канал находится якорная стоянка, где куча судов стоит и ждет своей очереди на проход. Но, нам дали добро на проход практически без ожидания и постояв на якоре всего три часа, приехал лоцман, скомандовал \"dead slow ahead\" (самый малый вперед), я перевел ручку машинного телеграфа, похожую на автомобильный рычаг автоматической коробки передач в указанное положение, на что телеграф отозвался громким сигналом. Стрелка тахометра поползла вверх, судно слегка задрожало и мы медленно но верно начали набирать скорость и двигаться в сторону канала. Вход в канал со стороны океана выделен буями - десятки буев, выстроенные в две линии показывают единственный верный путь, выгладит грандиозно. А справа, за пальмами и дорогими виллами со стоящими рядом с ними роскошными яхтами, простирется огромный город небоскребов - Панама. А через десять минут открылось еще одно не менее грандиозное зрелище - строительство новых шлюзов, которые будут пропускать суда бОльших размеров, чем нынешние шлюзы. Но тут впереди из-за поворота показалась огромная стена, которая двигалась в нашу сторону. Навстречу нам шло судно-автомобилевоз Green Cove и так как канал - место совсем не широкое, автомобилевоз прошел от нас всего в 50 метрах, непривычно было стоя на мостике наблюдать столь близкое расхождение. А вот и буксиры. Это значит что мы подходим к шлюзам. Только мы подошли к шлюзу, как тут же нас привязали к резвым локомотивам - Мулам. Дальше все зависело только от них. 4 Мула держали нас с разных сторон и уже они руководили процессом, они нас резво разгоняли и останавливали в нужном месте и терпеливо ждали пока шлюз не наполнится водой. Так как наше судно не очень широкое, проблем с заходом в шлюз не было, а по длине получилось так, что за нами сзади поместилось еще одно небольшое судно. Правда мы оказались более быстроходными и пока мы шли от тихоокеанских до атлантических шлюзов по каналу и озеру Гатун, это судно безнадежно отстало, но нам пришлось его ждать :(\n\nАтлантический океан.\nПроходя мимо Бермудских островов, мы попали под влияние местной погоды - то светит солнце и стоит невыносимое пекло, то идет огромная туча и под ней стена ливня, сквозь которую ничего не видно, то все небо усыпано облаками разных форм, оттенков и размеров и сияет радуга. Интересно, в общем. Но потом все это прошло и была ясная жаркая погода почти без ветра. Вода гладкая и почти без волн, как будто в озере. Я себе Атлантический океан представлял иначе. Но через несколько дней атмосферное давление упало, поднялся ветер и нас начало валять с борта на борт. В общем весело, но не тогда, когда надо что-то делать. Например, надо точку на карту нанести, возьмешь линейку и тут эээх! Поехало все по столу и карандаш убежал. Или можно случайно ту же точку просто поставить совсем не туда, куда нужно. Но ничего, привыкли. Правда в душ было страшно ходить - там можно было легко убиться с такой качкой. И вот, мы пришли в Дувр (Великобритания). При заходе в порт открывается отличный вид: белые отвесные скалы огромной высоты, а наверху зеленые холмы. На одном из холмов располагается крепость , которая в данный момент является музеем. Эх, жаль что у нас была короткая стоянка, меньше суток. Так и не получилось сойти на берег. Но мы сейчас на линии, так что мы еще сюда вернемся, и не один раз. Паромы. Они большие и их много. Паромное сообщение с материком очень оживленное, примерно каждые 10-15 минут приходит или отходит очередной огромный паром, кое-как нашли интервал чтобы проскочить в порт и встать к причалу. Прошел день, грузовые операции по данному порту завершены и уже пора отчаливать и идти в следующий порт - Гамбург, Германия. Всего меньше суток переход и мы уже подходим к очередному порту. Гамбург находится на реке Эльба, в нескольких часах хода от моря. Пока шли до города, по обе стороны был приятный европейский пейзаж - аккуратные домики с мягким освещением, парки, беговые дорожки, все выглядит очень гармонично и уютно. На берегу пляжик с каяками, и даже прохладным вечером на пляже отдыхали люди в куртках, человек 30-50. Оставалось только смотреть и вздыхать. А чуть позже показался завод, с первого взгляда совсем неприметный, хоть и не маленький. А когда подошли чуть ближе, я увидел на стене логотип и надпись Airbus. Как сказал лоцман, это один из главных заводов Airbus в мире. И да, здание завода застеклено и со стороны реки было хорошо видно самолеты внутри. Точнее, самолеты в процессе сборки. У некоторых корпус еще желто-зеленого цвета, у других уже нанесена раскраска. В Гамбурге мы стояли так же как и в Дувре - всего день, тоже не удалось погулять, печально. И вот мы выгрузили часть бананов, разгрузили контейнера, погрузили новые и отправились в путь. Идем в Роттердам, Нидерланды (Голландия). Также переход меньше суток.\n\nРоттердам.\nВ этом городе случилось то, что я так долго ждал - наконец-то получилось сойти на берег. Город относительно чистый, хотя периодически мусор попадается, но совсем немного. Много парков, да и вообще много зелени. Много велосипедов, они везде, припаркованы почти у каждого дома, даже возде некоторых домов есть специальные маленькие гаражи для велосипедов, а вдоль всех дорог есть велосипедные дорожки с разделительной полосой и даже с собственными светофорами. Из водного транспорта есть водное такси и водный автобус. Живность. Я не видел ни одной кошки, зато по городским улицам бегают зайцы. :) На первых этажах домов в Роттердаме отсутствуют решетки, выглядит непривычно и опять же наталкивает на грустные мысли. Решили поменять немного денег и чтобы найти обменник обратились за помощью к местным жителям. Сидят два человека около 60 лет, спросили у них. Пока один объяснял дорогу, второй, со смугловатым лицом изъеденным морщинами, достал из кармана какие-то прибамбасы и начал их раскладывать: специальная коробочка была наполовину заполнена марихуаной, он насыпал ее на уже подготовленную бумажку и ловко завернул косячок размером чуть меньше обычной сигареты, улыбнулся ртом, в котором количество зубов можно было пересчитать по пальцам, и закурил. И да, мы там несколько раз видели магазины, которые нельзя встретить практически больше нигде в мире. Пора возвращаться на судно. Прыгнули в водный автобус и понеслись в сторону нашего причалм - Waalhaven. А на судне полным ходом идет погрузка. В одних трюмах уже стоят машины и экскаваторы, в другие грузят ящики с напитками и картошку. На крышки трюмов поставили еще пару экскаваторов и грузовиков, а также какие-то металлические конструкции. А на верхнюю палубу погрузили контейнеры, 60 штук. Почти все это добро мы повезем в Парамарибо, Суринам. В Роттердаме мы простояли почти 3 дня и вот, погрузка завершена и по причалу в нашу сторону идет человек в спасательном жилете и с чемоданом. Это лоцман. Я его проводил на мостик, включил ходовые огни, поставил на автоматической идентификационной системе (АИС) статус \"Under way using engine\" и вбил туда следующий порт назначения, поднял флаг \"Hotel\", обозначающий присутствие лоцмана на борту, согласовал АИС и радары с гирокомпасом и... мы поехали. Впереди нас ждет 10-дневный переход через Атлантический океан из Роттердама в Парамарибо.\n\n31 августа 2013г. Английский канал.\n\n\nОдин вечер.\nПодъезжая к морю на машине, либо подходя пешком, всегда замечаешь знакомый аромат и в голове появляется обычная мысль, которая иногда произносится вслух: Морем пахнет. А как пахнет море там, далеко-далеко, где не видно берегов, где под тобой глубина несколько километров? Или океан, где вокруг даже других судов не видно? Да никак оно не пахнет, там просто очень чистый воздух, очень легко дышится, но нет никаких запахов. Или запахи есть, но появились они на судне - то покрасят что-то и стоит резкий духан ацетоновой краски, то повар на камбузе готовит какую-нибудь вкусняшку и дразнящие ароматы ползут по коридорам. А морем не пахнет. Как так? Нет, я не принюхался и не привык к запаху моря, его на самом деле нет. И он появляется когда мы подходим к берегу. На самом деле пахнет не морем, это благоухают водоросли выброшенные на берег и еще что-нибудь вместе с ними. А звуки? в городе слышно суету, машины, людей и прочие шумы, в лесу - пенье птиц и шелест листьев, а в море? Особенно когда штиль и вода спокойная? А в море слышно работу главного двигателя. Не громко, находясь в каюте лишь слышно низкий гул, но его практически не замечаешь. Зато если спуститься в машинное отделение, вот там его прекрасно слышно - и гул, и свист турбины, и прочие шумы, которые в сумме своей громкостью напоминают запуск космической ракеты, к соплам которой тебя привязали ухом, чтоб лучше слышно было. Разговаривать в машинном отделении почти невозможно, с трудом удается разобрать слова когда тебе собеседник изо всех сил орет прямо в ухо. Поэтому там все носят шумозащитные наушники и не разговаривают :) Ну да ладно, вернемся на верхнюю палубу. А там стоят рефконтейнеры, в них находятся рефрежираторные установки для охлаждения содержимого и эти установки постоянно жужжат своими вентиляторами. Громко жужжат, разговаривать можно, а вот рацию можно и не услышать. Но можно пройти на бак (носовая часть судна) и там ничего этого практически не слышно - только шум ветра и шелест воды, которую рассекает нос судна. А еще говорят, мол, хочешь узнать где берег - ищи облака. Ерунда это. Атлантический океан, сотни, даже тысячи миль до берегов и... все небо в облаках, но не сплошная серая масса, а такое разнообразие, такое буйство красок, что диву даешься. Вечером пришел на вахту, время без пятнадцати восемь, а на небе такое... будто у Создателя разыгралась муза и он еле смог остановиться. Эту красоту сложно представить и невозможно передать словами. Горело все небо, всеми цветами, солнце медленно приближалось к горизонту, унося с собой очередной день и уступая место теплой ночи. (Да, здесь жнем жарко а ночью хорошо. Посередине атлантики вода ярко-синего цвета и ее температура +28 градусов, глубина около 3-5 километров). Слева недалеко от нас появилось какое-то движение - это стая дельфинов, весело выпрыгивая из воды спешит куда-то по своим важным дельфиньим делам. Смотрю, на радаре впереди появилась точка, милях в 5. Ну думаю, наверное помеха. Визуально ничего не видно. Смотрю на второй радар - там тоже есть засвет. Тааак, уже интересно, беру бинокль и... ничего не вижу. На обоих радарах точка приближается, причем сигнал уже идет четкий, явно не мусор засветился. Даже буй бы светился на радаре не так ярко. Проходит какое-то время, радары показывают объект в 3 милях от нас, двигается со скоростю 3 узла. Опять смотрю в бинокль - и ничего. Дельфины тоже были недалеко, но их радар не видел, так как опять же слишком мелкий объект. А тут вот оно, светится и никуда не девается. Кратчайшая дистанция была около двух миль, я так ничего и не увидел. Иногда радар бъет облака, но это выглядит абсолютно по-другому. Так что, что это было я не знаю. Вахта прошла спокойно, вокруг никого и ничего, глубина большая, бояться нечего. Вот уже без десяти двенадцать, на мостике скрипнула дверь и с хмурым лицом зашел принимать вахту третий помощник. Не выспался, видимо. А еще сегодня на его вахте переводятся часы на час назад, это значит что стоять ему на час больше. А мне на час больше спать :).\n-Все тихо-спокойно?\n-Да, как обычно.\n-Это радует (Егор улыбнулся, видимо мозг начал просыпаться)\nВремя без пяти, надо делать запись в судовой журнал, стою, пишу. И тут в ночной тиши из УКВ-радиостанции раздается смех. А мы уже несколько дней в эфире ничего не слышали - судов нету, а если и попадаются, то говорить с ними не о чем, всегда расходимя молча. Мы переглянулись, пожали плечами. Я продолжил писать в журнал, а Егор пошел смотреть радар, кто это там хулиганит. В этот момент из рации снова: хахааа-ха-ха-хаа... Мда, бывает же. Уже не обращаем внимания. И тут снова раздается смех и так совпало, что в этот момент с лампы подсведки стола, на котором я заполнял журнал, срывается висевшая на ней линейка и неожиданным шлепком падает прямо перед моим носом на журнал. \"Сука!!!\" - невольно вырвалось из меня. Как я себе в штаны не нагадил, я не знаю. Егор тоже вздрогнул от неожиданности, немая пауза. Молча вешаю линейку назад и через несколько секунд мы начинаем ржать. Ладно, задержусь немного, мы давно собирались настроить факсимильный приемник погоды, который тихонько стоит в радиорубке и не понятно, работает он вообще или нет. Включили. На приемнике загорелись лампочки. О, уже неплохо. Нашли частоты метеостанций, настроились на ближайшую и стали ждать. Потом решили оставить это дело на ночь, а утром проверить, авось чего примется. В итоге, через 12 часов мы вспомнили про приемник и пошли проверять. Всю ночь он принимал и распечатывал помехи и оттуда вылезли не погодные карты, а пять метров пестрых обоев. Решили пока эксперименты с погодным приемником приостановить и перенести на попозже, когда берега будут ближе. Все равно сейчас погода принимается специальной программой через спутниковый интернет.\n\n4 сентября 2013г. Атлантический океан." +} diff --git a/resources/xml/activity/main.xml b/resources/xml/activity/main.xml index 8045a4ba..666e2211 100644 --- a/resources/xml/activity/main.xml +++ b/resources/xml/activity/main.xml @@ -6,6 +6,10 @@ + + + + diff --git a/resources/xml/tabs/scroll_test.xml b/resources/xml/tabs/scroll_test.xml new file mode 100644 index 00000000..5ce0627d --- /dev/null +++ b/resources/xml/tabs/scroll_test.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + From 1314ea43864880d804bec3150fc1707b911ada9b Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sun, 14 Feb 2021 21:19:41 +0300 Subject: [PATCH 14/59] Added animation time crop on top and bottom boundaries --- library/lib/views/scrolling_frame.cpp | 32 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index b797aca7..512b2041 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -78,7 +78,7 @@ ScrollingFrame::ScrollingFrame() float newScroll = (startY - (pan->getY() - pan->getStartY())) / contentHeight; float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; - // // Bottom boundary + // Bottom boundary if (newScroll > bottomLimit) newScroll = bottomLimit; @@ -86,27 +86,35 @@ ScrollingFrame::ScrollingFrame() if (newScroll < 0.0f) newScroll = 0.0f; - //Start animation + // Start animation if (pan->getState() != GestureState::END) startScrolling(true, newScroll); else { - newScroll = (this->scrollY * contentHeight + pan->getAcceleration().distanceY) / contentHeight; + float time = pan->getAcceleration().timeY * 1000.0f; + float newPos = this->scrollY * contentHeight + pan->getAcceleration().distanceY; - float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; + // Bottom boundary + float bottomLimit = contentHeight - this->getScrollingAreaHeight(); + if (newPos > bottomLimit) + { + time = time * (1 - fabs(newPos - bottomLimit) / fabs(pan->getAcceleration().distanceY)); + newPos = bottomLimit; + } - // // Bottom boundary - if (newScroll > bottomLimit) - newScroll = bottomLimit; - // Top boundary - if (newScroll < 0.0f) - newScroll = 0.0f; + if (newPos < 0) + { + time = time * (1 - fabs(newPos) / fabs(pan->getAcceleration().distanceY)); + newPos = 0; + } + + newScroll = newPos / contentHeight; - if (newScroll == this->scrollY) + if (newScroll == this->scrollY || time < 100) return; - animateScrolling(newScroll, pan->getAcceleration().timeY * 1000.0f); + animateScrolling(newScroll, time); } }, PanAxis::VERTICAL)); } From f35f81a1ec74f080672a17b2df788733deb8373a Mon Sep 17 00:00:00 2001 From: XITRIX Date: Tue, 16 Feb 2021 20:35:10 +0300 Subject: [PATCH 15/59] Added touch focus sound --- library/include/borealis/core/audio.hpp | 5 ++-- .../platforms/switch/switch_audio.hpp | 2 +- .../include/pulsar/player/player.h | 6 +++++ .../switch-libpulsar/src/player/player.c | 26 +++++++++++++++++++ library/lib/platforms/switch/switch_audio.cpp | 4 ++- library/lib/views/button.cpp | 17 ++++++++---- library/lib/views/sidebar.cpp | 18 ++++++++++--- 7 files changed, 65 insertions(+), 13 deletions(-) diff --git a/library/include/borealis/core/audio.hpp b/library/include/borealis/core/audio.hpp index 6c4a8ee2..7b93203d 100644 --- a/library/include/borealis/core/audio.hpp +++ b/library/include/borealis/core/audio.hpp @@ -35,6 +35,7 @@ enum Sound SOUND_CLICK_ERROR, // played when the user clicks a disabled button / a view focused with no click action SOUND_HONK, // honk SOUND_CLICK_SIDEBAR, // played when a sidebar item is clicked + SOUND_TOUCH_UNFOCUS, // played when touch focus has been interrupted _SOUND_MAX, // not an actual sound, just used to count of many sounds there are }; @@ -62,7 +63,7 @@ class AudioPlayer * * Returns a boolean indicating if the sound has been played or not. */ - virtual bool play(enum Sound sound) = 0; + virtual bool play(enum Sound sound, float pitch = 1) = 0; }; // An AudioPlayer that does nothing @@ -74,7 +75,7 @@ class NullAudioPlayer : public AudioPlayer return false; } - bool play(enum Sound sound) override + bool play(enum Sound sound, float pitch) override { return false; } diff --git a/library/include/borealis/platforms/switch/switch_audio.hpp b/library/include/borealis/platforms/switch/switch_audio.hpp index 4403e584..fc076e49 100644 --- a/library/include/borealis/platforms/switch/switch_audio.hpp +++ b/library/include/borealis/platforms/switch/switch_audio.hpp @@ -34,7 +34,7 @@ class SwitchAudioPlayer : public AudioPlayer ~SwitchAudioPlayer(); bool load(enum Sound sound) override; - bool play(enum Sound sound) override; + bool play(enum Sound sound, float pitch) override; private: bool init = false; diff --git a/library/lib/extern/switch-libpulsar/include/pulsar/player/player.h b/library/lib/extern/switch-libpulsar/include/pulsar/player/player.h index ff40ef14..2917c8ff 100644 --- a/library/lib/extern/switch-libpulsar/include/pulsar/player/player.h +++ b/library/lib/extern/switch-libpulsar/include/pulsar/player/player.h @@ -81,3 +81,9 @@ PLSR_RC plsrPlayerStop(PLSR_PlayerSoundId id); /// Free ressources used by a loaded sound void plsrPlayerFree(PLSR_PlayerSoundId id); + +/// Set sound pitch factor (effective next time it's played) +PLSR_RC plsrPlayerSetPitch(PLSR_PlayerSoundId id, float pitch); + +/// Set sound volume factor (effective next time it's played) +PLSR_RC plsrPlayerSetVolume(PLSR_PlayerSoundId id, float volume); diff --git a/library/lib/extern/switch-libpulsar/src/player/player.c b/library/lib/extern/switch-libpulsar/src/player/player.c index 90f50f14..10256bf5 100644 --- a/library/lib/extern/switch-libpulsar/src/player/player.c +++ b/library/lib/extern/switch-libpulsar/src/player/player.c @@ -156,4 +156,30 @@ void plsrPlayerFree(PLSR_PlayerSoundId id) { free(sound); } +PLSR_RC plsrPlayerSetPitch(PLSR_PlayerSoundId id, float pitch) { + if(id == PLSR_PLAYER_INVALID_SOUND) { + return _LOCAL_RC_MAKE(BadInput); + } + + PLSR_PlayerSound* sound = (PLSR_PlayerSound*)id; + for(unsigned int i = 0; i < sound->channelCount; i++) { + audrvVoiceSetPitch(&g_instance->driver, sound->channels[i].voiceId, pitch); + } + + return PLSR_RC_OK; +} + +PLSR_RC plsrPlayerSetVolume(PLSR_PlayerSoundId id, float volume) { + if(id == PLSR_PLAYER_INVALID_SOUND) { + return _LOCAL_RC_MAKE(BadInput); + } + + PLSR_PlayerSound* sound = (PLSR_PlayerSound*)id; + for(unsigned int i = 0; i < sound->channelCount; i++) { + audrvVoiceSetVolume(&g_instance->driver, sound->channels[i].voiceId, volume); + } + + return PLSR_RC_OK; +} + #endif diff --git a/library/lib/platforms/switch/switch_audio.cpp b/library/lib/platforms/switch/switch_audio.cpp index 8a64e45f..21bc3232 100644 --- a/library/lib/platforms/switch/switch_audio.cpp +++ b/library/lib/platforms/switch/switch_audio.cpp @@ -36,6 +36,7 @@ const std::string SOUNDS_MAP[_SOUND_MAX] = { "SeKeyError", // SOUND_CLICK_ERROR "SeUnlockKeyZR", // SOUND_HONK "SeNaviDecide", // SOUND_CLICK_SIDEBAR + "SeTouchUnfocus", //SOUND_TOUCH_UNFOCUS }; SwitchAudioPlayer::SwitchAudioPlayer() @@ -106,7 +107,7 @@ bool SwitchAudioPlayer::load(enum Sound sound) return true; } -bool SwitchAudioPlayer::play(enum Sound sound) +bool SwitchAudioPlayer::play(enum Sound sound, float pitch) { if (!this->init) return false; @@ -122,6 +123,7 @@ bool SwitchAudioPlayer::play(enum Sound sound) } // Play the sound + plsrPlayerSetPitch(this->sounds[sound], pitch); PLSR_RC rc = plsrPlayerPlay(this->sounds[sound]); if (PLSR_RC_FAILED(rc)) { diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index 3c3c86f5..69b38ee9 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -104,12 +104,19 @@ Button::Button() if (action.available) { this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); - if (recogniser->getState() != GestureState::END) - return; - - if (action.actionListener(this)) + switch (recogniser->getState()) { - Application::getAudioPlayer()->play(action.sound); + case GestureState::UNSURE: + Application::getAudioPlayer()->play(SOUND_FOCUS_CHANGE); + break; + case GestureState::FAILED: + case GestureState::INTERRUPTED: + Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); + break; + case GestureState::END: + if (action.actionListener(this)) + Application::getAudioPlayer()->play(action.sound); + break; } } } diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index 507e56d6..3aedff4b 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -88,11 +88,21 @@ SidebarItem::SidebarItem() return; this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); - if (recogniser->getState() != GestureState::END) - return; - Application::giveFocus(this); - Application::getAudioPlayer()->play(SOUND_CLICK_SIDEBAR); + switch (recogniser->getState()) + { + case GestureState::UNSURE: + Application::getAudioPlayer()->play(SOUND_FOCUS_SIDEBAR); + break; + case GestureState::FAILED: + case GestureState::INTERRUPTED: + Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); + break; + case GestureState::END: + Application::getAudioPlayer()->play(SOUND_CLICK_SIDEBAR); + Application::giveFocus(this); + break; + } }, false)); } From b924c51b05437d940c6d92eee92b196d83675894 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Tue, 16 Feb 2021 23:53:53 +0300 Subject: [PATCH 16/59] Added touch sounds --- demo/captioned_image.cpp | 1 + library/include/borealis/core/audio.hpp | 1 + .../borealis/core/touch/gesture_recognizer.hpp | 1 + .../core/touch/tap_gesture_recognizer.hpp | 4 +++- library/include/borealis/views/view.hpp | 9 ++++++++- library/lib/core/application.cpp | 7 ++++++- library/lib/core/touch/gesture_recognizer.cpp | 5 +++++ library/lib/core/touch/tap_gesture_recognizer.cpp | 15 ++++++++++----- library/lib/platforms/switch/switch_audio.cpp | 3 ++- library/lib/views/button.cpp | 2 ++ library/lib/views/sidebar.cpp | 4 +++- library/lib/views/view.cpp | 8 ++++++-- 12 files changed, 48 insertions(+), 12 deletions(-) diff --git a/demo/captioned_image.cpp b/demo/captioned_image.cpp index ee0d1d84..960d0ccc 100644 --- a/demo/captioned_image.cpp +++ b/demo/captioned_image.cpp @@ -47,6 +47,7 @@ CaptionedImage::CaptionedImage() this->addGestureRecognizer(new brls::TapGestureRecognizer([this](auto _){ brls::Application::giveFocus(this); + return true; })); } diff --git a/library/include/borealis/core/audio.hpp b/library/include/borealis/core/audio.hpp index 7b93203d..3b54bb14 100644 --- a/library/include/borealis/core/audio.hpp +++ b/library/include/borealis/core/audio.hpp @@ -36,6 +36,7 @@ enum Sound SOUND_HONK, // honk SOUND_CLICK_SIDEBAR, // played when a sidebar item is clicked SOUND_TOUCH_UNFOCUS, // played when touch focus has been interrupted + SOUND_TOUCH, // played when touch not requires it's own click sound _SOUND_MAX, // not an actual sound, just used to count of many sounds there are }; diff --git a/library/include/borealis/core/touch/gesture_recognizer.hpp b/library/include/borealis/core/touch/gesture_recognizer.hpp index 919b6757..69b789e8 100644 --- a/library/include/borealis/core/touch/gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/gesture_recognizer.hpp @@ -42,6 +42,7 @@ class GestureRecognizer { public: virtual GestureState recognitionLoop(TouchState touch, View* view); + virtual bool soundOnTouch(); void interrupt(bool onlyIfUnsureState); bool enabled = true; GestureState getState() const { return state; } diff --git a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp index b06f43d6..0f8eb40b 100644 --- a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp @@ -25,15 +25,17 @@ namespace brls { class TapGestureRecognizer; -typedef std::function TapGestureRespond; +typedef std::function TapGestureRespond; class TapGestureRecognizer: public GestureRecognizer { public: TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); GestureState recognitionLoop(TouchState touch, View* view) override; + bool soundOnTouch() override; private: TapGestureRespond respond; + bool playSound; float x; float y; bool callbackOnEndOnly; diff --git a/library/include/borealis/views/view.hpp b/library/include/borealis/views/view.hpp index 3e49f221..43c9d954 100644 --- a/library/include/borealis/views/view.hpp +++ b/library/include/borealis/views/view.hpp @@ -1047,7 +1047,14 @@ class View void addGestureRecognizer(GestureRecognizer* recognizer); - virtual void gestureRecognizerRequest(TouchState touch); + /** + * Called each frame when touch is registered. + * + * @return false if there is no recognizer + * that needs to play it's own click sound, + * so play default is allowed + */ + bool gestureRecognizerRequest(TouchState touch); /** * Called each frame diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 3c65a79b..f99070e0 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -185,7 +185,12 @@ bool Application::mainLoop() if (Application::firstResponder) { - Application::firstResponder->gestureRecognizerRequest(Application::touchState); + if (Application::firstResponder->gestureRecognizerRequest(Application::touchState)) + { + // Play touch sound with random pitch + float pitch = (rand() % 10) / 10.0f + 1.0f; + Application::getAudioPlayer()->play(SOUND_TOUCH, pitch); + } } // Input diff --git a/library/lib/core/touch/gesture_recognizer.cpp b/library/lib/core/touch/gesture_recognizer.cpp index 1ee69286..480dcd04 100644 --- a/library/lib/core/touch/gesture_recognizer.cpp +++ b/library/lib/core/touch/gesture_recognizer.cpp @@ -35,4 +35,9 @@ void GestureRecognizer::interrupt(bool onlyIfUnsureState) this->state = GestureState::INTERRUPTED; } +bool GestureRecognizer::soundOnTouch() +{ + return true; +} + } \ No newline at end of file diff --git a/library/lib/core/touch/tap_gesture_recognizer.cpp b/library/lib/core/touch/tap_gesture_recognizer.cpp index 4d7c5a45..332332b6 100644 --- a/library/lib/core/touch/tap_gesture_recognizer.cpp +++ b/library/lib/core/touch/tap_gesture_recognizer.cpp @@ -23,7 +23,7 @@ namespace brls { TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly) - : respond(respond), callbackOnEndOnly(callbackOnEndOnly) + : respond(respond), callbackOnEndOnly(callbackOnEndOnly), playSound(true) { } @@ -37,7 +37,7 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) this->state == GestureState::FAILED) { if (respond && !this->callbackOnEndOnly && this->state != lastState) - this->respond(this); + this->playSound = this->respond(this); lastState = this->state; return this->state; @@ -52,7 +52,7 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) this->y = touch.y; if (respond && !this->callbackOnEndOnly) - this->respond(this); + this->playSound = this->respond(this); break; case TouchEvent::STAY: if (touch.x < view->getX() || touch.x > view->getX() + view->getWidth() || @@ -60,13 +60,13 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) { this->state = GestureState::FAILED; if (respond && !this->callbackOnEndOnly) - this->respond(this); + this->playSound = this->respond(this); } break; case TouchEvent::END: this->state = GestureState::END; if (respond) - this->respond(this); + this->playSound = this->respond(this); break; case TouchEvent::NONE: this->state = GestureState::FAILED; @@ -77,4 +77,9 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) return this->state; } +bool TapGestureRecognizer::soundOnTouch() +{ + return this->playSound; +} + }; \ No newline at end of file diff --git a/library/lib/platforms/switch/switch_audio.cpp b/library/lib/platforms/switch/switch_audio.cpp index 21bc3232..d8c941fe 100644 --- a/library/lib/platforms/switch/switch_audio.cpp +++ b/library/lib/platforms/switch/switch_audio.cpp @@ -36,7 +36,8 @@ const std::string SOUNDS_MAP[_SOUND_MAX] = { "SeKeyError", // SOUND_CLICK_ERROR "SeUnlockKeyZR", // SOUND_HONK "SeNaviDecide", // SOUND_CLICK_SIDEBAR - "SeTouchUnfocus", //SOUND_TOUCH_UNFOCUS + "SeTouchUnfocus", // SOUND_TOUCH_UNFOCUS + "SeTouch", // SOUND_TOUCH }; SwitchAudioPlayer::SwitchAudioPlayer() diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index 69b38ee9..b08527db 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -118,8 +118,10 @@ Button::Button() Application::getAudioPlayer()->play(action.sound); break; } + return false; } } + return true; }, false)); } diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index 3aedff4b..2d56ad0b 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -85,7 +85,7 @@ SidebarItem::SidebarItem() this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) { if (this->active) - return; + return true; this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); @@ -103,6 +103,8 @@ SidebarItem::SidebarItem() Application::giveFocus(this); break; } + + return false; }, false)); } diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index 8d548129..8b7a57b6 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -111,17 +111,21 @@ void View::addGestureRecognizer(GestureRecognizer* recognizer) this->gestureRecognizers.push_back(recognizer); } -void View::gestureRecognizerRequest(TouchState touch) +bool View::gestureRecognizerRequest(TouchState touch) { + bool res = touch.state == TouchEvent::START; for (GestureRecognizer* recognizer : getGestureRecognizers()) { GestureState state = recognizer->recognitionLoop(touch, this); + if (res) res &= recognizer->enabled && recognizer->soundOnTouch(); if (state == GestureState::START) Application::firstResponder->interruptGestures(true); } if (parent) - parent->gestureRecognizerRequest(touch); + res &= parent->gestureRecognizerRequest(touch); + + return res; } void View::frame(FrameContext* ctx) From f853c27b788f1f4aa26a8618d1f367faa4e10972 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sat, 20 Feb 2021 17:58:08 +0300 Subject: [PATCH 17/59] Fix view shadow on focus in touch mode. --- library/lib/views/view.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lib/views/view.cpp b/library/lib/views/view.cpp index 8b7a57b6..9ef31b41 100644 --- a/library/lib/views/view.cpp +++ b/library/lib/views/view.cpp @@ -153,7 +153,7 @@ void View::frame(FrameContext* ctx) this->drawBackground(ctx->vg, ctx, style); // Draw shadow - if (this->shadowType != ShadowType::NONE && this->showShadow) + if (this->shadowType != ShadowType::NONE && (this->showShadow || Application::getFocusTouchMode())) this->drawShadow(ctx->vg, ctx, style, x, y, width, height); // Draw border From 8030aef5a5f37c1ff78abaf014d7eca696374072 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sat, 20 Feb 2021 19:05:35 +0300 Subject: [PATCH 18/59] Moved first responder from public header --- library/include/borealis/core/application.hpp | 5 ----- library/include/borealis/core/view.hpp | 2 +- library/lib/core/application.cpp | 9 +++++---- library/lib/core/view.cpp | 6 +++--- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index 56b21c85..039b7bd3 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -205,11 +205,6 @@ class Application * Returns the current system locale. */ static std::string getLocale(); - - /** - * View that was recognized as target for touch events. - */ - inline static View* firstResponder; /** * If touch input mode enabled, disable it and move focus on last view diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index 1251ca53..a57c1f37 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -1052,7 +1052,7 @@ class View * that needs to play it's own click sound, * so play default is allowed */ - bool gestureRecognizerRequest(TouchState touch); + bool gestureRecognizerRequest(TouchState touch, View* firstResponder); /** * Called each frame diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 43d4a96c..9b34e347 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -155,6 +155,7 @@ void Application::createWindow(std::string windowTitle) bool Application::mainLoop() { static ControllerState oldControllerState = {}; + static View* firstResponder; // Main loop callback if (!Application::platform->mainLoopIteration() || Application::quitRequested) @@ -177,18 +178,18 @@ bool Application::mainLoop() case TouchEvent::START: Logger::debug("Touched at X: " + std::to_string(touchState.x) + ", Y: " + std::to_string(touchState.y)); Application::focusTouchMode = true; - Application::firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] + firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] ->getContentView()->hitTest(touchState.x, touchState.y); break; case TouchEvent::NONE: - Application::firstResponder = nullptr; + firstResponder = nullptr; break; default: break; } - if (Application::firstResponder) + if (firstResponder) { - if (Application::firstResponder->gestureRecognizerRequest(touchState)) + if (firstResponder->gestureRecognizerRequest(touchState, firstResponder)) { // Play touch sound with random pitch float pitch = (rand() % 10) / 10.0f + 1.0f; diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index d1e26aac..7b0c0dc0 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -110,7 +110,7 @@ void View::addGestureRecognizer(GestureRecognizer* recognizer) this->gestureRecognizers.push_back(recognizer); } -bool View::gestureRecognizerRequest(TouchState touch) +bool View::gestureRecognizerRequest(TouchState touch, View* firstResponder) { bool res = touch.state == TouchEvent::START; for (GestureRecognizer* recognizer : getGestureRecognizers()) @@ -118,11 +118,11 @@ bool View::gestureRecognizerRequest(TouchState touch) GestureState state = recognizer->recognitionLoop(touch, this); if (res) res &= recognizer->enabled && recognizer->soundOnTouch(); if (state == GestureState::START) - Application::firstResponder->interruptGestures(true); + firstResponder->interruptGestures(true); } if (parent) - res &= parent->gestureRecognizerRequest(touch); + res &= parent->gestureRecognizerRequest(touch, firstResponder); return res; } From 0eb4c6ddee61c2ad385c33c38620c05e6efbd2e8 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sat, 20 Feb 2021 20:14:50 +0300 Subject: [PATCH 19/59] Updated license --- .../core/touch/gesture_recognizer.hpp | 31 +++++++++---------- .../core/touch/pan_gesture_recognizer.hpp | 23 ++++++-------- .../core/touch/tap_gesture_recognizer.hpp | 31 +++++++++---------- library/lib/core/touch/gesture_recognizer.cpp | 31 +++++++++---------- .../lib/core/touch/pan_gesture_recognizer.cpp | 23 ++++++-------- .../lib/core/touch/tap_gesture_recognizer.cpp | 25 +++++++-------- 6 files changed, 73 insertions(+), 91 deletions(-) diff --git a/library/include/borealis/core/touch/gesture_recognizer.hpp b/library/include/borealis/core/touch/gesture_recognizer.hpp index 69b789e8..3ca21ca0 100644 --- a/library/include/borealis/core/touch/gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/gesture_recognizer.hpp @@ -1,20 +1,17 @@ /* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #pragma once @@ -50,4 +47,4 @@ class GestureRecognizer GestureState state; }; -}; \ No newline at end of file +}; diff --git a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp index eb4af548..6441aac7 100644 --- a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp @@ -1,20 +1,17 @@ /* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX + Copyright 2021 XITRIX - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - This program 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 General Public License for more details. + http://www.apache.org/licenses/LICENSE-2.0 - You should have received a copy of the GNU General Public License - along with this program. If not, see . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #pragma once diff --git a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp index 0f8eb40b..4079a37b 100644 --- a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp @@ -1,20 +1,17 @@ /* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #pragma once @@ -42,4 +39,4 @@ class TapGestureRecognizer: public GestureRecognizer GestureState lastState; }; -}; \ No newline at end of file +}; diff --git a/library/lib/core/touch/gesture_recognizer.cpp b/library/lib/core/touch/gesture_recognizer.cpp index 480dcd04..9121f730 100644 --- a/library/lib/core/touch/gesture_recognizer.cpp +++ b/library/lib/core/touch/gesture_recognizer.cpp @@ -1,20 +1,17 @@ /* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #include @@ -40,4 +37,4 @@ bool GestureRecognizer::soundOnTouch() return true; } -} \ No newline at end of file +} diff --git a/library/lib/core/touch/pan_gesture_recognizer.cpp b/library/lib/core/touch/pan_gesture_recognizer.cpp index d28532b3..14f5a687 100644 --- a/library/lib/core/touch/pan_gesture_recognizer.cpp +++ b/library/lib/core/touch/pan_gesture_recognizer.cpp @@ -1,20 +1,17 @@ /* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX + Copyright 2021 XITRIX - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - This program 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 General Public License for more details. + http://www.apache.org/licenses/LICENSE-2.0 - You should have received a copy of the GNU General Public License - along with this program. If not, see . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #include diff --git a/library/lib/core/touch/tap_gesture_recognizer.cpp b/library/lib/core/touch/tap_gesture_recognizer.cpp index 332332b6..45784cc2 100644 --- a/library/lib/core/touch/tap_gesture_recognizer.cpp +++ b/library/lib/core/touch/tap_gesture_recognizer.cpp @@ -1,20 +1,17 @@ /* - Borealis, a Nintendo Switch UI Library - Copyright (C) 2021 natinusala - Copyright (C) 2021 XITRIX + Copyright 2021 XITRIX - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - This program 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 General Public License for more details. + http://www.apache.org/licenses/LICENSE-2.0 - You should have received a copy of the GNU General Public License - along with this program. If not, see . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ #include @@ -82,4 +79,4 @@ bool TapGestureRecognizer::soundOnTouch() return this->playSound; } -}; \ No newline at end of file +}; From 0ab59505a287d3a2ff097402796890fa7d35084d Mon Sep 17 00:00:00 2001 From: XITRIX Date: Tue, 23 Feb 2021 04:18:52 +0300 Subject: [PATCH 20/59] Replaced focusTouchMode by InputType enum --- library/include/borealis/core/application.hpp | 20 +++++++++++-------- library/lib/core/application.cpp | 20 +++++++++++-------- library/lib/core/view.cpp | 6 +++--- library/lib/views/scrolling_frame.cpp | 2 +- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index 039b7bd3..8290dec3 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -55,6 +55,11 @@ namespace brls void frame(FrameContext* ctx) override; }; TODO: restore that */ +enum class InputType { + GAMEPAD, + TOUCH, +}; + typedef std::function XMLViewCreator; class Application @@ -207,14 +212,11 @@ class Application static std::string getLocale(); /** - * If touch input mode enabled, disable it and move focus on last view - * Returns true if touch mode was disabled. + * Returns the current input type. */ - static bool dismissTouchMode(); - - inline static bool getFocusTouchMode() + inline static InputType getInputType() { - return focusTouchMode; + return inputType; } private: @@ -237,8 +239,10 @@ class Application inline static unsigned windowWidth, windowHeight; inline static View* currentFocus; - - inline static bool focusTouchMode = false; + + static bool setInputType(InputType type); + + inline static InputType inputType = InputType::GAMEPAD; inline static unsigned blockInputsTokens = 0; // any value > 0 means inputs are blocked diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 9b34e347..ea7acb43 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -177,7 +177,7 @@ bool Application::mainLoop() { case TouchEvent::START: Logger::debug("Touched at X: " + std::to_string(touchState.x) + ", Y: " + std::to_string(touchState.y)); - Application::focusTouchMode = true; + Application::setInputType(InputType::TOUCH); firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] ->getContentView()->hitTest(touchState.x, touchState.y); break; @@ -255,7 +255,7 @@ void Application::quit() void Application::navigate(FocusDirection direction) { - if (Application::dismissTouchMode()) + if (Application::setInputType(InputType::GAMEPAD)) return; View* currentFocus = Application::currentFocus; @@ -354,14 +354,18 @@ void Application::onControllerButtonPressed(enum ControllerButton button, bool r } } -bool Application::dismissTouchMode() +bool Application::setInputType(InputType type) { - if (Application::focusTouchMode) { - Application::focusTouchMode = false; + if (type == Application::inputType) + return false; + + Application::inputType = type; + + if (type == InputType::GAMEPAD) { Application::currentFocus->onFocusGained(); - return true; } - return false; + + return true; } View* Application::getCurrentFocus() @@ -372,7 +376,7 @@ View* Application::getCurrentFocus() bool Application::handleAction(char button) { if (button == BUTTON_A && - Application::dismissTouchMode()) + setInputType(InputType::GAMEPAD)) return false; if (Application::activitiesStack.empty()) diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index 7b0c0dc0..78c68b70 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -152,7 +152,7 @@ void View::frame(FrameContext* ctx) this->drawBackground(ctx->vg, ctx, style); // Draw shadow - if (this->shadowType != ShadowType::NONE && (this->showShadow || Application::getFocusTouchMode())) + if (this->shadowType != ShadowType::NONE && (this->showShadow || Application::getInputType() == InputType::TOUCH)) this->drawShadow(ctx->vg, ctx, style, x, y, width, height); // Draw border @@ -217,7 +217,7 @@ void View::playClickAnimation(bool reverse) reverse ? EasingFunction::quadraticOut : EasingFunction::quadraticIn); this->clickAlpha.setEndCallback([this, reverse](bool finished) { - if (reverse || Application::getFocusTouchMode()) + if (reverse || Application::getInputType() == InputType::TOUCH) return; this->playClickAnimation(true); @@ -459,7 +459,7 @@ void View::setAlpha(float alpha) // TODO: Slight glow all around void View::drawHighlight(NVGcontext* vg, Theme theme, float alpha, Style style, bool background) { - if (Application::getFocusTouchMode()) + if (Application::getInputType() == InputType::TOUCH) return; nvgSave(vg); diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index bba71f9f..ab40523b 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -272,7 +272,7 @@ void ScrollingFrame::onChildFocusGained(View* directChild, View* focusedView) this->childFocused = true; // Start scrolling - if (!Application::getFocusTouchMode()) + if (Application::getInputType() != InputType::TOUCH) this->updateScrolling(true); Box::onChildFocusGained(directChild, focusedView); From 49987c89493f5fa611f9aa72b6830b932a46bf19 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Tue, 23 Feb 2021 04:30:44 +0300 Subject: [PATCH 21/59] Added comments --- demo/captioned_image.cpp | 2 +- .../core/touch/gesture_recognizer.hpp | 25 ++++++++++---- .../core/touch/pan_gesture_recognizer.hpp | 33 +++++++++++++++++-- .../core/touch/tap_gesture_recognizer.hpp | 6 ++++ library/include/borealis/core/view.hpp | 11 +++++++ library/lib/core/application.cpp | 2 ++ .../lib/core/touch/pan_gesture_recognizer.cpp | 17 ++++++++-- .../lib/core/touch/tap_gesture_recognizer.cpp | 10 ++++-- 8 files changed, 90 insertions(+), 16 deletions(-) diff --git a/demo/captioned_image.cpp b/demo/captioned_image.cpp index ded38488..08e7ca2f 100644 --- a/demo/captioned_image.cpp +++ b/demo/captioned_image.cpp @@ -39,7 +39,7 @@ CaptionedImage::CaptionedImage() this->forwardXMLAttribute("caption", this->label, "text"); - this->addGestureRecognizer(new brls::TapGestureRecognizer([this](auto _){ + this->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureRecognizer* recognizer){ brls::Application::giveFocus(this); return true; })); diff --git a/library/include/borealis/core/touch/gesture_recognizer.hpp b/library/include/borealis/core/touch/gesture_recognizer.hpp index 3ca21ca0..d48a1867 100644 --- a/library/include/borealis/core/touch/gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/gesture_recognizer.hpp @@ -25,23 +25,36 @@ namespace brls class View; +// Represents current gesture state enum class GestureState { - INTERRUPTED, - UNSURE, - START, - STAY, - END, - FAILED, + INTERRUPTED, // Gesture has been interupted, no callbacks will come + UNSURE, // Gesture started recognition and not sure if it should interupt other recognizers + START, // Gesture sure that it match to conditions and will interupt other recognizers + STAY, // Gesture in process, user still hold finger on screen + END, // User released it's finger from screen, final frame of gesture + FAILED, // Gesture failed conditions }; +// Superclass for all the other recognizers class GestureRecognizer { public: + // Main recognition loop, for internal usage only, should not be called anywhere, but Application virtual GestureState recognitionLoop(TouchState touch, View* view); + + // Returns true if recognizer plays default 'click' sound on touch start virtual bool soundOnTouch(); + + // Interupt this recognizer + // If onlyIfUnsureState == true recognizer will be interupted + // only if current state is UNSURE void interrupt(bool onlyIfUnsureState); + + // If false, this recognizer will be skipped bool enabled = true; + + // Get the current state of recognizer GestureState getState() const { return state; } protected: GestureState state; diff --git a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp index 6441aac7..6ab8e386 100644 --- a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/pan_gesture_recognizer.hpp @@ -24,11 +24,12 @@ namespace brls class PanGestureRecognizer; typedef std::function PanGestureRespond; +// Axis of pan recognition start conditions enum class PanAxis { - HORIZONTAL, - VERTICAL, - NONE, + HORIZONTAL, // Triggers only on horizontal coordinate changes + VERTICAL, // Triggers only on vertical coordinate changes + NONE, // Any movement allowed }; struct position @@ -37,27 +38,52 @@ struct position float y; }; +// Contains info about acceleration on pan ends struct pan_acceleration { + // distances in pixels float distanceX; float distanceY; + + // times to cover the distance float timeX; float timeY; }; +// Pan recognizer +// UNSURE: while touch not moved enough to recognize it as pan +// START: gesture has been recognized +// MOVE: gesture in process +// END: finger released, acceleration will be calculated +// FAILED: unsupported class PanGestureRecognizer: public GestureRecognizer { public: PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); GestureState recognitionLoop(TouchState touch, View* view) override; + // Get current X position float getX() const { return x; } + + // Get current Y position float getY() const { return y; } + + // Get start X position float getStartX() const { return startX; } + + // Get start Y position float getStartY() const { return startY; } + + // Get difference between current and previous positions by X float getDeltaX() const { return deltaX; } + + // Get difference between current and previous positions by Y float getDeltaY() const { return deltaY; } + + // PanAxis getAxis() const { return this->axis; } + + // Get acceleration info, actual data only when current state is END pan_acceleration getAcceleration() const { return this->acceleration; } private: PanGestureRespond respond; @@ -70,6 +96,7 @@ class PanGestureRecognizer: public GestureRecognizer PanAxis axis; pan_acceleration acceleration; std::vector posHistory; + GestureState lastState; }; }; diff --git a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp index 4079a37b..bca2d5e7 100644 --- a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/tap_gesture_recognizer.hpp @@ -24,6 +24,12 @@ namespace brls class TapGestureRecognizer; typedef std::function TapGestureRespond; +// Tap recognizer +// UNSURE: while touch moves inside of View bounds +// START: unsupported +// MOVE: unsupported +// END: touch released inside View's bounds +// FAILED: touch moved outside View's bounds class TapGestureRecognizer: public GestureRecognizer { public: diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index 2e436fd8..db07106d 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -1036,13 +1036,24 @@ class View return this->actions; } + /** + * Get the vector of all gesture recognizers attached to that view. + */ const std::vector& getGestureRecognizers() { return this->gestureRecognizers; } + /** + * Interrupt every recognizer on this view. + * If onlyIfUnsureState == true, only recognizers with + * current state UNSURE will be interupted + */ void interruptGestures(bool onlyIfUnsureState); + /** + * Add new gesture recognizer on this view. + */ void addGestureRecognizer(GestureRecognizer* recognizer); /** diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index ea7acb43..d7bf0e00 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -178,6 +178,8 @@ bool Application::mainLoop() case TouchEvent::START: Logger::debug("Touched at X: " + std::to_string(touchState.x) + ", Y: " + std::to_string(touchState.y)); Application::setInputType(InputType::TOUCH); + + // Search for first responder, which will be the root of recognition tree firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] ->getContentView()->hitTest(touchState.x, touchState.y); break; diff --git a/library/lib/core/touch/pan_gesture_recognizer.cpp b/library/lib/core/touch/pan_gesture_recognizer.cpp index 14f5a687..6fe8116e 100644 --- a/library/lib/core/touch/pan_gesture_recognizer.cpp +++ b/library/lib/core/touch/pan_gesture_recognizer.cpp @@ -33,11 +33,19 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) { if (!enabled) return GestureState::FAILED; + // If not first touch frame and state is + // INTERRUPTED or FAILED, stop recognition if (touch.state != TouchEvent::START) { if (this->state == GestureState::INTERRUPTED || - this->state == GestureState::FAILED) + this->state == GestureState::FAILED) + { + if (respond && this->state != lastState) + this->respond(this); + + lastState = this->state; return this->state; + } } switch (touch.state) @@ -59,6 +67,7 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) this->x = touch.x; this->y = touch.y; + // Check if pass any condition to set state START if (this->state == GestureState::UNSURE) { if (fabs(this->startX - touch.x) > MAX_DELTA_MOVEMENT || @@ -87,7 +96,8 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) else this->state = GestureState::END; } - + + // If last touch frame, calculate acceleration if (this->state == GestureState::END) { float time = posHistory.size() / FPS; @@ -118,7 +128,8 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) break; } - posHistory.insert(posHistory.begin(), position{ .x = this->x, .y = this->y }); + // Add current state to history + posHistory.insert(posHistory.begin(), position { .x = this->x, .y = this->y }); while (posHistory.size() > HISTORY_LIMIT) { posHistory.pop_back(); } diff --git a/library/lib/core/touch/tap_gesture_recognizer.cpp b/library/lib/core/touch/tap_gesture_recognizer.cpp index 45784cc2..4a0f65d0 100644 --- a/library/lib/core/touch/tap_gesture_recognizer.cpp +++ b/library/lib/core/touch/tap_gesture_recognizer.cpp @@ -28,13 +28,15 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) { if (!enabled) return GestureState::FAILED; + // If not first touch frame and state is + // INTERRUPTED or FAILED, stop recognition if (touch.state != TouchEvent::START) { if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { if (respond && !this->callbackOnEndOnly && this->state != lastState) - this->playSound = this->respond(this); + this->respond(this); lastState = this->state; return this->state; @@ -52,18 +54,20 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) this->playSound = this->respond(this); break; case TouchEvent::STAY: + // Check if touch is out view's bounds + // if true, FAIL recognition if (touch.x < view->getX() || touch.x > view->getX() + view->getWidth() || touch.y < view->getY() || touch.y > view->getY() + view->getHeight()) { this->state = GestureState::FAILED; if (respond && !this->callbackOnEndOnly) - this->playSound = this->respond(this); + this->respond(this); } break; case TouchEvent::END: this->state = GestureState::END; if (respond) - this->playSound = this->respond(this); + this->respond(this); break; case TouchEvent::NONE: this->state = GestureState::FAILED; From 79f891d204fe61a6603002155acc1b38c8fc51d7 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Wed, 24 Feb 2021 00:56:18 +0300 Subject: [PATCH 22/59] Review changes --- library/include/borealis.hpp | 5 +++-- library/include/borealis/core/application.hpp | 5 +++-- .../core/{touch/gesture_recognizer.hpp => gesture.hpp} | 10 +++++++--- .../{pan_gesture_recognizer.hpp => pan_gesture.hpp} | 2 +- .../{tap_gesture_recognizer.hpp => tap_gesture.hpp} | 2 +- library/include/borealis/core/view.hpp | 2 +- .../core/{touch/gesture_recognizer.cpp => gesture.cpp} | 2 +- .../{pan_gesture_recognizer.cpp => pan_gesture.cpp} | 0 .../{tap_gesture_recognizer.cpp => tap_gesture.cpp} | 0 library/lib/core/view.cpp | 2 +- library/lib/views/button.cpp | 2 +- library/lib/views/scrolling_frame.cpp | 2 +- library/lib/views/sidebar.cpp | 2 +- library/meson.build | 6 +++--- 14 files changed, 24 insertions(+), 18 deletions(-) rename library/include/borealis/core/{touch/gesture_recognizer.hpp => gesture.hpp} (89%) rename library/include/borealis/core/touch/{pan_gesture_recognizer.hpp => pan_gesture.hpp} (98%) rename library/include/borealis/core/touch/{tap_gesture_recognizer.hpp => tap_gesture.hpp} (97%) rename library/lib/core/{touch/gesture_recognizer.cpp => gesture.cpp} (94%) rename library/lib/core/touch/{pan_gesture_recognizer.cpp => pan_gesture.cpp} (100%) rename library/lib/core/touch/{tap_gesture_recognizer.cpp => tap_gesture.cpp} (100%) diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index f255dc7f..2a58d734 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -40,6 +40,7 @@ #include #include #include +#include //Views #include @@ -54,5 +55,5 @@ #include //Gestures -#include -#include +#include +#include diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index 8290dec3..c2d840d7 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -55,9 +55,10 @@ namespace brls void frame(FrameContext* ctx) override; }; TODO: restore that */ +// Input types for entire app enum class InputType { - GAMEPAD, - TOUCH, + GAMEPAD, // Gamepad or keyboard + TOUCH, // Touch screen }; typedef std::function XMLViewCreator; diff --git a/library/include/borealis/core/touch/gesture_recognizer.hpp b/library/include/borealis/core/gesture.hpp similarity index 89% rename from library/include/borealis/core/touch/gesture_recognizer.hpp rename to library/include/borealis/core/gesture.hpp index d48a1867..f75b596b 100644 --- a/library/include/borealis/core/touch/gesture_recognizer.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -52,12 +52,16 @@ class GestureRecognizer void interrupt(bool onlyIfUnsureState); // If false, this recognizer will be skipped - bool enabled = true; + bool isEnabled() const { return this->enabled; } + + // If false, this recognizer will be skipped + void setEnabled(bool enabled) { this->enabled = enabled; } // Get the current state of recognizer GestureState getState() const { return state; } protected: - GestureState state; + GestureState state = GestureState::FAILED; + bool enabled = true; }; -}; +} // namespace brls diff --git a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp b/library/include/borealis/core/touch/pan_gesture.hpp similarity index 98% rename from library/include/borealis/core/touch/pan_gesture_recognizer.hpp rename to library/include/borealis/core/touch/pan_gesture.hpp index 6ab8e386..1895612b 100644 --- a/library/include/borealis/core/touch/pan_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -16,7 +16,7 @@ #pragma once -#include "gesture_recognizer.hpp" +#include namespace brls { diff --git a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp b/library/include/borealis/core/touch/tap_gesture.hpp similarity index 97% rename from library/include/borealis/core/touch/tap_gesture_recognizer.hpp rename to library/include/borealis/core/touch/tap_gesture.hpp index bca2d5e7..8558d875 100644 --- a/library/include/borealis/core/touch/tap_gesture_recognizer.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -16,7 +16,7 @@ #pragma once -#include "gesture_recognizer.hpp" +#include namespace brls { diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index db07106d..62b7224f 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/library/lib/core/touch/gesture_recognizer.cpp b/library/lib/core/gesture.cpp similarity index 94% rename from library/lib/core/touch/gesture_recognizer.cpp rename to library/lib/core/gesture.cpp index 9121f730..bbc1c4cc 100644 --- a/library/lib/core/touch/gesture_recognizer.cpp +++ b/library/lib/core/gesture.cpp @@ -14,7 +14,7 @@ limitations under the License. */ -#include +#include namespace brls { diff --git a/library/lib/core/touch/pan_gesture_recognizer.cpp b/library/lib/core/touch/pan_gesture.cpp similarity index 100% rename from library/lib/core/touch/pan_gesture_recognizer.cpp rename to library/lib/core/touch/pan_gesture.cpp diff --git a/library/lib/core/touch/tap_gesture_recognizer.cpp b/library/lib/core/touch/tap_gesture.cpp similarity index 100% rename from library/lib/core/touch/tap_gesture_recognizer.cpp rename to library/lib/core/touch/tap_gesture.cpp diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index 78c68b70..683da47a 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -116,7 +116,7 @@ bool View::gestureRecognizerRequest(TouchState touch, View* firstResponder) for (GestureRecognizer* recognizer : getGestureRecognizers()) { GestureState state = recognizer->recognitionLoop(touch, this); - if (res) res &= recognizer->enabled && recognizer->soundOnTouch(); + if (res) res &= recognizer->isEnabled() && recognizer->soundOnTouch(); if (state == GestureState::START) firstResponder->interruptGestures(true); } diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index 54bfd0f9..4fd4ad2f 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include namespace brls { diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index ab40523b..453df151 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include namespace brls { diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index 2593b207..4ec1e1b5 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include using namespace brls::literals; diff --git a/library/meson.build b/library/meson.build index ca9f6a70..1b2dea3a 100644 --- a/library/meson.build +++ b/library/meson.build @@ -19,9 +19,9 @@ borealis_files = files( 'lib/core/box.cpp', 'lib/core/bind.cpp', - 'lib/core/touch/gesture_recognizer.cpp', - 'lib/core/touch/tap_gesture_recognizer.cpp', - 'lib/core/touch/pan_gesture_recognizer.cpp', + 'lib/core/gesture.cpp', + 'lib/core/touch/tap_gesture.cpp', + 'lib/core/touch/pan_gesture.cpp', 'lib/platforms/glfw/glfw_platform.cpp', 'lib/platforms/glfw/glfw_video.cpp', From f1f28c3494ea59e0dcfef7dcca702ad4e353df7c Mon Sep 17 00:00:00 2001 From: XITRIX Date: Wed, 24 Feb 2021 01:30:26 +0300 Subject: [PATCH 23/59] clang formatter used --- demo/captioned_image.cpp | 2 +- library/include/borealis.hpp | 4 +- library/include/borealis/core/application.hpp | 11 +- library/include/borealis/core/gesture.hpp | 26 +-- library/include/borealis/core/input.hpp | 3 +- library/include/borealis/core/platform.hpp | 2 +- .../borealis/core/touch/pan_gesture.hpp | 63 +++---- .../borealis/core/touch/tap_gesture.hpp | 17 +- library/include/borealis/core/util.hpp | 2 +- library/lib/core/application.cpp | 50 +++--- library/lib/core/box.cpp | 2 +- library/lib/core/gesture.cpp | 2 +- library/lib/core/touch/pan_gesture.cpp | 159 +++++++++--------- library/lib/core/touch/tap_gesture.cpp | 67 ++++---- library/lib/core/view.cpp | 23 ++- library/lib/platforms/glfw/glfw_input.cpp | 23 ++- library/lib/platforms/glfw/glfw_platform.cpp | 2 +- library/lib/platforms/switch/switch_audio.cpp | 2 +- library/lib/platforms/switch/switch_input.cpp | 30 ++-- .../lib/platforms/switch/switch_platform.cpp | 2 +- library/lib/views/button.cpp | 35 ++-- library/lib/views/scrolling_frame.cpp | 33 ++-- library/lib/views/sidebar.cpp | 34 ++-- 23 files changed, 308 insertions(+), 286 deletions(-) diff --git a/demo/captioned_image.cpp b/demo/captioned_image.cpp index 08e7ca2f..2633884f 100644 --- a/demo/captioned_image.cpp +++ b/demo/captioned_image.cpp @@ -39,7 +39,7 @@ CaptionedImage::CaptionedImage() this->forwardXMLAttribute("caption", this->label, "text"); - this->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureRecognizer* recognizer){ + this->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureRecognizer* recognizer) { brls::Application::giveFocus(this); return true; })); diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index 2a58d734..591ff59a 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,6 @@ #include #include #include -#include //Views #include @@ -55,5 +55,5 @@ #include //Gestures -#include #include +#include diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index c2d840d7..34d52e78 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -56,7 +56,8 @@ namespace brls }; TODO: restore that */ // Input types for entire app -enum class InputType { +enum class InputType +{ GAMEPAD, // Gamepad or keyboard TOUCH, // Touch screen }; @@ -211,7 +212,7 @@ class Application * Returns the current system locale. */ static std::string getLocale(); - + /** * Returns the current input type. */ @@ -240,11 +241,11 @@ class Application inline static unsigned windowWidth, windowHeight; inline static View* currentFocus; - + static bool setInputType(InputType type); - + inline static InputType inputType = InputType::GAMEPAD; - + inline static unsigned blockInputsTokens = 0; // any value > 0 means inputs are blocked inline static std::string commonFooter = ""; diff --git a/library/include/borealis/core/gesture.hpp b/library/include/borealis/core/gesture.hpp index f75b596b..fa680a83 100644 --- a/library/include/borealis/core/gesture.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -16,9 +16,8 @@ #pragma once -#include - #include +#include namespace brls { @@ -39,29 +38,30 @@ enum class GestureState // Superclass for all the other recognizers class GestureRecognizer { -public: + public: // Main recognition loop, for internal usage only, should not be called anywhere, but Application virtual GestureState recognitionLoop(TouchState touch, View* view); - - // Returns true if recognizer plays default 'click' sound on touch start + + // Returns true if recognizer plays default 'click' sound on touch start virtual bool soundOnTouch(); - + // Interupt this recognizer // If onlyIfUnsureState == true recognizer will be interupted // only if current state is UNSURE void interrupt(bool onlyIfUnsureState); - + // If false, this recognizer will be skipped bool isEnabled() const { return this->enabled; } - + // If false, this recognizer will be skipped void setEnabled(bool enabled) { this->enabled = enabled; } - + // Get the current state of recognizer - GestureState getState() const { return state; } -protected: - GestureState state = GestureState::FAILED; - bool enabled = true; + GestureState getState() const { return state; } + + protected: + GestureState state = GestureState::FAILED; + bool enabled = true; }; } // namespace brls diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index 4df222a8..994f44ab 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -72,7 +72,8 @@ typedef struct ControllerState float axes[_AXES_MAX]; // from 0.0f to 1.0f } ControllerState; -enum class TouchEvent { +enum class TouchEvent +{ START, STAY, END, diff --git a/library/include/borealis/core/platform.hpp b/library/include/borealis/core/platform.hpp index 473f8632..1db855e6 100644 --- a/library/include/borealis/core/platform.hpp +++ b/library/include/borealis/core/platform.hpp @@ -91,7 +91,7 @@ class Platform */ virtual InputManager* getInputManager() = 0; - /** + /** * Returns the FontLoader for the platform. * Cannot return nullptr. */ diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index 1895612b..be257b0f 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -29,7 +29,7 @@ enum class PanAxis { HORIZONTAL, // Triggers only on horizontal coordinate changes VERTICAL, // Triggers only on vertical coordinate changes - NONE, // Any movement allowed + NONE, // Any movement allowed }; struct position @@ -44,7 +44,7 @@ struct pan_acceleration // distances in pixels float distanceX; float distanceY; - + // times to cover the distance float timeX; float timeY; @@ -56,44 +56,45 @@ struct pan_acceleration // MOVE: gesture in process // END: finger released, acceleration will be calculated // FAILED: unsupported -class PanGestureRecognizer: public GestureRecognizer +class PanGestureRecognizer : public GestureRecognizer { -public: - PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); - GestureState recognitionLoop(TouchState touch, View* view) override; - + public: + PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); + GestureState recognitionLoop(TouchState touch, View* view) override; + // Get current X position - float getX() const { return x; } - + float getX() const { return x; } + // Get current Y position - float getY() const { return y; } - + float getY() const { return y; } + // Get start X position - float getStartX() const { return startX; } - + float getStartX() const { return startX; } + // Get start Y position - float getStartY() const { return startY; } - + float getStartY() const { return startY; } + // Get difference between current and previous positions by X - float getDeltaX() const { return deltaX; } - + float getDeltaX() const { return deltaX; } + // Get difference between current and previous positions by Y - float getDeltaY() const { return deltaY; } - - // - PanAxis getAxis() const { return this->axis; } - + float getDeltaY() const { return deltaY; } + + // + PanAxis getAxis() const { return this->axis; } + // Get acceleration info, actual data only when current state is END pan_acceleration getAcceleration() const { return this->acceleration; } -private: - PanGestureRespond respond; - float x; - float y; - float startX; - float startY; - float deltaX; - float deltaY; - PanAxis axis; + + private: + PanGestureRespond respond; + float x; + float y; + float startX; + float startY; + float deltaX; + float deltaY; + PanAxis axis; pan_acceleration acceleration; std::vector posHistory; GestureState lastState; diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index 8558d875..7e9d5faf 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -30,17 +30,18 @@ typedef std::function TapGestureRespond; // MOVE: unsupported // END: touch released inside View's bounds // FAILED: touch moved outside View's bounds -class TapGestureRecognizer: public GestureRecognizer +class TapGestureRecognizer : public GestureRecognizer { -public: - TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); - GestureState recognitionLoop(TouchState touch, View* view) override; + public: + TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); + GestureState recognitionLoop(TouchState touch, View* view) override; bool soundOnTouch() override; -private: - TapGestureRespond respond; + + private: + TapGestureRespond respond; bool playSound; - float x; - float y; + float x; + float y; bool callbackOnEndOnly; GestureState lastState; }; diff --git a/library/include/borealis/core/util.hpp b/library/include/borealis/core/util.hpp index 95f96377..d02ccf05 100644 --- a/library/include/borealis/core/util.hpp +++ b/library/include/borealis/core/util.hpp @@ -25,6 +25,6 @@ namespace brls /** * Prints the given error message message and throws a std::logic_error. */ -[[ noreturn ]] void fatal(std::string message); +[[noreturn]] void fatal(std::string message); } // namespace brls diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index d7bf0e00..6f066fd9 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -166,27 +166,29 @@ bool Application::mainLoop() // Input ControllerState controllerState = {}; - TouchState touchState = {}; + TouchState touchState = {}; InputManager* inputManager = Application::platform->getInputManager(); inputManager->updateTouchState(&touchState); inputManager->updateControllerState(&controllerState); - + // Touch controller events switch (touchState.state) { - case TouchEvent::START: - Logger::debug("Touched at X: " + std::to_string(touchState.x) + ", Y: " + std::to_string(touchState.y)); - Application::setInputType(InputType::TOUCH); - - // Search for first responder, which will be the root of recognition tree - firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] - ->getContentView()->hitTest(touchState.x, touchState.y); - break; - case TouchEvent::NONE: - firstResponder = nullptr; - break; - default: break; + case TouchEvent::START: + Logger::debug("Touched at X: " + std::to_string(touchState.x) + ", Y: " + std::to_string(touchState.y)); + Application::setInputType(InputType::TOUCH); + + // Search for first responder, which will be the root of recognition tree + firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] + ->getContentView() + ->hitTest(touchState.x, touchState.y); + break; + case TouchEvent::NONE: + firstResponder = nullptr; + break; + default: + break; } if (firstResponder) @@ -259,7 +261,7 @@ void Application::navigate(FocusDirection direction) { if (Application::setInputType(InputType::GAMEPAD)) return; - + View* currentFocus = Application::currentFocus; // Do nothing if there is no current focus @@ -329,7 +331,7 @@ void Application::onControllerButtonPressed(enum ControllerButton button, bool r return; Application::repetitionOldFocus = Application::currentFocus; - + // Actions if (Application::handleAction(button)) return; @@ -360,13 +362,14 @@ bool Application::setInputType(InputType type) { if (type == Application::inputType) return false; - + Application::inputType = type; - - if (type == InputType::GAMEPAD) { + + if (type == InputType::GAMEPAD) + { Application::currentFocus->onFocusGained(); } - + return true; } @@ -377,10 +380,9 @@ View* Application::getCurrentFocus() bool Application::handleAction(char button) { - if (button == BUTTON_A && - setInputType(InputType::GAMEPAD)) + if (button == BUTTON_A && setInputType(InputType::GAMEPAD)) return false; - + if (Application::activitiesStack.empty()) return false; @@ -402,7 +404,7 @@ bool Application::handleAction(char button) if (action.available) { - + if (action.actionListener(hintParent)) { if (button == BUTTON_A) diff --git a/library/lib/core/box.cpp b/library/lib/core/box.cpp index 0c010527..2511df14 100644 --- a/library/lib/core/box.cpp +++ b/library/lib/core/box.cpp @@ -319,7 +319,7 @@ View* Box::getDefaultFocus() View* Box::hitTest(double x, double y) { // Check if touch fits in view frame - if (pointInside(x, y)) + if (pointInside(x, y)) { Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); for (View* child : this->children) diff --git a/library/lib/core/gesture.cpp b/library/lib/core/gesture.cpp index bbc1c4cc..ab2819d5 100644 --- a/library/lib/core/gesture.cpp +++ b/library/lib/core/gesture.cpp @@ -26,7 +26,7 @@ GestureState GestureRecognizer::recognitionLoop(TouchState touch, View* view) void GestureRecognizer::interrupt(bool onlyIfUnsureState) { - if (onlyIfUnsureState && this->state != GestureState::UNSURE) + if (onlyIfUnsureState && this->state != GestureState::UNSURE) return; this->state = GestureState::INTERRUPTED; diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index 6fe8116e..b5f025cf 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -25,24 +25,25 @@ namespace brls { PanGestureRecognizer::PanGestureRecognizer(PanGestureRespond respond, PanAxis axis) - : respond(respond), axis(axis) + : respond(respond) + , axis(axis) { } -GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) +GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) { - if (!enabled) return GestureState::FAILED; - + if (!enabled) + return GestureState::FAILED; + // If not first touch frame and state is // INTERRUPTED or FAILED, stop recognition - if (touch.state != TouchEvent::START) + if (touch.state != TouchEvent::START) { - if (this->state == GestureState::INTERRUPTED || - this->state == GestureState::FAILED) + if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { if (respond && this->state != lastState) this->respond(this); - + lastState = this->state; return this->state; } @@ -50,87 +51,85 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) switch (touch.state) { - case TouchEvent::START: - this->posHistory.clear(); - this->state = GestureState::UNSURE; - this->startX = touch.x; - this->startY = touch.y; - this->x = touch.x; - this->y = touch.y; - break; - case TouchEvent::STAY: - case TouchEvent::END: - - this->deltaX = touch.x - this->x; - this->deltaY = touch.y - this->y; - - this->x = touch.x; - this->y = touch.y; - - // Check if pass any condition to set state START - if (this->state == GestureState::UNSURE) - { - if (fabs(this->startX - touch.x) > MAX_DELTA_MOVEMENT || - fabs(this->startY - touch.y) > MAX_DELTA_MOVEMENT) + case TouchEvent::START: + this->posHistory.clear(); + this->state = GestureState::UNSURE; + this->startX = touch.x; + this->startY = touch.y; + this->x = touch.x; + this->y = touch.y; + break; + case TouchEvent::STAY: + case TouchEvent::END: + + this->deltaX = touch.x - this->x; + this->deltaY = touch.y - this->y; + + this->x = touch.x; + this->y = touch.y; + + // Check if pass any condition to set state START + if (this->state == GestureState::UNSURE) { - switch (axis) + if (fabs(this->startX - touch.x) > MAX_DELTA_MOVEMENT || fabs(this->startY - touch.y) > MAX_DELTA_MOVEMENT) { - case PanAxis::HORIZONTAL: - if (fabs(deltaX) > fabs(deltaY)) - this->state = GestureState::START; - break; - case PanAxis::VERTICAL: - if (fabs(deltaX) < fabs(deltaY)) - this->state = GestureState::START; - break; - case PanAxis::NONE: - this->state = GestureState::START; - break; + switch (axis) + { + case PanAxis::HORIZONTAL: + if (fabs(deltaX) > fabs(deltaY)) + this->state = GestureState::START; + break; + case PanAxis::VERTICAL: + if (fabs(deltaX) < fabs(deltaY)) + this->state = GestureState::START; + break; + case PanAxis::NONE: + this->state = GestureState::START; + break; + } } } - } - else - { - if (touch.state == TouchEvent::STAY) - this->state = GestureState::STAY; else - this->state = GestureState::END; - } - - // If last touch frame, calculate acceleration - if (this->state == GestureState::END) - { - float time = posHistory.size() / FPS; - - float distanceX = posHistory[posHistory.size()].x - posHistory[0].x; - float distanceY = posHistory[posHistory.size()].y - posHistory[0].y; - - float velocityX = distanceX / time; - float velocityY = distanceY / time; - - acceleration.timeX = -fabs(velocityX) / PAN_SCROLL_ACCELERATION; - acceleration.timeY = -fabs(velocityY) / PAN_SCROLL_ACCELERATION; - - acceleration.distanceX = velocityX * acceleration.timeX / 2; - acceleration.distanceY = velocityY * acceleration.timeY / 2; - } + { + if (touch.state == TouchEvent::STAY) + this->state = GestureState::STAY; + else + this->state = GestureState::END; + } - if (this->state == GestureState::START || - this->state == GestureState::STAY || - this->state == GestureState::END) - { - this->respond(this); - } - - break; - case TouchEvent::NONE: - this->state = GestureState::FAILED; - break; + // If last touch frame, calculate acceleration + if (this->state == GestureState::END) + { + float time = posHistory.size() / FPS; + + float distanceX = posHistory[posHistory.size()].x - posHistory[0].x; + float distanceY = posHistory[posHistory.size()].y - posHistory[0].y; + + float velocityX = distanceX / time; + float velocityY = distanceY / time; + + acceleration.timeX = -fabs(velocityX) / PAN_SCROLL_ACCELERATION; + acceleration.timeY = -fabs(velocityY) / PAN_SCROLL_ACCELERATION; + + acceleration.distanceX = velocityX * acceleration.timeX / 2; + acceleration.distanceY = velocityY * acceleration.timeY / 2; + } + + if (this->state == GestureState::START || this->state == GestureState::STAY || this->state == GestureState::END) + { + this->respond(this); + } + + break; + case TouchEvent::NONE: + this->state = GestureState::FAILED; + break; } - + // Add current state to history posHistory.insert(posHistory.begin(), position { .x = this->x, .y = this->y }); - while (posHistory.size() > HISTORY_LIMIT) { + while (posHistory.size() > HISTORY_LIMIT) + { posHistory.pop_back(); } diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index 4a0f65d0..6409f9e8 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -20,22 +20,24 @@ namespace brls { TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly) - : respond(respond), callbackOnEndOnly(callbackOnEndOnly), playSound(true) + : respond(respond) + , callbackOnEndOnly(callbackOnEndOnly) + , playSound(true) { } -GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) +GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) { - if (!enabled) return GestureState::FAILED; - + if (!enabled) + return GestureState::FAILED; + // If not first touch frame and state is // INTERRUPTED or FAILED, stop recognition - if (touch.state != TouchEvent::START) + if (touch.state != TouchEvent::START) { - if (this->state == GestureState::INTERRUPTED || - this->state == GestureState::FAILED) + if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { - if (respond && !this->callbackOnEndOnly && this->state != lastState) + if (respond && !this->callbackOnEndOnly && this->state != lastState) this->respond(this); lastState = this->state; @@ -45,33 +47,32 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) switch (touch.state) { - case TouchEvent::START: - this->state = GestureState::UNSURE; - this->x = touch.x; - this->y = touch.y; + case TouchEvent::START: + this->state = GestureState::UNSURE; + this->x = touch.x; + this->y = touch.y; - if (respond && !this->callbackOnEndOnly) - this->playSound = this->respond(this); - break; - case TouchEvent::STAY: - // Check if touch is out view's bounds - // if true, FAIL recognition - if (touch.x < view->getX() || touch.x > view->getX() + view->getWidth() || - touch.y < view->getY() || touch.y > view->getY() + view->getHeight()) - { - this->state = GestureState::FAILED; - if (respond && !this->callbackOnEndOnly) + if (respond && !this->callbackOnEndOnly) + this->playSound = this->respond(this); + break; + case TouchEvent::STAY: + // Check if touch is out view's bounds + // if true, FAIL recognition + if (touch.x < view->getX() || touch.x > view->getX() + view->getWidth() || touch.y < view->getY() || touch.y > view->getY() + view->getHeight()) + { + this->state = GestureState::FAILED; + if (respond && !this->callbackOnEndOnly) + this->respond(this); + } + break; + case TouchEvent::END: + this->state = GestureState::END; + if (respond) this->respond(this); - } - break; - case TouchEvent::END: - this->state = GestureState::END; - if (respond) - this->respond(this); - break; - case TouchEvent::NONE: - this->state = GestureState::FAILED; - break; + break; + case TouchEvent::NONE: + this->state = GestureState::FAILED; + break; } lastState = this->state; diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index 683da47a..010d3375 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -98,9 +98,9 @@ NVGpaint View::a(NVGpaint paint) void View::interruptGestures(bool onlyIfUnsureState) { - for (GestureRecognizer* recognizer : getGestureRecognizers()) + for (GestureRecognizer* recognizer : getGestureRecognizers()) recognizer->interrupt(onlyIfUnsureState); - + if (parent) parent->interruptGestures(onlyIfUnsureState); } @@ -113,10 +113,11 @@ void View::addGestureRecognizer(GestureRecognizer* recognizer) bool View::gestureRecognizerRequest(TouchState touch, View* firstResponder) { bool res = touch.state == TouchEvent::START; - for (GestureRecognizer* recognizer : getGestureRecognizers()) + for (GestureRecognizer* recognizer : getGestureRecognizers()) { GestureState state = recognizer->recognitionLoop(touch, this); - if (res) res &= recognizer->isEnabled() && recognizer->soundOnTouch(); + if (res) + res &= recognizer->isEnabled() && recognizer->soundOnTouch(); if (state == GestureState::START) firstResponder->interruptGestures(true); } @@ -459,7 +460,7 @@ void View::setAlpha(float alpha) // TODO: Slight glow all around void View::drawHighlight(NVGcontext* vg, Theme theme, float alpha, Style style, bool background) { - if (Application::getInputType() == InputType::TOUCH) + if (Application::getInputType() == InputType::TOUCH) return; nvgSave(vg); @@ -1274,7 +1275,7 @@ View::~View() for (tinyxml2::XMLDocument* document : this->boundDocuments) delete document; - + for (GestureRecognizer* recognizer : this->gestureRecognizers) delete recognizer; } @@ -2048,11 +2049,7 @@ View* View::getDefaultFocus() bool View::pointInside(double x, double y) { - return - getX() <= x && - getWidth() + getX() >= x && - getY() <= y && - getHeight() + getY() >= y; + return getX() <= x && getWidth() + getX() >= x && getY() <= y && getHeight() + getY() >= y; } View* View::hitTest(double x, double y) @@ -2060,9 +2057,9 @@ View* View::hitTest(double x, double y) // Check if can focus ourself first if (!this->isFocusable()) return nullptr; - + Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); - + if (pointInside(x, y)) { Logger::debug(describe() + ": OK"); diff --git a/library/lib/platforms/glfw/glfw_input.cpp b/library/lib/platforms/glfw/glfw_input.cpp index aaacf540..93ad1738 100644 --- a/library/lib/platforms/glfw/glfw_input.cpp +++ b/library/lib/platforms/glfw/glfw_input.cpp @@ -15,8 +15,8 @@ limitations under the License. */ -#include #include +#include #include #define GLFW_INCLUDE_NONE @@ -122,16 +122,25 @@ void GLFWInputManager::updateTouchState(TouchState* state) state->y = y / Application::windowScale; int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); - if (clickState == GLFW_PRESS) { - if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { + if (clickState == GLFW_PRESS) + { + if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) + { state->state = TouchEvent::STAY; - } else { + } + else + { state->state = TouchEvent::START; } - } else { - if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) { + } + else + { + if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) + { state->state = TouchEvent::NONE; - } else { + } + else + { state->state = TouchEvent::END; } } diff --git a/library/lib/platforms/glfw/glfw_platform.cpp b/library/lib/platforms/glfw/glfw_platform.cpp index 02f06e1c..678ac1e7 100644 --- a/library/lib/platforms/glfw/glfw_platform.cpp +++ b/library/lib/platforms/glfw/glfw_platform.cpp @@ -93,7 +93,7 @@ InputManager* GLFWPlatform::getInputManager() { return this->inputManager; } - + FontLoader* GLFWPlatform::getFontLoader() { return this->fontLoader; diff --git a/library/lib/platforms/switch/switch_audio.cpp b/library/lib/platforms/switch/switch_audio.cpp index b521ee9d..4bca8ffa 100644 --- a/library/lib/platforms/switch/switch_audio.cpp +++ b/library/lib/platforms/switch/switch_audio.cpp @@ -122,7 +122,7 @@ bool SwitchAudioPlayer::play(enum Sound sound, float pitch) } // Play the sound - plsrPlayerSetPitch(this->sounds[sound], pitch); + plsrPlayerSetPitch(this->sounds[sound], pitch); PLSR_RC rc = plsrPlayerPlay(this->sounds[sound]); if (PLSR_RC_FAILED(rc)) { diff --git a/library/lib/platforms/switch/switch_input.cpp b/library/lib/platforms/switch/switch_input.cpp index 08b2c27b..27f977d9 100644 --- a/library/lib/platforms/switch/switch_input.cpp +++ b/library/lib/platforms/switch/switch_input.cpp @@ -72,11 +72,12 @@ void SwitchInputManager::updateTouchState(TouchState* state) // Get gamepad state float x = oldTouch.x; float y = oldTouch.y; - - HidTouchScreenState hidState={0}; - if (hidGetTouchScreenStates(&hidState, 1)) + + HidTouchScreenState hidState = { 0 }; + if (hidGetTouchScreenStates(&hidState, 1)) { - if (hidState.count > 0) { + if (hidState.count > 0) + { x = hidState.touches[0].x / Application::windowScale; y = hidState.touches[0].y / Application::windowScale; } @@ -85,16 +86,25 @@ void SwitchInputManager::updateTouchState(TouchState* state) state->x = x; state->y = y; - if (hidState.count > 0) { - if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) { + if (hidState.count > 0) + { + if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) + { state->state = TouchEvent::STAY; - } else { + } + else + { state->state = TouchEvent::START; } - } else { - if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) { + } + else + { + if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) + { state->state = TouchEvent::NONE; - } else { + } + else + { state->state = TouchEvent::END; } } diff --git a/library/lib/platforms/switch/switch_platform.cpp b/library/lib/platforms/switch/switch_platform.cpp index 3379dc24..bf57b54e 100644 --- a/library/lib/platforms/switch/switch_platform.cpp +++ b/library/lib/platforms/switch/switch_platform.cpp @@ -108,7 +108,7 @@ InputManager* SwitchPlatform::getInputManager() { return this->inputManager; } - + FontLoader* SwitchPlatform::getFontLoader() { return this->fontLoader; diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index 4fd4ad2f..d5dfabdc 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -18,8 +18,8 @@ #include #include -#include #include +#include namespace brls { @@ -88,36 +88,37 @@ Button::Button() this->applyStyle(); - this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) - { + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) { Application::giveFocus(this); for (auto& action : getActions()) { if (action.button != static_cast(BUTTON_A)) continue; - if (action.available) { + if (action.available) + { this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); - switch (recogniser->getState()) + switch (recogniser->getState()) { - case GestureState::UNSURE: - Application::getAudioPlayer()->play(SOUND_FOCUS_CHANGE); - break; - case GestureState::FAILED: - case GestureState::INTERRUPTED: - Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); - break; - case GestureState::END: - if (action.actionListener(this)) - Application::getAudioPlayer()->play(action.sound); - break; + case GestureState::UNSURE: + Application::getAudioPlayer()->play(SOUND_FOCUS_CHANGE); + break; + case GestureState::FAILED: + case GestureState::INTERRUPTED: + Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); + break; + case GestureState::END: + if (action.actionListener(this)) + Application::getAudioPlayer()->play(action.sound); + break; } return false; } } return true; - }, false)); + }, + false)); } void Button::applyStyle() diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 453df151..242527ae 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -16,9 +16,9 @@ */ #include +#include #include #include -#include namespace brls { @@ -63,17 +63,16 @@ ScrollingFrame::ScrollingFrame() this->setMaximumAllowedXMLElements(1); - addGestureRecognizer(new PanGestureRecognizer([this](PanGestureRecognizer* pan) - { + addGestureRecognizer(new PanGestureRecognizer([this](PanGestureRecognizer* pan) { float contentHeight = this->getContentHeight(); static float startY; - if (pan->getState() == GestureState::START) + if (pan->getState() == GestureState::START) startY = this->scrollY * contentHeight; - float newScroll = (startY - (pan->getY() - pan->getStartY())) / contentHeight; + float newScroll = (startY - (pan->getY() - pan->getStartY())) / contentHeight; float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; - + // Bottom boundary if (newScroll > bottomLimit) newScroll = bottomLimit; @@ -87,32 +86,33 @@ ScrollingFrame::ScrollingFrame() startScrolling(true, newScroll); else { - float time = pan->getAcceleration().timeY * 1000.0f; + float time = pan->getAcceleration().timeY * 1000.0f; float newPos = this->scrollY * contentHeight + pan->getAcceleration().distanceY; - + // Bottom boundary float bottomLimit = contentHeight - this->getScrollingAreaHeight(); if (newPos > bottomLimit) { - time = time * (1 - fabs(newPos - bottomLimit) / fabs(pan->getAcceleration().distanceY)); + time = time * (1 - fabs(newPos - bottomLimit) / fabs(pan->getAcceleration().distanceY)); newPos = bottomLimit; } - + // Top boundary if (newPos < 0) { - time = time * (1 - fabs(newPos) / fabs(pan->getAcceleration().distanceY)); + time = time * (1 - fabs(newPos) / fabs(pan->getAcceleration().distanceY)); newPos = 0; } - + newScroll = newPos / contentHeight; - + if (newScroll == this->scrollY || time < 100) return; animateScrolling(newScroll, time); } - }, PanAxis::VERTICAL)); + }, + PanAxis::VERTICAL)); } void ScrollingFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) @@ -228,13 +228,12 @@ void ScrollingFrame::startScrolling(bool animated, float newScroll) this->scrollY = newScroll; this->invalidate(); } - } void ScrollingFrame::animateScrolling(float newScroll, float time) { this->scrollY.stop(); - + this->scrollY.reset(); this->scrollY.addStep(newScroll, time, EasingFunction::quadraticOut); @@ -244,7 +243,7 @@ void ScrollingFrame::animateScrolling(float newScroll, float time) }); this->scrollY.start(); - + this->invalidate(); } diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index 4ec1e1b5..cce8f762 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -18,8 +18,8 @@ #include #include -#include #include +#include using namespace brls::literals; @@ -77,30 +77,30 @@ SidebarItem::SidebarItem() }, false, SOUND_CLICK_SIDEBAR); - this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) - { - if (this->active) + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) { + if (this->active) return true; this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); - switch (recogniser->getState()) + switch (recogniser->getState()) { - case GestureState::UNSURE: - Application::getAudioPlayer()->play(SOUND_FOCUS_SIDEBAR); - break; - case GestureState::FAILED: - case GestureState::INTERRUPTED: - Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); - break; - case GestureState::END: - Application::getAudioPlayer()->play(SOUND_CLICK_SIDEBAR); - Application::giveFocus(this); - break; + case GestureState::UNSURE: + Application::getAudioPlayer()->play(SOUND_FOCUS_SIDEBAR); + break; + case GestureState::FAILED: + case GestureState::INTERRUPTED: + Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); + break; + case GestureState::END: + Application::getAudioPlayer()->play(SOUND_CLICK_SIDEBAR); + Application::giveFocus(this); + break; } return false; - }, false)); + }, + false)); } void SidebarItem::setActive(bool active) From 57c43ba9f4beb11df4acc49d452fbfac76724895 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Thu, 25 Feb 2021 15:15:11 +0300 Subject: [PATCH 24/59] Added shouldPlayDefaultSound attribute --- library/include/borealis/core/gesture.hpp | 5 +---- .../include/borealis/core/touch/pan_gesture.hpp | 2 +- .../include/borealis/core/touch/tap_gesture.hpp | 4 +--- library/lib/core/gesture.cpp | 7 +------ library/lib/core/touch/pan_gesture.cpp | 2 +- library/lib/core/touch/tap_gesture.cpp | 10 ++-------- library/lib/core/view.cpp | 14 ++++++++------ 7 files changed, 15 insertions(+), 29 deletions(-) diff --git a/library/include/borealis/core/gesture.hpp b/library/include/borealis/core/gesture.hpp index fa680a83..0ed2711d 100644 --- a/library/include/borealis/core/gesture.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -40,10 +40,7 @@ class GestureRecognizer { public: // Main recognition loop, for internal usage only, should not be called anywhere, but Application - virtual GestureState recognitionLoop(TouchState touch, View* view); - - // Returns true if recognizer plays default 'click' sound on touch start - virtual bool soundOnTouch(); + virtual GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound); // Interupt this recognizer // If onlyIfUnsureState == true recognizer will be interupted diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index be257b0f..e8fce4da 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -60,7 +60,7 @@ class PanGestureRecognizer : public GestureRecognizer { public: PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); - GestureState recognitionLoop(TouchState touch, View* view) override; + GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; // Get current X position float getX() const { return x; } diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index 7e9d5faf..97b91e3c 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -34,12 +34,10 @@ class TapGestureRecognizer : public GestureRecognizer { public: TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); - GestureState recognitionLoop(TouchState touch, View* view) override; - bool soundOnTouch() override; + GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; private: TapGestureRespond respond; - bool playSound; float x; float y; bool callbackOnEndOnly; diff --git a/library/lib/core/gesture.cpp b/library/lib/core/gesture.cpp index ab2819d5..48fc7498 100644 --- a/library/lib/core/gesture.cpp +++ b/library/lib/core/gesture.cpp @@ -19,7 +19,7 @@ namespace brls { -GestureState GestureRecognizer::recognitionLoop(TouchState touch, View* view) +GestureState GestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) { return GestureState::FAILED; } @@ -32,9 +32,4 @@ void GestureRecognizer::interrupt(bool onlyIfUnsureState) this->state = GestureState::INTERRUPTED; } -bool GestureRecognizer::soundOnTouch() -{ - return true; -} - } diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index b5f025cf..74f8b56a 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -30,7 +30,7 @@ PanGestureRecognizer::PanGestureRecognizer(PanGestureRespond respond, PanAxis ax { } -GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view) +GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) { if (!enabled) return GestureState::FAILED; diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index 6409f9e8..955db16d 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -22,11 +22,10 @@ namespace brls TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly) : respond(respond) , callbackOnEndOnly(callbackOnEndOnly) - , playSound(true) { } -GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) +GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) { if (!enabled) return GestureState::FAILED; @@ -53,7 +52,7 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) this->y = touch.y; if (respond && !this->callbackOnEndOnly) - this->playSound = this->respond(this); + *shouldPlayDefaultSound &= this->respond(this); break; case TouchEvent::STAY: // Check if touch is out view's bounds @@ -79,9 +78,4 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view) return this->state; } -bool TapGestureRecognizer::soundOnTouch() -{ - return this->playSound; -} - }; diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index 010d3375..974e8dc5 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -112,20 +112,22 @@ void View::addGestureRecognizer(GestureRecognizer* recognizer) bool View::gestureRecognizerRequest(TouchState touch, View* firstResponder) { - bool res = touch.state == TouchEvent::START; + bool shouldPlayDefaultSound = touch.state == TouchEvent::START; + for (GestureRecognizer* recognizer : getGestureRecognizers()) { - GestureState state = recognizer->recognitionLoop(touch, this); - if (res) - res &= recognizer->isEnabled() && recognizer->soundOnTouch(); + if (!recognizer->isEnabled()) + continue; + + GestureState state = recognizer->recognitionLoop(touch, this, &shouldPlayDefaultSound); if (state == GestureState::START) firstResponder->interruptGestures(true); } if (parent) - res &= parent->gestureRecognizerRequest(touch, firstResponder); + shouldPlayDefaultSound &= parent->gestureRecognizerRequest(touch, firstResponder); - return res; + return shouldPlayDefaultSound; } void View::frame(FrameContext* ctx) From 961e3cf77579048e3378abcb6e0968917326c239 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Tue, 2 Mar 2021 19:42:56 +0300 Subject: [PATCH 25/59] Review changes --- library/include/borealis.hpp | 4 ++-- library/include/borealis/core/gesture.hpp | 4 ++-- library/include/borealis/core/platform.hpp | 2 +- library/include/borealis/core/touch/pan_gesture.hpp | 12 ++++++------ .../include/borealis/platforms/glfw/glfw_input.hpp | 1 - library/lib/core/touch/pan_gesture.cpp | 4 ++-- library/lib/platforms/glfw/glfw_input.cpp | 2 ++ library/lib/platforms/switch/switch_input.cpp | 7 ++++--- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index 591ff59a..aae80f3d 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -42,7 +42,7 @@ #include #include -//Views +// Views #include #include #include @@ -54,6 +54,6 @@ #include #include -//Gestures +// Gestures #include #include diff --git a/library/include/borealis/core/gesture.hpp b/library/include/borealis/core/gesture.hpp index 0ed2711d..2bfca353 100644 --- a/library/include/borealis/core/gesture.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -31,7 +31,7 @@ enum class GestureState UNSURE, // Gesture started recognition and not sure if it should interupt other recognizers START, // Gesture sure that it match to conditions and will interupt other recognizers STAY, // Gesture in process, user still hold finger on screen - END, // User released it's finger from screen, final frame of gesture + END, // User released their finger from screen, final frame of gesture FAILED, // Gesture failed conditions }; @@ -42,7 +42,7 @@ class GestureRecognizer // Main recognition loop, for internal usage only, should not be called anywhere, but Application virtual GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound); - // Interupt this recognizer + // Interrupt this recognizer // If onlyIfUnsureState == true recognizer will be interupted // only if current state is UNSURE void interrupt(bool onlyIfUnsureState); diff --git a/library/include/borealis/core/platform.hpp b/library/include/borealis/core/platform.hpp index 1db855e6..5c274bfa 100644 --- a/library/include/borealis/core/platform.hpp +++ b/library/include/borealis/core/platform.hpp @@ -92,7 +92,7 @@ class Platform virtual InputManager* getInputManager() = 0; /** - * Returns the FontLoader for the platform. + * Returns the FontLoader for the platform. * Cannot return nullptr. */ virtual FontLoader* getFontLoader() = 0; diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index e8fce4da..0b1be299 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -29,17 +29,17 @@ enum class PanAxis { HORIZONTAL, // Triggers only on horizontal coordinate changes VERTICAL, // Triggers only on vertical coordinate changes - NONE, // Any movement allowed + ANY, // Any movement allowed }; -struct position +struct PanPosition { float x; float y; }; // Contains info about acceleration on pan ends -struct pan_acceleration +struct PanAcceleration { // distances in pixels float distanceX; @@ -84,7 +84,7 @@ class PanGestureRecognizer : public GestureRecognizer PanAxis getAxis() const { return this->axis; } // Get acceleration info, actual data only when current state is END - pan_acceleration getAcceleration() const { return this->acceleration; } + PanAcceleration getAcceleration() const { return this->acceleration; } private: PanGestureRespond respond; @@ -95,8 +95,8 @@ class PanGestureRecognizer : public GestureRecognizer float deltaX; float deltaY; PanAxis axis; - pan_acceleration acceleration; - std::vector posHistory; + PanAcceleration acceleration; + std::vector posHistory; GestureState lastState; }; diff --git a/library/include/borealis/platforms/glfw/glfw_input.hpp b/library/include/borealis/platforms/glfw/glfw_input.hpp index f3dc6544..c20043e2 100644 --- a/library/include/borealis/platforms/glfw/glfw_input.hpp +++ b/library/include/borealis/platforms/glfw/glfw_input.hpp @@ -36,7 +36,6 @@ class GLFWInputManager : public InputManager private: GLFWwindow* window; - TouchState oldTouch; }; }; diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index 74f8b56a..e0981793 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -83,7 +83,7 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, if (fabs(deltaX) < fabs(deltaY)) this->state = GestureState::START; break; - case PanAxis::NONE: + case PanAxis::ANY: this->state = GestureState::START; break; } @@ -127,7 +127,7 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, } // Add current state to history - posHistory.insert(posHistory.begin(), position { .x = this->x, .y = this->y }); + posHistory.insert(posHistory.begin(), PanPosition { .x = this->x, .y = this->y }); while (posHistory.size() > HISTORY_LIMIT) { posHistory.pop_back(); diff --git a/library/lib/platforms/glfw/glfw_input.cpp b/library/lib/platforms/glfw/glfw_input.cpp index 93ad1738..5f6fe8e6 100644 --- a/library/lib/platforms/glfw/glfw_input.cpp +++ b/library/lib/platforms/glfw/glfw_input.cpp @@ -120,6 +120,8 @@ void GLFWInputManager::updateTouchState(TouchState* state) state->x = x / Application::windowScale; state->y = y / Application::windowScale; + + static TouchState oldTouch; int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); if (clickState == GLFW_PRESS) diff --git a/library/lib/platforms/switch/switch_input.cpp b/library/lib/platforms/switch/switch_input.cpp index 27f977d9..02f185be 100644 --- a/library/lib/platforms/switch/switch_input.cpp +++ b/library/lib/platforms/switch/switch_input.cpp @@ -70,10 +70,11 @@ void SwitchInputManager::updateControllerState(ControllerState* state) void SwitchInputManager::updateTouchState(TouchState* state) { // Get gamepad state - float x = oldTouch.x; - float y = oldTouch.y; + static float x = oldTouch.x; + static float y = oldTouch.y; - HidTouchScreenState hidState = { 0 }; + static HidTouchScreenState hidState = { 0 }; + if (hidGetTouchScreenStates(&hidState, 1)) { if (hidState.count > 0) From 6a5ff7c93ae4f515833c221641428010b21b5fb8 Mon Sep 17 00:00:00 2001 From: XITRIX Date: Sun, 7 Mar 2021 00:33:38 +0300 Subject: [PATCH 26/59] Added structs for gesture callbacks --- demo/captioned_image.cpp | 2 +- .../borealis/core/touch/pan_gesture.hpp | 70 +++++++++---------- .../borealis/core/touch/tap_gesture.hpp | 12 +++- library/lib/core/touch/pan_gesture.cpp | 22 +++++- library/lib/core/touch/tap_gesture.cpp | 18 +++-- library/lib/views/button.cpp | 6 +- library/lib/views/scrolling_frame.cpp | 16 ++--- library/lib/views/sidebar.cpp | 6 +- 8 files changed, 92 insertions(+), 60 deletions(-) diff --git a/demo/captioned_image.cpp b/demo/captioned_image.cpp index 2633884f..9257b473 100644 --- a/demo/captioned_image.cpp +++ b/demo/captioned_image.cpp @@ -39,7 +39,7 @@ CaptionedImage::CaptionedImage() this->forwardXMLAttribute("caption", this->label, "text"); - this->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureRecognizer* recognizer) { + this->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureStatus status) { brls::Application::giveFocus(this); return true; })); diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index 0b1be299..af810153 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -21,8 +21,35 @@ namespace brls { -class PanGestureRecognizer; -typedef std::function PanGestureRespond; +// Contains info about acceleration on pan ends +struct PanAcceleration +{ + // distances in pixels + float distanceX; + float distanceY; + + // times to cover the distance + float timeX; + float timeY; +}; + +// Current status of gesture +struct PanGestureStatus +{ + GestureState state; // Gesture state + float x; // Current X position + float y; // Current Y position + float startX; // Start X position + float startY; // Start Y position + float deltaX; // Difference between current and previous positions by X + float deltaY; // Difference between current and previous positions by Y + + // Acceleration info, NOT NULL ONLY from + // gesture callback and when current state is END + PanAcceleration acceleration; +}; + +typedef std::function PanGestureRespond; // Axis of pan recognition start conditions enum class PanAxis @@ -38,18 +65,6 @@ struct PanPosition float y; }; -// Contains info about acceleration on pan ends -struct PanAcceleration -{ - // distances in pixels - float distanceX; - float distanceY; - - // times to cover the distance - float timeX; - float timeY; -}; - // Pan recognizer // UNSURE: while touch not moved enough to recognize it as pan // START: gesture has been recognized @@ -62,29 +77,11 @@ class PanGestureRecognizer : public GestureRecognizer PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; - // Get current X position - float getX() const { return x; } - - // Get current Y position - float getY() const { return y; } - - // Get start X position - float getStartX() const { return startX; } - - // Get start Y position - float getStartY() const { return startY; } - - // Get difference between current and previous positions by X - float getDeltaX() const { return deltaX; } - - // Get difference between current and previous positions by Y - float getDeltaY() const { return deltaY; } - - // + // Get pan gesture axis PanAxis getAxis() const { return this->axis; } - - // Get acceleration info, actual data only when current state is END - PanAcceleration getAcceleration() const { return this->acceleration; } + + // Get current state of recognizer + PanGestureStatus getCurrentStatus(); private: PanGestureRespond respond; @@ -95,7 +92,6 @@ class PanGestureRecognizer : public GestureRecognizer float deltaX; float deltaY; PanAxis axis; - PanAcceleration acceleration; std::vector posHistory; GestureState lastState; }; diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index 97b91e3c..2eb4324b 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -21,8 +21,13 @@ namespace brls { -class TapGestureRecognizer; -typedef std::function TapGestureRespond; +struct TapGestureStatus +{ + GestureState state; // Gesture state + float x; // Current X position + float y; // Current Y position +}; +typedef std::function TapGestureRespond; // Tap recognizer // UNSURE: while touch moves inside of View bounds @@ -35,6 +40,9 @@ class TapGestureRecognizer : public GestureRecognizer public: TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; + + // Get current state of recognizer + TapGestureStatus getCurrentStatus(); private: TapGestureRespond respond; diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index e0981793..284df84d 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -42,7 +42,7 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { if (respond && this->state != lastState) - this->respond(this); + this->respond(getCurrentStatus()); lastState = this->state; return this->state; @@ -98,6 +98,8 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, } // If last touch frame, calculate acceleration + + static PanAcceleration acceleration; if (this->state == GestureState::END) { float time = posHistory.size() / FPS; @@ -117,7 +119,9 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, if (this->state == GestureState::START || this->state == GestureState::STAY || this->state == GestureState::END) { - this->respond(this); + PanGestureStatus state = getCurrentStatus(); + state.acceleration = acceleration; + this->respond(state); } break; @@ -136,4 +140,18 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, return this->state; } +PanGestureStatus PanGestureRecognizer::getCurrentStatus() +{ + return PanGestureStatus + { + .state = this->state, + .x = this->x, + .y = this->y, + .startX = this->startX, + .startY = this->startY, + .deltaX = this->deltaX, + .deltaY = this->deltaY + }; +} + }; diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index 955db16d..7e0f7674 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -37,7 +37,7 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { if (respond && !this->callbackOnEndOnly && this->state != lastState) - this->respond(this); + this->respond(getCurrentStatus()); lastState = this->state; return this->state; @@ -52,7 +52,7 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, this->y = touch.y; if (respond && !this->callbackOnEndOnly) - *shouldPlayDefaultSound &= this->respond(this); + *shouldPlayDefaultSound &= this->respond(getCurrentStatus()); break; case TouchEvent::STAY: // Check if touch is out view's bounds @@ -61,13 +61,13 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, { this->state = GestureState::FAILED; if (respond && !this->callbackOnEndOnly) - this->respond(this); + this->respond(getCurrentStatus()); } break; case TouchEvent::END: this->state = GestureState::END; if (respond) - this->respond(this); + this->respond(getCurrentStatus()); break; case TouchEvent::NONE: this->state = GestureState::FAILED; @@ -78,4 +78,14 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, return this->state; } +TapGestureStatus TapGestureRecognizer::getCurrentStatus() +{ + return TapGestureStatus + { + .state = this->state, + .x = this->x, + .y = this->y, + }; +} + }; diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index d5dfabdc..b7bcfe08 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -88,7 +88,7 @@ Button::Button() this->applyStyle(); - this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) { + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureStatus status) { Application::giveFocus(this); for (auto& action : getActions()) { @@ -97,9 +97,9 @@ Button::Button() if (action.available) { - this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); + this->playClickAnimation(status.state != GestureState::UNSURE); - switch (recogniser->getState()) + switch (status.state) { case GestureState::UNSURE: Application::getAudioPlayer()->play(SOUND_FOCUS_CHANGE); diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 242527ae..6da700f2 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -63,14 +63,14 @@ ScrollingFrame::ScrollingFrame() this->setMaximumAllowedXMLElements(1); - addGestureRecognizer(new PanGestureRecognizer([this](PanGestureRecognizer* pan) { + addGestureRecognizer(new PanGestureRecognizer([this](PanGestureStatus state) { float contentHeight = this->getContentHeight(); static float startY; - if (pan->getState() == GestureState::START) + if (state.state == GestureState::START) startY = this->scrollY * contentHeight; - float newScroll = (startY - (pan->getY() - pan->getStartY())) / contentHeight; + float newScroll = (startY - (state.y - state.startY)) / contentHeight; float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; // Bottom boundary @@ -82,25 +82,25 @@ ScrollingFrame::ScrollingFrame() newScroll = 0.0f; // Start animation - if (pan->getState() != GestureState::END) + if (state.state != GestureState::END) startScrolling(true, newScroll); else { - float time = pan->getAcceleration().timeY * 1000.0f; - float newPos = this->scrollY * contentHeight + pan->getAcceleration().distanceY; + float time = state.acceleration.timeY * 1000.0f; + float newPos = this->scrollY * contentHeight + state.acceleration.distanceY; // Bottom boundary float bottomLimit = contentHeight - this->getScrollingAreaHeight(); if (newPos > bottomLimit) { - time = time * (1 - fabs(newPos - bottomLimit) / fabs(pan->getAcceleration().distanceY)); + time = time * (1 - fabs(newPos - bottomLimit) / fabs(state.acceleration.distanceY)); newPos = bottomLimit; } // Top boundary if (newPos < 0) { - time = time * (1 - fabs(newPos) / fabs(pan->getAcceleration().distanceY)); + time = time * (1 - fabs(newPos) / fabs(state.acceleration.distanceY)); newPos = 0; } diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index cce8f762..8e06a51c 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -77,13 +77,13 @@ SidebarItem::SidebarItem() }, false, SOUND_CLICK_SIDEBAR); - this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureRecognizer* recogniser) { + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureStatus status) { if (this->active) return true; - this->playClickAnimation(recogniser->getState() != GestureState::UNSURE); + this->playClickAnimation(status.state != GestureState::UNSURE); - switch (recogniser->getState()) + switch (status.state) { case GestureState::UNSURE: Application::getAudioPlayer()->play(SOUND_FOCUS_SIDEBAR); From 154a13c452ca1e96d1769c8c7eb616b6d8977b6a Mon Sep 17 00:00:00 2001 From: XITRIX Date: Fri, 12 Mar 2021 20:15:15 +0300 Subject: [PATCH 27/59] Touch improvements - Added metrics - Touch phase calculation logic moved in core - Several touch structs renamed to match their purpose --- library/include/borealis/core/box.hpp | 2 +- library/include/borealis/core/gesture.hpp | 2 +- library/include/borealis/core/input.hpp | 35 +++-- .../include/borealis/core/metrics/point.hpp | 65 +++++++++ .../include/borealis/core/metrics/rect.hpp | 84 +++++++++++ .../include/borealis/core/metrics/size.hpp | 65 +++++++++ .../borealis/core/touch/pan_gesture.hpp | 34 ++--- .../borealis/core/touch/tap_gesture.hpp | 8 +- library/include/borealis/core/view.hpp | 27 ++-- .../borealis/platforms/glfw/glfw_input.hpp | 2 +- .../platforms/switch/switch_input.hpp | 4 +- library/lib/core/application.cpp | 18 ++- library/lib/core/box.cpp | 6 +- library/lib/core/gesture.cpp | 2 +- library/lib/core/input.cpp | 53 +++++++ library/lib/core/touch/pan_gesture.cpp | 53 ++++--- library/lib/core/touch/tap_gesture.cpp | 22 ++- library/lib/core/view.cpp | 130 +++++++++--------- library/lib/platforms/glfw/glfw_input.cpp | 33 +---- library/lib/platforms/switch/switch_input.cpp | 40 +----- library/lib/views/scrolling_frame.cpp | 10 +- library/meson.build | 1 + 22 files changed, 451 insertions(+), 245 deletions(-) create mode 100644 library/include/borealis/core/metrics/point.hpp create mode 100644 library/include/borealis/core/metrics/rect.hpp create mode 100644 library/include/borealis/core/metrics/size.hpp create mode 100644 library/lib/core/input.cpp diff --git a/library/include/borealis/core/box.hpp b/library/include/borealis/core/box.hpp index 0fb2e57f..a3edfcf3 100644 --- a/library/include/borealis/core/box.hpp +++ b/library/include/borealis/core/box.hpp @@ -65,7 +65,7 @@ class Box : public View void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; View* getDefaultFocus() override; - View* hitTest(double x, double y) override; + View* hitTest(Point point) override; View* getNextFocus(FocusDirection direction, View* currentView) override; void willAppear(bool resetState) override; void willDisappear(bool resetState) override; diff --git a/library/include/borealis/core/gesture.hpp b/library/include/borealis/core/gesture.hpp index 2bfca353..e2e38039 100644 --- a/library/include/borealis/core/gesture.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -40,7 +40,7 @@ class GestureRecognizer { public: // Main recognition loop, for internal usage only, should not be called anywhere, but Application - virtual GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound); + virtual GestureState recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound); // Interrupt this recognizer // If onlyIfUnsureState == true recognizer will be interupted diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index 994f44ab..79e77b8a 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -1,5 +1,6 @@ /* Copyright 2021 natinusala + Copyright 2021 XITRIX Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,6 +17,8 @@ #pragma once +#include + namespace brls { @@ -66,13 +69,14 @@ enum ControllerAxis }; // Represents the state of the controller (a gamepad or a keyboard) in the current frame -typedef struct ControllerState +struct ControllerState { bool buttons[_BUTTON_MAX]; // true: pressed float axes[_AXES_MAX]; // from 0.0f to 1.0f -} ControllerState; +}; -enum class TouchEvent +// Represents a touch phase in the current frame +enum class TouchPhase { START, STAY, @@ -80,11 +84,19 @@ enum class TouchEvent NONE, }; -typedef struct TouchState +// Contains raw touch data +struct RawTouch { - TouchEvent state; - float x, y; -} TouchState; + bool pressed; + Point position; +}; + +// Contains touch data filled with it's current phase +struct Touch +{ + TouchPhase phase; + Point position; +}; // Interface responsible for reporting input state to the application - button presses, // axis position and touch screen state @@ -99,9 +111,14 @@ class InputManager virtual void updateControllerState(ControllerState* state) = 0; /** - * Called once every frame to fill the given TouchState struct with the touch state. + * Called once every frame to fill the given Touch struct with the touch state. + */ + virtual void updateTouchState(RawTouch* state) = 0; + + /** + * Calculate current touch phase based on it's previous state */ - virtual void updateTouchState(TouchState* state) = 0; + static Touch computeTouchState(RawTouch currentTouch, Touch lastFrameState); }; }; // namespace brls diff --git a/library/include/borealis/core/metrics/point.hpp b/library/include/borealis/core/metrics/point.hpp new file mode 100644 index 00000000..4014da63 --- /dev/null +++ b/library/include/borealis/core/metrics/point.hpp @@ -0,0 +1,65 @@ +/* + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +namespace brls +{ + +// A structure that contains a point in a two-dimensional coordinate system. +struct Point +{ + float x; // The x-coordinate of the point. + float y; // The y-coordinate of the point. + + // Creates a point with location (0,0). + Point(): Point(0.0f, 0.0f) + { } + + // Creates a point with coordinates specified as float values. + Point(float x, float y) + { + this->x = x; + this->y = y; + } + + Point operator+(const Point& a) const + { + return Point(a.x+x, a.y+y); + } + + Point operator-(const Point& a) const + { + return Point(a.x-x, a.y-y); + } + + Point operator/(const float& a) const + { + return Point(x/a, y/a); + } + + Point operator*(const float& a) const + { + return Point(x*a, y*a); + } + + bool operator==(const Point& other) const { + return x == other.x && y == other.y; + } +}; + + +} // namespace brls diff --git a/library/include/borealis/core/metrics/rect.hpp b/library/include/borealis/core/metrics/rect.hpp new file mode 100644 index 00000000..5d40f229 --- /dev/null +++ b/library/include/borealis/core/metrics/rect.hpp @@ -0,0 +1,84 @@ +/* + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include +#include + +namespace brls +{ + +// A structure that contains the location and dimensions of a rectangle. +struct Rect +{ + Point origin; // A point that specifies the coordinates of the rectangle’s origin. + Size size; // A size that specifies the height and width of the rectangle. + + // Creates a rectangle with origin (0,0) and size (0,0). + Rect() + { + this->origin = Point(); + this->size = Size(); + } + + // Creates a rectangle with the specified origin and size. + Rect(Point origin, Size size) + { + this->origin = origin; + this->size = size; + } + + // Creates a rectangle with coordinates and dimensions specified as float values. + Rect(float x, float y, float width, float height) + { + this->origin = Point(x, y); + this->size = Size(width, height); + } + + float getWidth() const { return this->size.width; } // Returns the width of a rectangle. + float getHeight() const { return this->size.height; } // Returns the height of a rectangle. + + float getMinX() const { return this->origin.x; } // Returns the smallest value for the x-coordinate of the rectangle. + float getMinY() const { return this->origin.y; } // Returns the smallest value for the y-coordinate of the rectangle. + + float getMidX() const { return this->origin.x + getWidth() / 2; } // Returns the x-coordinate that establishes the center of a rectangle. + float getMidY() const { return this->origin.y + getHeight() / 2; } // Returns the y-coordinate that establishes the center of the rectangle. + + float getMaxX() const { return this->origin.x + getWidth(); } // Returns the largest value of the x-coordinate for the rectangle. + float getMaxY() const { return this->origin.y + getHeight(); } // Returns the largest value for the y-coordinate of the rectangle. + + bool operator==(const Rect& other) const { + return origin == other.origin && size == other.size; + } + + + // Returns true if point is inside this Rect + bool pointInside(Point point) + { + return getMinX() <= point.x && getMaxX() >= point.x && getMinY() <= point.y && getMaxY() >= point.y; + } + + std::string describe() + { + return "X: " + std::to_string((int)getMinX()) + + ", Y: " + std::to_string((int)getMinY()) + + ", W: " + std::to_string((int)getWidth()) + + ", H: " + std::to_string((int)getHeight()); + } +}; + +} // namespace brls diff --git a/library/include/borealis/core/metrics/size.hpp b/library/include/borealis/core/metrics/size.hpp new file mode 100644 index 00000000..91443f89 --- /dev/null +++ b/library/include/borealis/core/metrics/size.hpp @@ -0,0 +1,65 @@ +/* + Copheightright 2021 widthITRIwidth + + Licensed under the Apache License, Version 2.0 (the "License"); + heightou maheight not use this file ewidthcept in compliance with the License. + heightou maheight obtain a copheight of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required bheight applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANheight KIND, either ewidthpress or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +namespace brls +{ + +// A structure that contains width and height values. +struct Size +{ + float width; // A width value. + float height; // A height value. + + // Creates a size with zero width and height. + Size(): Size(0.0f, 0.0f) + { } + + // Creates a size with dimensions specified as float values. + Size(float width, float height) + { + this->width = width; + this->height = height; + } + + Size operator+(const Size& a) const + { + return Size(a.width+width, a.height+height); + } + + Size operator-(const Size& a) const + { + return Size(a.width-width, a.height-height); + } + + Size operator/(const float& a) const + { + return Size(width/a, height/a); + } + + Size operator*(const float& a) const + { + return Size(width*a, height*a); + } + + bool operator==(const Size& other) const { + return width == other.width && height == other.height; + } +}; + + +} // namespace brls diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index af810153..6a9d5bb0 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -25,24 +25,19 @@ namespace brls struct PanAcceleration { // distances in pixels - float distanceX; - float distanceY; + Point distance; // times to cover the distance - float timeX; - float timeY; + Point time; }; // Current status of gesture struct PanGestureStatus { GestureState state; // Gesture state - float x; // Current X position - float y; // Current Y position - float startX; // Start X position - float startY; // Start Y position - float deltaX; // Difference between current and previous positions by X - float deltaY; // Difference between current and previous positions by Y + Point position; // Current position + Point startPosition; // Start X position + Point delta; // Difference between current and previous positions by X // Acceleration info, NOT NULL ONLY from // gesture callback and when current state is END @@ -59,12 +54,6 @@ enum class PanAxis ANY, // Any movement allowed }; -struct PanPosition -{ - float x; - float y; -}; - // Pan recognizer // UNSURE: while touch not moved enough to recognize it as pan // START: gesture has been recognized @@ -75,7 +64,7 @@ class PanGestureRecognizer : public GestureRecognizer { public: PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); - GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; + GestureState recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) override; // Get pan gesture axis PanAxis getAxis() const { return this->axis; } @@ -85,14 +74,11 @@ class PanGestureRecognizer : public GestureRecognizer private: PanGestureRespond respond; - float x; - float y; - float startX; - float startY; - float deltaX; - float deltaY; + Point position; + Point startPosition; + Point delta; PanAxis axis; - std::vector posHistory; + std::vector posHistory; GestureState lastState; }; diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index 2eb4324b..26309e0b 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -24,8 +24,7 @@ namespace brls struct TapGestureStatus { GestureState state; // Gesture state - float x; // Current X position - float y; // Current Y position + Point position; // Current position }; typedef std::function TapGestureRespond; @@ -39,15 +38,14 @@ class TapGestureRecognizer : public GestureRecognizer { public: TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); - GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; + GestureState recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) override; // Get current state of recognizer TapGestureStatus getCurrentStatus(); private: TapGestureRespond respond; - float x; - float y; + Point position; bool callbackOnEndOnly; GestureState lastState; }; diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index 9afa39e4..0de4fd17 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -162,12 +163,12 @@ class View ViewBackground background = ViewBackground::NONE; void drawBackground(NVGcontext* vg, FrameContext* ctx, Style style); - void drawShadow(NVGcontext* vg, FrameContext* ctx, Style style, float x, float y, float width, float height); - void drawBorder(NVGcontext* vg, FrameContext* ctx, Style style, float x, float y, float width, float height); + void drawShadow(NVGcontext* vg, FrameContext* ctx, Style style, Rect frame); + void drawBorder(NVGcontext* vg, FrameContext* ctx, Style style, Rect frame); void drawHighlight(NVGcontext* vg, Theme theme, float alpha, Style style, bool background); - void drawClickAnimation(NVGcontext* vg, FrameContext* ctx, float x, float y, float width, float height); - void drawWireframe(FrameContext* ctx, float x, float y, float width, float height); - void drawLine(FrameContext* ctx, float x, float y, float width, float height); + void drawClickAnimation(NVGcontext* vg, FrameContext* ctx, Rect frame); + void drawWireframe(FrameContext* ctx, Rect frame); + void drawLine(FrameContext* ctx, Rect frame); Animatable highlightAlpha = 0.0f; float highlightPadding = 0.0f; @@ -193,11 +194,9 @@ class View bool hideHighlightBackground = false; bool detached = false; - float detachedOriginX = 0.0f; - float detachedOriginY = 0.0f; + Point detachedOrigin; - float translationX = 0.0f; - float translationY = 0.0f; + Point translation; bool wireframeEnabled = false; @@ -304,6 +303,7 @@ class View void shakeHighlight(FocusDirection direction); + Rect getFrame(); float getX(); float getY(); float getWidth(); @@ -1061,7 +1061,7 @@ class View * that needs to play it's own click sound, * so play default is allowed */ - bool gestureRecognizerRequest(TouchState touch, View* firstResponder); + bool gestureRecognizerRequest(Touch touch, View* firstResponder); /** * Called each frame @@ -1178,11 +1178,6 @@ class View */ virtual View* getDefaultFocus(); - /** - * Returns true if point hits inside this view - */ - virtual bool pointInside(double x, double y); - /** * Returns the view to focus with the corresponding screen coordinates in the view or its children, * or nullptr if it hasn't been found. @@ -1190,7 +1185,7 @@ class View * Research is done recursively by traversing the tree starting from this view. * This view's parents are not traversed. */ - virtual View* hitTest(double x, double y); + virtual View* hitTest(Point point); /** * Returns the next view to focus given the requested direction diff --git a/library/include/borealis/platforms/glfw/glfw_input.hpp b/library/include/borealis/platforms/glfw/glfw_input.hpp index c20043e2..3c3d01d8 100644 --- a/library/include/borealis/platforms/glfw/glfw_input.hpp +++ b/library/include/borealis/platforms/glfw/glfw_input.hpp @@ -32,7 +32,7 @@ class GLFWInputManager : public InputManager void updateControllerState(ControllerState* state) override; - void updateTouchState(TouchState* state) override; + void updateTouchState(RawTouch* state) override; private: GLFWwindow* window; diff --git a/library/include/borealis/platforms/switch/switch_input.hpp b/library/include/borealis/platforms/switch/switch_input.hpp index 21301c0f..429ca003 100644 --- a/library/include/borealis/platforms/switch/switch_input.hpp +++ b/library/include/borealis/platforms/switch/switch_input.hpp @@ -32,11 +32,11 @@ class SwitchInputManager : public InputManager void updateControllerState(ControllerState* state); - void updateTouchState(TouchState* state); + void updateTouchState(RawTouch* state); private: PadState padState; - TouchState oldTouch; + Touch oldTouch; }; } // namespace brls diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 3545948d..053269cf 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -163,25 +163,29 @@ bool Application::mainLoop() // Input ControllerState controllerState = {}; - TouchState touchState = {}; + RawTouch rawTouch = {}; InputManager* inputManager = Application::platform->getInputManager(); - inputManager->updateTouchState(&touchState); + inputManager->updateTouchState(&rawTouch); inputManager->updateControllerState(&controllerState); + static Touch oldTouch; + Touch touchState = InputManager::computeTouchState(rawTouch, oldTouch); + oldTouch = touchState; + // Touch controller events - switch (touchState.state) + switch (touchState.phase) { - case TouchEvent::START: - Logger::debug("Touched at X: " + std::to_string(touchState.x) + ", Y: " + std::to_string(touchState.y)); + case TouchPhase::START: + Logger::debug("Touched at X: " + std::to_string(touchState.position.x) + ", Y: " + std::to_string(touchState.position.y)); Application::setInputType(InputType::TOUCH); // Search for first responder, which will be the root of recognition tree firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] ->getContentView() - ->hitTest(touchState.x, touchState.y); + ->hitTest(touchState.position); break; - case TouchEvent::NONE: + case TouchPhase::NONE: firstResponder = nullptr; break; default: diff --git a/library/lib/core/box.cpp b/library/lib/core/box.cpp index 3b36da61..1e5b2c77 100644 --- a/library/lib/core/box.cpp +++ b/library/lib/core/box.cpp @@ -316,15 +316,15 @@ View* Box::getDefaultFocus() return nullptr; } -View* Box::hitTest(double x, double y) +View* Box::hitTest(Point point) { // Check if touch fits in view frame - if (pointInside(x, y)) + if (getFrame().pointInside(point)) { Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); for (View* child : this->children) { - View* result = child->hitTest(x, y); + View* result = child->hitTest(point); if (result) return result; diff --git a/library/lib/core/gesture.cpp b/library/lib/core/gesture.cpp index 48fc7498..7a1ae37d 100644 --- a/library/lib/core/gesture.cpp +++ b/library/lib/core/gesture.cpp @@ -19,7 +19,7 @@ namespace brls { -GestureState GestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) +GestureState GestureRecognizer::recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) { return GestureState::FAILED; } diff --git a/library/lib/core/input.cpp b/library/lib/core/input.cpp new file mode 100644 index 00000000..12501ddb --- /dev/null +++ b/library/lib/core/input.cpp @@ -0,0 +1,53 @@ +/* + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include + +namespace brls +{ + +Touch InputManager::computeTouchState(RawTouch currentTouch, Touch lastFrameState) +{ + Touch result = {}; + result.position = currentTouch.position; + + if (currentTouch.pressed) + { + if (lastFrameState.phase == TouchPhase::START || lastFrameState.phase == TouchPhase::STAY) + { + result.phase = TouchPhase::STAY; + } + else + { + result.phase = TouchPhase::START; + } + } + else + { + if (lastFrameState.phase == TouchPhase::END || lastFrameState.phase == TouchPhase::NONE) + { + result.phase = TouchPhase::NONE; + } + else + { + result.phase = TouchPhase::END; + } + } + + return result; +} + +} // namespace brls \ No newline at end of file diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index 284df84d..e1390ee1 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -30,14 +30,14 @@ PanGestureRecognizer::PanGestureRecognizer(PanGestureRespond respond, PanAxis ax { } -GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) +GestureState PanGestureRecognizer::recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) { if (!enabled) return GestureState::FAILED; // If not first touch frame and state is // INTERRUPTED or FAILED, stop recognition - if (touch.state != TouchEvent::START) + if (touch.phase != TouchPhase::START) { if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { @@ -49,38 +49,34 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, } } - switch (touch.state) + switch (touch.phase) { - case TouchEvent::START: + case TouchPhase::START: this->posHistory.clear(); this->state = GestureState::UNSURE; - this->startX = touch.x; - this->startY = touch.y; - this->x = touch.x; - this->y = touch.y; + this->startPosition = touch.position; + this->position = touch.position; break; - case TouchEvent::STAY: - case TouchEvent::END: + case TouchPhase::STAY: + case TouchPhase::END: - this->deltaX = touch.x - this->x; - this->deltaY = touch.y - this->y; + this->delta = touch.position - this->position; - this->x = touch.x; - this->y = touch.y; + this->position = touch.position; // Check if pass any condition to set state START if (this->state == GestureState::UNSURE) { - if (fabs(this->startX - touch.x) > MAX_DELTA_MOVEMENT || fabs(this->startY - touch.y) > MAX_DELTA_MOVEMENT) + if (fabs(this->startPosition.x - touch.position.x) > MAX_DELTA_MOVEMENT || fabs(this->startPosition.y - touch.position.y) > MAX_DELTA_MOVEMENT) { switch (axis) { case PanAxis::HORIZONTAL: - if (fabs(deltaX) > fabs(deltaY)) + if (fabs(delta.x) > fabs(delta.y)) this->state = GestureState::START; break; case PanAxis::VERTICAL: - if (fabs(deltaX) < fabs(deltaY)) + if (fabs(delta.x) < fabs(delta.y)) this->state = GestureState::START; break; case PanAxis::ANY: @@ -91,7 +87,7 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, } else { - if (touch.state == TouchEvent::STAY) + if (touch.phase == TouchPhase::STAY) this->state = GestureState::STAY; else this->state = GestureState::END; @@ -110,11 +106,11 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, float velocityX = distanceX / time; float velocityY = distanceY / time; - acceleration.timeX = -fabs(velocityX) / PAN_SCROLL_ACCELERATION; - acceleration.timeY = -fabs(velocityY) / PAN_SCROLL_ACCELERATION; + acceleration.time.x = -fabs(velocityX) / PAN_SCROLL_ACCELERATION; + acceleration.time.y = -fabs(velocityY) / PAN_SCROLL_ACCELERATION; - acceleration.distanceX = velocityX * acceleration.timeX / 2; - acceleration.distanceY = velocityY * acceleration.timeY / 2; + acceleration.distance.x = velocityX * acceleration.time.x / 2; + acceleration.distance.y = velocityY * acceleration.time.y / 2; } if (this->state == GestureState::START || this->state == GestureState::STAY || this->state == GestureState::END) @@ -125,13 +121,13 @@ GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, } break; - case TouchEvent::NONE: + case TouchPhase::NONE: this->state = GestureState::FAILED; break; } // Add current state to history - posHistory.insert(posHistory.begin(), PanPosition { .x = this->x, .y = this->y }); + posHistory.insert(posHistory.begin(), this->position); while (posHistory.size() > HISTORY_LIMIT) { posHistory.pop_back(); @@ -145,12 +141,9 @@ PanGestureStatus PanGestureRecognizer::getCurrentStatus() return PanGestureStatus { .state = this->state, - .x = this->x, - .y = this->y, - .startX = this->startX, - .startY = this->startY, - .deltaX = this->deltaX, - .deltaY = this->deltaY + .position = this->position, + .startPosition = this->startPosition, + .delta = this->delta, }; } diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index 7e0f7674..0c366fd4 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -25,14 +25,14 @@ TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, bool callb { } -GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) +GestureState TapGestureRecognizer::recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) { if (!enabled) return GestureState::FAILED; // If not first touch frame and state is // INTERRUPTED or FAILED, stop recognition - if (touch.state != TouchEvent::START) + if (touch.phase != TouchPhase::START) { if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { @@ -44,32 +44,31 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, } } - switch (touch.state) + switch (touch.phase) { - case TouchEvent::START: + case TouchPhase::START: this->state = GestureState::UNSURE; - this->x = touch.x; - this->y = touch.y; + this->position = touch.position; if (respond && !this->callbackOnEndOnly) *shouldPlayDefaultSound &= this->respond(getCurrentStatus()); break; - case TouchEvent::STAY: + case TouchPhase::STAY: // Check if touch is out view's bounds // if true, FAIL recognition - if (touch.x < view->getX() || touch.x > view->getX() + view->getWidth() || touch.y < view->getY() || touch.y > view->getY() + view->getHeight()) + if (touch.position.x < view->getX() || touch.position.x > view->getX() + view->getWidth() || touch.position.y < view->getY() || touch.position.y > view->getY() + view->getHeight()) { this->state = GestureState::FAILED; if (respond && !this->callbackOnEndOnly) this->respond(getCurrentStatus()); } break; - case TouchEvent::END: + case TouchPhase::END: this->state = GestureState::END; if (respond) this->respond(getCurrentStatus()); break; - case TouchEvent::NONE: + case TouchPhase::NONE: this->state = GestureState::FAILED; break; } @@ -83,8 +82,7 @@ TapGestureStatus TapGestureRecognizer::getCurrentStatus() return TapGestureStatus { .state = this->state, - .x = this->x, - .y = this->y, + .position = this->position, }; } diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index 4a874eb3..faaf0827 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -110,9 +110,9 @@ void View::addGestureRecognizer(GestureRecognizer* recognizer) this->gestureRecognizers.push_back(recognizer); } -bool View::gestureRecognizerRequest(TouchState touch, View* firstResponder) +bool View::gestureRecognizerRequest(Touch touch, View* firstResponder) { - bool shouldPlayDefaultSound = touch.state == TouchEvent::START; + bool shouldPlayDefaultSound = touch.phase == TouchPhase::START; for (GestureRecognizer* recognizer : getGestureRecognizers()) { @@ -144,10 +144,11 @@ void View::frame(FrameContext* ctx) if (this->themeOverride) ctx->theme = *themeOverride; - float x = this->getX(); - float y = this->getY(); - float width = this->getWidth(); - float height = this->getHeight(); + Rect frame = getFrame(); + float x = frame.getMinX(); + float y = frame.getMinY(); + float width = frame.getWidth(); + float height = frame.getHeight(); if (this->alpha > 0.0f && this->collapseState != 0.0f) { @@ -156,11 +157,11 @@ void View::frame(FrameContext* ctx) // Draw shadow if (this->shadowType != ShadowType::NONE && (this->showShadow || Application::getInputType() == InputType::TOUCH)) - this->drawShadow(ctx->vg, ctx, style, x, y, width, height); + this->drawShadow(ctx->vg, ctx, style, frame); // Draw border if (this->borderThickness > 0.0f) - this->drawBorder(ctx->vg, ctx, style, x, y, width, height); + this->drawBorder(ctx->vg, ctx, style, frame); // Draw highlight background if (this->highlightAlpha > 0.0f && !this->hideHighlightBackground) @@ -168,7 +169,7 @@ void View::frame(FrameContext* ctx) // Draw click animation if (this->clickAlpha > 0.0f) - this->drawClickAnimation(ctx->vg, ctx, x, y, width, height); + this->drawClickAnimation(ctx->vg, ctx, frame); // Collapse clipping if (this->collapseState < 1.0f) @@ -185,9 +186,9 @@ void View::frame(FrameContext* ctx) this->drawHighlight(ctx->vg, ctx->theme, this->highlightAlpha, style, false); if (this->wireframeEnabled) - this->drawWireframe(ctx, x, y, width, height); + this->drawWireframe(ctx, frame); - this->drawLine(ctx, x, y, width, height); + this->drawLine(ctx, frame); //Reset clipping if (this->collapseState < 1.0f) @@ -229,7 +230,7 @@ void View::playClickAnimation(bool reverse) this->clickAlpha.start(); } -void View::drawClickAnimation(NVGcontext* vg, FrameContext* ctx, float x, float y, float width, float height) +void View::drawClickAnimation(NVGcontext* vg, FrameContext* ctx, Rect frame) { Theme theme = ctx->theme; NVGcolor color = theme["brls/click_pulse"]; @@ -240,14 +241,14 @@ void View::drawClickAnimation(NVGcontext* vg, FrameContext* ctx, float x, float nvgBeginPath(vg); if (this->cornerRadius > 0.0f) - nvgRoundedRect(vg, x, y, width, height, this->cornerRadius); + nvgRoundedRect(vg, frame.getMinX(), frame.getMinY(), frame.getWidth(), frame.getHeight(), this->cornerRadius); else - nvgRect(vg, x, y, width, height); + nvgRect(ctx->vg, frame.getMinX(), frame.getMinY(), frame.getWidth(), frame.getHeight()); nvgFill(vg); } -void View::drawLine(FrameContext* ctx, float x, float y, float width, float height) +void View::drawLine(FrameContext* ctx, Rect frame) { // Don't setup and draw empty nvg path if there is no line to draw if (this->lineTop <= 0 && this->lineRight <= 0 && this->lineBottom <= 0 && this->lineLeft <= 0) @@ -257,28 +258,28 @@ void View::drawLine(FrameContext* ctx, float x, float y, float width, float heig nvgFillColor(ctx->vg, a(this->lineColor)); if (this->lineTop > 0) - nvgRect(ctx->vg, x, y, width, this->lineTop); + nvgRect(ctx->vg, frame.getMinX(), frame.getMinY(), frame.size.width, this->lineTop); if (this->lineRight > 0) - nvgRect(ctx->vg, x + width, y, this->lineRight, height); + nvgRect(ctx->vg, frame.getMaxX(), frame.getMinY(), this->lineRight, frame.size.height); if (this->lineBottom > 0) - nvgRect(ctx->vg, x, y + height - this->lineBottom, width, this->lineBottom); + nvgRect(ctx->vg, frame.getMinX(), frame.getMaxY() - this->lineBottom, frame.size.width, this->lineBottom); if (this->lineLeft > 0) - nvgRect(ctx->vg, x - this->lineLeft, y, this->lineLeft, height); + nvgRect(ctx->vg, frame.getMinX() - this->lineLeft, frame.getMinY(), this->lineLeft, frame.size.height); nvgFill(ctx->vg); } -void View::drawWireframe(FrameContext* ctx, float x, float y, float width, float height) +void View::drawWireframe(FrameContext* ctx, Rect frame) { nvgStrokeWidth(ctx->vg, 1); // Outline nvgBeginPath(ctx->vg); nvgStrokeColor(ctx->vg, nvgRGB(0, 0, 255)); - nvgRect(ctx->vg, x, y, width, height); + nvgRect(ctx->vg, frame.getMinX(), frame.getMinY(), frame.getWidth(), frame.getHeight()); nvgStroke(ctx->vg); if (this->hasParent()) @@ -287,13 +288,13 @@ void View::drawWireframe(FrameContext* ctx, float x, float y, float width, float nvgFillColor(ctx->vg, nvgRGB(0, 0, 255)); nvgBeginPath(ctx->vg); - nvgMoveTo(ctx->vg, x, y); - nvgLineTo(ctx->vg, x + width, y + height); + nvgMoveTo(ctx->vg, frame.getMinX(), frame.getMinY()); + nvgLineTo(ctx->vg, frame.getMaxX(), frame.getMaxY()); nvgFill(ctx->vg); nvgBeginPath(ctx->vg); - nvgMoveTo(ctx->vg, x + width, y); - nvgLineTo(ctx->vg, x, y + height); + nvgMoveTo(ctx->vg, frame.getMaxX(), frame.getMinY()); + nvgLineTo(ctx->vg, frame.getMinX(), frame.getMaxY()); nvgFill(ctx->vg); } @@ -308,19 +309,19 @@ void View::drawWireframe(FrameContext* ctx, float x, float y, float width, float // Top if (paddingTop > 0) - nvgRect(ctx->vg, x, y, width, paddingTop); + nvgRect(ctx->vg, frame.getMinX(), frame.getMinY(), frame.getWidth(), paddingTop); // Right if (paddingRight > 0) - nvgRect(ctx->vg, x + width - paddingRight, y, paddingRight, height); + nvgRect(ctx->vg, frame.getMaxX() - paddingRight, frame.getMinY(), paddingRight, frame.getHeight()); // Bottom if (paddingBottom > 0) - nvgRect(ctx->vg, x, y + height - paddingBottom, width, paddingBottom); + nvgRect(ctx->vg, frame.getMinX(), frame.getMaxY() - paddingBottom, frame.getWidth(), paddingBottom); // Left if (paddingLeft > 0) - nvgRect(ctx->vg, x, y, paddingLeft, height); + nvgRect(ctx->vg, frame.getMinX(), frame.getMinY(), paddingLeft, frame.getHeight()); nvgStroke(ctx->vg); @@ -335,33 +336,33 @@ void View::drawWireframe(FrameContext* ctx, float x, float y, float width, float // Top if (marginTop > 0) - nvgRect(ctx->vg, x - marginLeft, y - marginTop, width + marginLeft + marginRight, marginTop); + nvgRect(ctx->vg, frame.getMinX() - marginLeft, frame.getMinY() - marginTop, frame.getWidth() + marginLeft + marginRight, marginTop); // Right if (marginRight > 0) - nvgRect(ctx->vg, x + width, y - marginTop, marginRight, height + marginTop + marginBottom); + nvgRect(ctx->vg, frame.getMaxX(), frame.getMinY() - marginTop, marginRight, frame.getHeight() + marginTop + marginBottom); // Bottom if (marginBottom > 0) - nvgRect(ctx->vg, x - marginLeft, y + height, width + marginLeft + marginRight, marginBottom); + nvgRect(ctx->vg, frame.getMinX() - marginLeft, frame.getMaxY(), frame.getWidth() + marginLeft + marginRight, marginBottom); // Left if (marginLeft > 0) - nvgRect(ctx->vg, x - marginLeft, y - marginTop, marginLeft, height + marginTop + marginBottom); + nvgRect(ctx->vg, frame.getMinX() - marginLeft, frame.getMinY() - marginTop, marginLeft, frame.getHeight() + marginTop + marginBottom); nvgStroke(ctx->vg); } -void View::drawBorder(NVGcontext* vg, FrameContext* ctx, Style style, float x, float y, float width, float height) +void View::drawBorder(NVGcontext* vg, FrameContext* ctx, Style style, Rect frame) { nvgBeginPath(vg); nvgStrokeColor(vg, this->borderColor); nvgStrokeWidth(vg, this->borderThickness); - nvgRoundedRect(vg, x, y, width, height, this->cornerRadius); + nvgRoundedRect(vg, frame.getMinX(), frame.getMinY(), frame.getWidth(), frame.getHeight(), this->cornerRadius); nvgStroke(vg); } -void View::drawShadow(NVGcontext* vg, FrameContext* ctx, Style style, float x, float y, float width, float height) +void View::drawShadow(NVGcontext* vg, FrameContext* ctx, Style style, Rect frame) { float shadowWidth = 0.0f; float shadowFeather = 0.0f; @@ -384,19 +385,19 @@ void View::drawShadow(NVGcontext* vg, FrameContext* ctx, Style style, float x, f NVGpaint shadowPaint = nvgBoxGradient( vg, - x, y + shadowWidth, - width, height, + frame.getMinX(), frame.getMinY() + shadowWidth, + frame.getWidth(), frame.getHeight(), this->cornerRadius * 2, shadowFeather, RGBA(0, 0, 0, shadowOpacity * alpha), TRANSPARENT); nvgBeginPath(vg); nvgRect( vg, - x - shadowOffset, - y - shadowOffset, - width + shadowOffset * 2, - height + shadowOffset * 3); - nvgRoundedRect(vg, x, y, width, height, this->cornerRadius); + frame.getMinX() - shadowOffset, + frame.getMinY() - shadowOffset, + frame.getWidth() + shadowOffset * 2, + frame.getHeight() + shadowOffset * 3); + nvgRoundedRect(vg, frame.getMinX(), frame.getMinY(), frame.getWidth(), frame.getHeight(), this->cornerRadius); nvgPathWinding(vg, NVG_HOLE); nvgFillPaint(vg, shadowPaint); nvgFill(vg); @@ -1025,24 +1026,23 @@ void View::invalidate() YGNodeCalculateLayout(this->ygNode, YGUndefined, YGUndefined, YGDirectionLTR); } +Rect View::getFrame() +{ + return Rect(getX(), getY(), getWidth(), getHeight()); +} + float View::getX() { - if (this->detached) - return this->detachedOriginX + this->translationX; - else if (this->hasParent()) - return this->getParent()->getX() + YGNodeLayoutGetLeft(this->ygNode) + this->translationX; - else - return YGNodeLayoutGetLeft(this->ygNode) + this->translationX; + if (this->hasParent()) + return this->getParent()->getX() + YGNodeLayoutGetLeft(this->ygNode) + this->translation.x; + return YGNodeLayoutGetLeft(this->ygNode) + this->translation.x; } float View::getY() { - if (this->detached) - return this->detachedOriginY + this->translationY; - else if (this->hasParent()) - return this->getParent()->getY() + YGNodeLayoutGetTop(this->ygNode) + this->translationY; - else - return YGNodeLayoutGetTop(this->ygNode) + this->translationY; + if (this->hasParent()) + return this->getParent()->getY() + YGNodeLayoutGetTop(this->ygNode) + this->translation.y; + return YGNodeLayoutGetTop(this->ygNode) + this->translation.y; } float View::getHeight(bool includeCollapse) @@ -1062,8 +1062,8 @@ void View::detach() void View::setDetachedPosition(float x, float y) { - this->detachedOriginX = x; - this->detachedOriginY = y; + this->detachedOrigin.x = x; + this->detachedOrigin.y = y; } bool View::isDetached() @@ -1917,12 +1917,12 @@ void View::registerCommonAttributes() void View::setTranslationY(float translationY) { - this->translationY = translationY; + this->translation.y = translationY; } void View::setTranslationX(float translationX) { - this->translationX = translationX; + this->translation.x = translationX; } void View::setVisibility(Visibility visibility) @@ -2045,20 +2045,16 @@ View* View::getDefaultFocus() return nullptr; } -bool View::pointInside(double x, double y) -{ - return getX() <= x && getWidth() + getX() >= x && getY() <= y && getHeight() + getY() >= y; -} - -View* View::hitTest(double x, double y) +View* View::hitTest(Point point) { // Check if can focus ourself first if (!this->isFocusable()) return nullptr; - Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); + Rect frame = getFrame(); + Logger::debug(describe() + ": --- " + frame.describe()); - if (pointInside(x, y)) + if (frame.pointInside(point)) { Logger::debug(describe() + ": OK"); return this; diff --git a/library/lib/platforms/glfw/glfw_input.cpp b/library/lib/platforms/glfw/glfw_input.cpp index 5f6fe8e6..19b35075 100644 --- a/library/lib/platforms/glfw/glfw_input.cpp +++ b/library/lib/platforms/glfw/glfw_input.cpp @@ -112,41 +112,20 @@ void GLFWInputManager::updateControllerState(ControllerState* state) } } -void GLFWInputManager::updateTouchState(TouchState* state) +void GLFWInputManager::updateTouchState(RawTouch* state) { - // Get gamepad state + // Get touchscreen state double x, y; glfwGetCursorPos(this->window, &x, &y); - state->x = x / Application::windowScale; - state->y = y / Application::windowScale; - - static TouchState oldTouch; - + state->pressed = false; int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); if (clickState == GLFW_PRESS) { - if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) - { - state->state = TouchEvent::STAY; - } - else - { - state->state = TouchEvent::START; - } - } - else - { - if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) - { - state->state = TouchEvent::NONE; - } - else - { - state->state = TouchEvent::END; - } + state->pressed = true; + state->position.x = x / Application::windowScale; + state->position.y = y / Application::windowScale; } - oldTouch = *state; } }; diff --git a/library/lib/platforms/switch/switch_input.cpp b/library/lib/platforms/switch/switch_input.cpp index 531a0976..566c23ed 100644 --- a/library/lib/platforms/switch/switch_input.cpp +++ b/library/lib/platforms/switch/switch_input.cpp @@ -66,49 +66,21 @@ void SwitchInputManager::updateControllerState(ControllerState* state) } } -void SwitchInputManager::updateTouchState(TouchState* state) +void SwitchInputManager::updateTouchState(RawTouch* state) { - // Get gamepad state - static float x = oldTouch.x; - static float y = oldTouch.y; - + // Get touchscreen state static HidTouchScreenState hidState = { 0 }; + state->pressed = false; if (hidGetTouchScreenStates(&hidState, 1)) { if (hidState.count > 0) { - x = hidState.touches[0].x / Application::windowScale; - y = hidState.touches[0].y / Application::windowScale; - } - } - - state->x = x; - state->y = y; - - if (hidState.count > 0) - { - if (oldTouch.state == TouchEvent::START || oldTouch.state == TouchEvent::STAY) - { - state->state = TouchEvent::STAY; - } - else - { - state->state = TouchEvent::START; - } - } - else - { - if (oldTouch.state == TouchEvent::END || oldTouch.state == TouchEvent::NONE) - { - state->state = TouchEvent::NONE; - } - else - { - state->state = TouchEvent::END; + state->pressed = true; + state->position.x = hidState.touches[0].x / Application::windowScale; + state->position.y = hidState.touches[0].y / Application::windowScale; } } - oldTouch = *state; } } // namespace brls diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 4bd9ae49..e1d703eb 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -41,7 +41,7 @@ ScrollingFrame::ScrollingFrame() if (state.state == GestureState::START) startY = this->scrollY * contentHeight; - float newScroll = (startY - (state.y - state.startY)) / contentHeight; + float newScroll = (startY - (state.position.y - state.startPosition.y)) / contentHeight; float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; // Bottom boundary @@ -57,21 +57,21 @@ ScrollingFrame::ScrollingFrame() startScrolling(true, newScroll); else { - float time = state.acceleration.timeY * 1000.0f; - float newPos = this->scrollY * contentHeight + state.acceleration.distanceY; + float time = state.acceleration.time.y * 1000.0f; + float newPos = this->scrollY * contentHeight + state.acceleration.distance.y; // Bottom boundary float bottomLimit = contentHeight - this->getScrollingAreaHeight(); if (newPos > bottomLimit) { - time = time * (1 - fabs(newPos - bottomLimit) / fabs(state.acceleration.distanceY)); + time = time * (1 - fabs(newPos - bottomLimit) / fabs(state.acceleration.distance.y)); newPos = bottomLimit; } // Top boundary if (newPos < 0) { - time = time * (1 - fabs(newPos) / fabs(state.acceleration.distanceY)); + time = time * (1 - fabs(newPos) / fabs(state.acceleration.distance.y)); newPos = 0; } diff --git a/library/meson.build b/library/meson.build index d0ca569e..3bf1f9f1 100644 --- a/library/meson.build +++ b/library/meson.build @@ -3,6 +3,7 @@ dep_glm = dependency('glm', version : '>=0.9.8') borealis_files = files( 'lib/core/logger.cpp', + 'lib/core/input.cpp', 'lib/core/application.cpp', 'lib/core/i18n.cpp', 'lib/core/theme.cpp', From 951effbb7c6ff2bfdcc0e1a2a7b529dfb328eeb1 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sat, 13 Mar 2021 23:08:48 +0300 Subject: [PATCH 28/59] Format script run --- library/include/borealis/core/application.hpp | 2 -- library/include/borealis/core/input.hpp | 2 +- .../include/borealis/core/metrics/point.hpp | 32 +++++++++-------- .../include/borealis/core/metrics/rect.hpp | 35 +++++++++---------- .../include/borealis/core/metrics/size.hpp | 34 +++++++++--------- .../borealis/core/touch/pan_gesture.hpp | 6 ++-- .../borealis/core/touch/tap_gesture.hpp | 2 +- library/include/borealis/core/view.hpp | 4 +-- library/lib/core/application.cpp | 4 +-- library/lib/core/input.cpp | 2 +- library/lib/core/touch/pan_gesture.cpp | 15 ++++---- library/lib/core/touch/tap_gesture.cpp | 7 ++-- library/lib/platforms/glfw/glfw_input.cpp | 2 +- library/lib/platforms/switch/switch_audio.cpp | 3 +- library/lib/platforms/switch/switch_input.cpp | 4 +-- library/lib/views/button.cpp | 7 ++-- 16 files changed, 80 insertions(+), 81 deletions(-) diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index 22395dec..b207b123 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -29,10 +29,8 @@ #include #include #include - #include #include - #include #include diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index 79e77b8a..ad66cc85 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -85,7 +85,7 @@ enum class TouchPhase }; // Contains raw touch data -struct RawTouch +struct RawTouch { bool pressed; Point position; diff --git a/library/include/borealis/core/metrics/point.hpp b/library/include/borealis/core/metrics/point.hpp index 4014da63..104d9a55 100644 --- a/library/include/borealis/core/metrics/point.hpp +++ b/library/include/borealis/core/metrics/point.hpp @@ -24,42 +24,44 @@ struct Point { float x; // The x-coordinate of the point. float y; // The y-coordinate of the point. - + // Creates a point with location (0,0). - Point(): Point(0.0f, 0.0f) - { } - + Point() + : Point(0.0f, 0.0f) + { + } + // Creates a point with coordinates specified as float values. Point(float x, float y) { this->x = x; this->y = y; } - + Point operator+(const Point& a) const { - return Point(a.x+x, a.y+y); + return Point(a.x + x, a.y + y); } - + Point operator-(const Point& a) const { - return Point(a.x-x, a.y-y); + return Point(a.x - x, a.y - y); } - + Point operator/(const float& a) const { - return Point(x/a, y/a); + return Point(x / a, y / a); } - + Point operator*(const float& a) const { - return Point(x*a, y*a); + return Point(x * a, y * a); } - - bool operator==(const Point& other) const { + + bool operator==(const Point& other) const + { return x == other.x && y == other.y; } }; - } // namespace brls diff --git a/library/include/borealis/core/metrics/rect.hpp b/library/include/borealis/core/metrics/rect.hpp index 5d40f229..c0a9d414 100644 --- a/library/include/borealis/core/metrics/rect.hpp +++ b/library/include/borealis/core/metrics/rect.hpp @@ -27,57 +27,54 @@ struct Rect { Point origin; // A point that specifies the coordinates of the rectangle’s origin. Size size; // A size that specifies the height and width of the rectangle. - + // Creates a rectangle with origin (0,0) and size (0,0). Rect() { this->origin = Point(); - this->size = Size(); + this->size = Size(); } - + // Creates a rectangle with the specified origin and size. Rect(Point origin, Size size) { this->origin = origin; - this->size = size; + this->size = size; } - + // Creates a rectangle with coordinates and dimensions specified as float values. Rect(float x, float y, float width, float height) { this->origin = Point(x, y); - this->size = Size(width, height); + this->size = Size(width, height); } - + float getWidth() const { return this->size.width; } // Returns the width of a rectangle. float getHeight() const { return this->size.height; } // Returns the height of a rectangle. - + float getMinX() const { return this->origin.x; } // Returns the smallest value for the x-coordinate of the rectangle. float getMinY() const { return this->origin.y; } // Returns the smallest value for the y-coordinate of the rectangle. - + float getMidX() const { return this->origin.x + getWidth() / 2; } // Returns the x-coordinate that establishes the center of a rectangle. float getMidY() const { return this->origin.y + getHeight() / 2; } // Returns the y-coordinate that establishes the center of the rectangle. - + float getMaxX() const { return this->origin.x + getWidth(); } // Returns the largest value of the x-coordinate for the rectangle. float getMaxY() const { return this->origin.y + getHeight(); } // Returns the largest value for the y-coordinate of the rectangle. - - bool operator==(const Rect& other) const { + + bool operator==(const Rect& other) const + { return origin == other.origin && size == other.size; } - - + // Returns true if point is inside this Rect bool pointInside(Point point) { return getMinX() <= point.x && getMaxX() >= point.x && getMinY() <= point.y && getMaxY() >= point.y; } - + std::string describe() { - return "X: " + std::to_string((int)getMinX()) + - ", Y: " + std::to_string((int)getMinY()) + - ", W: " + std::to_string((int)getWidth()) + - ", H: " + std::to_string((int)getHeight()); + return "X: " + std::to_string((int)getMinX()) + ", Y: " + std::to_string((int)getMinY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight()); } }; diff --git a/library/include/borealis/core/metrics/size.hpp b/library/include/borealis/core/metrics/size.hpp index 91443f89..76f552b8 100644 --- a/library/include/borealis/core/metrics/size.hpp +++ b/library/include/borealis/core/metrics/size.hpp @@ -24,42 +24,44 @@ struct Size { float width; // A width value. float height; // A height value. - + // Creates a size with zero width and height. - Size(): Size(0.0f, 0.0f) - { } - + Size() + : Size(0.0f, 0.0f) + { + } + // Creates a size with dimensions specified as float values. Size(float width, float height) { - this->width = width; + this->width = width; this->height = height; } - + Size operator+(const Size& a) const { - return Size(a.width+width, a.height+height); + return Size(a.width + width, a.height + height); } - + Size operator-(const Size& a) const { - return Size(a.width-width, a.height-height); + return Size(a.width - width, a.height - height); } - + Size operator/(const float& a) const { - return Size(width/a, height/a); + return Size(width / a, height / a); } - + Size operator*(const float& a) const { - return Size(width*a, height*a); + return Size(width * a, height * a); } - - bool operator==(const Size& other) const { + + bool operator==(const Size& other) const + { return width == other.width && height == other.height; } }; - } // namespace brls diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index 6a9d5bb0..acad3be5 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -38,7 +38,7 @@ struct PanGestureStatus Point position; // Current position Point startPosition; // Start X position Point delta; // Difference between current and previous positions by X - + // Acceleration info, NOT NULL ONLY from // gesture callback and when current state is END PanAcceleration acceleration; @@ -68,14 +68,14 @@ class PanGestureRecognizer : public GestureRecognizer // Get pan gesture axis PanAxis getAxis() const { return this->axis; } - + // Get current state of recognizer PanGestureStatus getCurrentStatus(); private: PanGestureRespond respond; Point position; - Point startPosition; + Point startPosition; Point delta; PanAxis axis; std::vector posHistory; diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index 26309e0b..5c8e997f 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -39,7 +39,7 @@ class TapGestureRecognizer : public GestureRecognizer public: TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); GestureState recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) override; - + // Get current state of recognizer TapGestureStatus getCurrentStatus(); diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index 0de4fd17..8b53d417 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -26,9 +26,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -193,7 +193,7 @@ class View bool hideHighlightBackground = false; - bool detached = false; + bool detached = false; Point detachedOrigin; Point translation; diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 053269cf..8cdb55d4 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -163,7 +163,7 @@ bool Application::mainLoop() // Input ControllerState controllerState = {}; - RawTouch rawTouch = {}; + RawTouch rawTouch = {}; InputManager* inputManager = Application::platform->getInputManager(); inputManager->updateTouchState(&rawTouch); @@ -171,7 +171,7 @@ bool Application::mainLoop() static Touch oldTouch; Touch touchState = InputManager::computeTouchState(rawTouch, oldTouch); - oldTouch = touchState; + oldTouch = touchState; // Touch controller events switch (touchState.phase) diff --git a/library/lib/core/input.cpp b/library/lib/core/input.cpp index 12501ddb..fd5a0312 100644 --- a/library/lib/core/input.cpp +++ b/library/lib/core/input.cpp @@ -21,7 +21,7 @@ namespace brls Touch InputManager::computeTouchState(RawTouch currentTouch, Touch lastFrameState) { - Touch result = {}; + Touch result = {}; result.position = currentTouch.position; if (currentTouch.pressed) diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index e1390ee1..c9b792f0 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -53,7 +53,7 @@ GestureState PanGestureRecognizer::recognitionLoop(Touch touch, View* view, bool { case TouchPhase::START: this->posHistory.clear(); - this->state = GestureState::UNSURE; + this->state = GestureState::UNSURE; this->startPosition = touch.position; this->position = touch.position; break; @@ -94,7 +94,7 @@ GestureState PanGestureRecognizer::recognitionLoop(Touch touch, View* view, bool } // If last touch frame, calculate acceleration - + static PanAcceleration acceleration; if (this->state == GestureState::END) { @@ -116,7 +116,7 @@ GestureState PanGestureRecognizer::recognitionLoop(Touch touch, View* view, bool if (this->state == GestureState::START || this->state == GestureState::STAY || this->state == GestureState::END) { PanGestureStatus state = getCurrentStatus(); - state.acceleration = acceleration; + state.acceleration = acceleration; this->respond(state); } @@ -138,12 +138,11 @@ GestureState PanGestureRecognizer::recognitionLoop(Touch touch, View* view, bool PanGestureStatus PanGestureRecognizer::getCurrentStatus() { - return PanGestureStatus - { - .state = this->state, - .position = this->position, + return PanGestureStatus { + .state = this->state, + .position = this->position, .startPosition = this->startPosition, - .delta = this->delta, + .delta = this->delta, }; } diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index 0c366fd4..6a9b64f0 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -47,7 +47,7 @@ GestureState TapGestureRecognizer::recognitionLoop(Touch touch, View* view, bool switch (touch.phase) { case TouchPhase::START: - this->state = GestureState::UNSURE; + this->state = GestureState::UNSURE; this->position = touch.position; if (respond && !this->callbackOnEndOnly) @@ -79,9 +79,8 @@ GestureState TapGestureRecognizer::recognitionLoop(Touch touch, View* view, bool TapGestureStatus TapGestureRecognizer::getCurrentStatus() { - return TapGestureStatus - { - .state = this->state, + return TapGestureStatus { + .state = this->state, .position = this->position, }; } diff --git a/library/lib/platforms/glfw/glfw_input.cpp b/library/lib/platforms/glfw/glfw_input.cpp index 19b35075..5f1b4c57 100644 --- a/library/lib/platforms/glfw/glfw_input.cpp +++ b/library/lib/platforms/glfw/glfw_input.cpp @@ -122,7 +122,7 @@ void GLFWInputManager::updateTouchState(RawTouch* state) int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); if (clickState == GLFW_PRESS) { - state->pressed = true; + state->pressed = true; state->position.x = x / Application::windowScale; state->position.y = y / Application::windowScale; } diff --git a/library/lib/platforms/switch/switch_audio.cpp b/library/lib/platforms/switch/switch_audio.cpp index c290b6ef..c4de86e3 100644 --- a/library/lib/platforms/switch/switch_audio.cpp +++ b/library/lib/platforms/switch/switch_audio.cpp @@ -57,7 +57,8 @@ SwitchAudioPlayer::SwitchAudioPlayer() char bfsarPath[29]; u64 programId = 0; svcGetInfo(&programId, InfoType_ProgramId, CUR_PROCESS_HANDLE, 0); - if (programId != QLAUNCH_PID) { + if (programId != QLAUNCH_PID) + { // Mount qlaunch ROMFS for the BFSAR Result result = romfsMountDataStorageFromProgram(QLAUNCH_PID, QLAUNCH_MOUNT_POINT); if (!R_SUCCEEDED(result)) diff --git a/library/lib/platforms/switch/switch_input.cpp b/library/lib/platforms/switch/switch_input.cpp index 566c23ed..06327241 100644 --- a/library/lib/platforms/switch/switch_input.cpp +++ b/library/lib/platforms/switch/switch_input.cpp @@ -70,13 +70,13 @@ void SwitchInputManager::updateTouchState(RawTouch* state) { // Get touchscreen state static HidTouchScreenState hidState = { 0 }; - + state->pressed = false; if (hidGetTouchScreenStates(&hidState, 1)) { if (hidState.count > 0) { - state->pressed = true; + state->pressed = true; state->position.x = hidState.touches[0].x / Application::windowScale; state->position.y = hidState.touches[0].y / Application::windowScale; } diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index fa68215d..536e153a 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -189,15 +189,16 @@ void Button::setState(ButtonState state) this->applyStyle(); } -void Button::setText(std::string text) { +void Button::setText(std::string text) +{ this->label->setText(text); } -std::string Button::getText() { +std::string Button::getText() +{ return label->getFullText(); } - View* Button::create() { return new Button(); From 53c0c146fdc6ac44feb0d9cd8c6dfc6baea84c43 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sat, 13 Mar 2021 23:24:25 +0300 Subject: [PATCH 29/59] END phase position fixed --- library/lib/core/input.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/lib/core/input.cpp b/library/lib/core/input.cpp index fd5a0312..3e3ff4b1 100644 --- a/library/lib/core/input.cpp +++ b/library/lib/core/input.cpp @@ -22,10 +22,10 @@ namespace brls Touch InputManager::computeTouchState(RawTouch currentTouch, Touch lastFrameState) { Touch result = {}; - result.position = currentTouch.position; if (currentTouch.pressed) { + result.position = currentTouch.position; if (lastFrameState.phase == TouchPhase::START || lastFrameState.phase == TouchPhase::STAY) { result.phase = TouchPhase::STAY; @@ -37,6 +37,7 @@ Touch InputManager::computeTouchState(RawTouch currentTouch, Touch lastFrameStat } else { + result.position = lastFrameState.position; if (lastFrameState.phase == TouchPhase::END || lastFrameState.phase == TouchPhase::NONE) { result.phase = TouchPhase::NONE; From 55c3665e1f4e3e1743d6072424077d381374fc4a Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sat, 13 Mar 2021 23:38:44 +0300 Subject: [PATCH 30/59] Disabled animation on scrollFrame scrolling --- library/lib/views/scrolling_frame.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index e1d703eb..9ac4a06b 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -54,7 +54,7 @@ ScrollingFrame::ScrollingFrame() // Start animation if (state.state != GestureState::END) - startScrolling(true, newScroll); + startScrolling(false, newScroll); else { float time = state.acceleration.time.y * 1000.0f; @@ -197,6 +197,7 @@ void ScrollingFrame::startScrolling(bool animated, float newScroll) { this->scrollY.stop(); this->scrollY = newScroll; + this->scrollAnimationTick(); this->invalidate(); } } From 8722f51ce2ef96108630d8183d5b0b971ac80ce3 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Mon, 15 Mar 2021 14:10:43 +0300 Subject: [PATCH 31/59] Added bounce effect to scroll on edges --- library/lib/core/touch/pan_gesture.cpp | 2 +- library/lib/views/scrolling_frame.cpp | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index c9b792f0..f424f97a 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -18,7 +18,7 @@ #define FPS 60.0f // TODO: get real FPS #define MAX_DELTA_MOVEMENT 10 -#define HISTORY_LIMIT 3 +#define HISTORY_LIMIT 2 #define PAN_SCROLL_ACCELERATION -5000 namespace brls diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 9ac4a06b..722463b9 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -46,22 +46,36 @@ ScrollingFrame::ScrollingFrame() // Bottom boundary if (newScroll > bottomLimit) - newScroll = bottomLimit; + newScroll = bottomLimit + (newScroll - bottomLimit) / 5.0f; // Top boundary if (newScroll < 0.0f) - newScroll = 0.0f; + newScroll = newScroll / 5.0f; // Start animation if (state.state != GestureState::END) startScrolling(false, newScroll); else { + if (this->scrollY < 0) + { + startScrolling(true, 0); + return; + } + + float bottomLimit = contentHeight - this->getScrollingAreaHeight(); + float bottomLimitNormal = bottomLimit / contentHeight; + + if (this->scrollY > bottomLimitNormal) + { + startScrolling(true, bottomLimitNormal); + return; + } + float time = state.acceleration.time.y * 1000.0f; float newPos = this->scrollY * contentHeight + state.acceleration.distance.y; // Bottom boundary - float bottomLimit = contentHeight - this->getScrollingAreaHeight(); if (newPos > bottomLimit) { time = time * (1 - fabs(newPos - bottomLimit) / fabs(state.acceleration.distance.y)); From ea632232f49937d9dca1b427d39ca71940b09340 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Mon, 15 Mar 2021 14:12:50 +0300 Subject: [PATCH 32/59] Moved metrics to core/geometry --- library/include/borealis.hpp | 1 + library/include/borealis/core/geometry.hpp | 107 +++++++++++++ library/include/borealis/core/input.hpp | 2 +- .../include/borealis/core/metrics/point.hpp | 67 --------- .../include/borealis/core/metrics/rect.hpp | 81 ---------- .../include/borealis/core/metrics/size.hpp | 67 --------- library/include/borealis/core/view.hpp | 2 +- library/lib/core/geometry.cpp | 142 ++++++++++++++++++ library/lib/core/input.cpp | 2 +- library/meson.build | 1 + 10 files changed, 254 insertions(+), 218 deletions(-) create mode 100644 library/include/borealis/core/geometry.hpp delete mode 100644 library/include/borealis/core/metrics/point.hpp delete mode 100644 library/include/borealis/core/metrics/rect.hpp delete mode 100644 library/include/borealis/core/metrics/size.hpp create mode 100644 library/lib/core/geometry.cpp diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index 7bc53b55..9d8cda11 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/library/include/borealis/core/geometry.hpp b/library/include/borealis/core/geometry.hpp new file mode 100644 index 00000000..170eaba3 --- /dev/null +++ b/library/include/borealis/core/geometry.hpp @@ -0,0 +1,107 @@ +/* + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include + +namespace brls +{ + +// A structure that contains a point in a two-dimensional coordinate system. +struct Point +{ + float x; // The x-coordinate of the point. + float y; // The y-coordinate of the point. + + // Creates a point with location (0,0). + Point(); + + // Creates a point with coordinates specified as float values. + Point(float x, float y); + + Point operator+(const Point& a) const; + Point operator-(const Point& a) const; + Point operator/(const float& a) const; + Point operator*(const float& a) const; + bool operator==(const Point& other) const; +}; + +// A structure that contains width and height values. +struct Size +{ + float width; // A width value. + float height; // A height value. + + // Creates a size with zero width and height. + Size(); + + // Creates a size with dimensions specified as float values. + Size(float width, float height); + + Size operator+(const Size& a) const; + Size operator-(const Size& a) const; + Size operator/(const float& a) const; + Size operator*(const float& a) const; + bool operator==(const Size& other) const; +}; + +// Rect +// A structure that contains the location and dimensions of a rectangle. +struct Rect +{ + Point origin; // A point that specifies the coordinates of the rectangle’s origin. + Size size; // A size that specifies the height and width of the rectangle. + + // Creates a rectangle with origin (0,0) and size (0,0). + Rect(); + + // Creates a rectangle with the specified origin and size. + Rect(Point origin, Size size); + + // Creates a rectangle with coordinates and dimensions specified as float values. + Rect(float x, float y, float width, float height); + + // Returns the width of a rectangle. + float getWidth() const; + // Returns the height of a rectangle. + float getHeight() const; + + // Returns the smallest value for the x-coordinate of the rectangle. + float getMinX() const; + // Returns the smallest value for the y-coordinate of the rectangle. + float getMinY() const; + + // Returns the x-coordinate that establishes the center of a rectangle. + float getMidX() const; + // Returns the y-coordinate that establishes the center of the rectangle. + float getMidY() const; + + // Returns the largest value of the x-coordinate for the rectangle. + float getMaxX() const; + // Returns the largest value for the y-coordinate of the rectangle. + float getMaxY() const; + + bool operator==(const Rect& other) const; + + // Returns true if point is inside this Rect + bool pointInside(Point point); + + // Returns string with description of current Rect + std::string describe(); +}; + +} // namespace brls \ No newline at end of file diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index ad66cc85..d50c9a99 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -17,7 +17,7 @@ #pragma once -#include +#include namespace brls { diff --git a/library/include/borealis/core/metrics/point.hpp b/library/include/borealis/core/metrics/point.hpp deleted file mode 100644 index 104d9a55..00000000 --- a/library/include/borealis/core/metrics/point.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright 2021 XITRIX - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#pragma once - -namespace brls -{ - -// A structure that contains a point in a two-dimensional coordinate system. -struct Point -{ - float x; // The x-coordinate of the point. - float y; // The y-coordinate of the point. - - // Creates a point with location (0,0). - Point() - : Point(0.0f, 0.0f) - { - } - - // Creates a point with coordinates specified as float values. - Point(float x, float y) - { - this->x = x; - this->y = y; - } - - Point operator+(const Point& a) const - { - return Point(a.x + x, a.y + y); - } - - Point operator-(const Point& a) const - { - return Point(a.x - x, a.y - y); - } - - Point operator/(const float& a) const - { - return Point(x / a, y / a); - } - - Point operator*(const float& a) const - { - return Point(x * a, y * a); - } - - bool operator==(const Point& other) const - { - return x == other.x && y == other.y; - } -}; - -} // namespace brls diff --git a/library/include/borealis/core/metrics/rect.hpp b/library/include/borealis/core/metrics/rect.hpp deleted file mode 100644 index c0a9d414..00000000 --- a/library/include/borealis/core/metrics/rect.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright 2021 XITRIX - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#pragma once - -#include -#include - -namespace brls -{ - -// A structure that contains the location and dimensions of a rectangle. -struct Rect -{ - Point origin; // A point that specifies the coordinates of the rectangle’s origin. - Size size; // A size that specifies the height and width of the rectangle. - - // Creates a rectangle with origin (0,0) and size (0,0). - Rect() - { - this->origin = Point(); - this->size = Size(); - } - - // Creates a rectangle with the specified origin and size. - Rect(Point origin, Size size) - { - this->origin = origin; - this->size = size; - } - - // Creates a rectangle with coordinates and dimensions specified as float values. - Rect(float x, float y, float width, float height) - { - this->origin = Point(x, y); - this->size = Size(width, height); - } - - float getWidth() const { return this->size.width; } // Returns the width of a rectangle. - float getHeight() const { return this->size.height; } // Returns the height of a rectangle. - - float getMinX() const { return this->origin.x; } // Returns the smallest value for the x-coordinate of the rectangle. - float getMinY() const { return this->origin.y; } // Returns the smallest value for the y-coordinate of the rectangle. - - float getMidX() const { return this->origin.x + getWidth() / 2; } // Returns the x-coordinate that establishes the center of a rectangle. - float getMidY() const { return this->origin.y + getHeight() / 2; } // Returns the y-coordinate that establishes the center of the rectangle. - - float getMaxX() const { return this->origin.x + getWidth(); } // Returns the largest value of the x-coordinate for the rectangle. - float getMaxY() const { return this->origin.y + getHeight(); } // Returns the largest value for the y-coordinate of the rectangle. - - bool operator==(const Rect& other) const - { - return origin == other.origin && size == other.size; - } - - // Returns true if point is inside this Rect - bool pointInside(Point point) - { - return getMinX() <= point.x && getMaxX() >= point.x && getMinY() <= point.y && getMaxY() >= point.y; - } - - std::string describe() - { - return "X: " + std::to_string((int)getMinX()) + ", Y: " + std::to_string((int)getMinY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight()); - } -}; - -} // namespace brls diff --git a/library/include/borealis/core/metrics/size.hpp b/library/include/borealis/core/metrics/size.hpp deleted file mode 100644 index 76f552b8..00000000 --- a/library/include/borealis/core/metrics/size.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copheightright 2021 widthITRIwidth - - Licensed under the Apache License, Version 2.0 (the "License"); - heightou maheight not use this file ewidthcept in compliance with the License. - heightou maheight obtain a copheight of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required bheight applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANheight KIND, either ewidthpress or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#pragma once - -namespace brls -{ - -// A structure that contains width and height values. -struct Size -{ - float width; // A width value. - float height; // A height value. - - // Creates a size with zero width and height. - Size() - : Size(0.0f, 0.0f) - { - } - - // Creates a size with dimensions specified as float values. - Size(float width, float height) - { - this->width = width; - this->height = height; - } - - Size operator+(const Size& a) const - { - return Size(a.width + width, a.height + height); - } - - Size operator-(const Size& a) const - { - return Size(a.width - width, a.height - height); - } - - Size operator/(const float& a) const - { - return Size(width / a, height / a); - } - - Size operator*(const float& a) const - { - return Size(width * a, height * a); - } - - bool operator==(const Size& other) const - { - return width == other.width && height == other.height; - } -}; - -} // namespace brls diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index 8b53d417..aad06dd5 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -27,8 +27,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/library/lib/core/geometry.cpp b/library/lib/core/geometry.cpp new file mode 100644 index 00000000..74cc26ff --- /dev/null +++ b/library/lib/core/geometry.cpp @@ -0,0 +1,142 @@ +/* + Copyright 2021 natinusala + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include + +namespace brls +{ + +// Point +Point::Point() + : Point(0.0f, 0.0f) +{ +} + +Point::Point(float x, float y) +{ + this->x = x; + this->y = y; +} + +Point Point::operator+(const Point& a) const +{ + return Point(a.x + x, a.y + y); +} + +Point Point::operator-(const Point& a) const +{ + return Point(a.x - x, a.y - y); +} + +Point Point::operator/(const float& a) const +{ + return Point(x / a, y / a); +} + +Point Point::operator*(const float& a) const +{ + return Point(x * a, y * a); +} + +bool Point::operator==(const Point& other) const +{ + return x == other.x && y == other.y; +} + +// Size +Size::Size() + : Size(0.0f, 0.0f) +{ +} + +Size::Size(float width, float height) +{ + this->width = width; + this->height = height; +} + +Size Size::operator+(const Size& a) const +{ + return Size(a.width + width, a.height + height); +} + +Size Size::operator-(const Size& a) const +{ + return Size(a.width - width, a.height - height); +} + +Size Size::operator/(const float& a) const +{ + return Size(width / a, height / a); +} + +Size Size::operator*(const float& a) const +{ + return Size(width * a, height * a); +} + +bool Size::operator==(const Size& other) const +{ + return width == other.width && height == other.height; +} + +// Rect +Rect::Rect() +{ + this->origin = Point(); + this->size = Size(); +} + +Rect::Rect(Point origin, Size size) +{ + this->origin = origin; + this->size = size; +} + +Rect::Rect(float x, float y, float width, float height) +{ + this->origin = Point(x, y); + this->size = Size(width, height); +} + +float Rect::getWidth() const { return this->size.width; } +float Rect::getHeight() const { return this->size.height; } + +float Rect::getMinX() const { return this->origin.x; } +float Rect::getMinY() const { return this->origin.y; } + +float Rect::getMidX() const { return this->origin.x + getWidth() / 2; } +float Rect::getMidY() const { return this->origin.y + getHeight() / 2; } + +float Rect::getMaxX() const { return this->origin.x + getWidth(); } +float Rect::getMaxY() const { return this->origin.y + getHeight(); } + +bool Rect::operator==(const Rect& other) const +{ + return origin == other.origin && size == other.size; +} + +bool Rect::pointInside(Point point) +{ + return getMinX() <= point.x && getMaxX() >= point.x && getMinY() <= point.y && getMaxY() >= point.y; +} + +std::string Rect::describe() +{ + return "X: " + std::to_string((int)getMinX()) + ", Y: " + std::to_string((int)getMinY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight()); +} + +} // namespace brls diff --git a/library/lib/core/input.cpp b/library/lib/core/input.cpp index 3e3ff4b1..019e048d 100644 --- a/library/lib/core/input.cpp +++ b/library/lib/core/input.cpp @@ -21,7 +21,7 @@ namespace brls Touch InputManager::computeTouchState(RawTouch currentTouch, Touch lastFrameState) { - Touch result = {}; + Touch result = {}; if (currentTouch.pressed) { diff --git a/library/meson.build b/library/meson.build index 3bf1f9f1..d1c1b6d5 100644 --- a/library/meson.build +++ b/library/meson.build @@ -10,6 +10,7 @@ borealis_files = files( 'lib/core/style.cpp', 'lib/core/activity.cpp', 'lib/core/platform.cpp', + 'lib/core/geometry.cpp', 'lib/core/font.cpp', 'lib/core/util.cpp', 'lib/core/time.cpp', From 0e2ea97a57f80828e53eaa8469701140ce2eef56 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Wed, 17 Mar 2021 19:37:48 +0300 Subject: [PATCH 33/59] Added comments --- library/include/borealis/core/application.hpp | 1 + library/lib/core/application.cpp | 2 ++ library/lib/core/input.cpp | 15 ++++++--------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/library/include/borealis/core/application.hpp b/library/include/borealis/core/application.hpp index b207b123..c177ca82 100644 --- a/library/include/borealis/core/application.hpp +++ b/library/include/borealis/core/application.hpp @@ -213,6 +213,7 @@ class Application inline static View* currentFocus; + // Return true if input type was changed static bool setInputType(InputType type); inline static InputType inputType = InputType::GAMEPAD; diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 8cdb55d4..4a7bde44 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -258,6 +258,7 @@ void Application::quit() void Application::navigate(FocusDirection direction) { + // Dismiss navigation if input type was changed if (Application::setInputType(InputType::GAMEPAD)) return; @@ -379,6 +380,7 @@ View* Application::getCurrentFocus() bool Application::handleAction(char button) { + // Dismiss if input type was changed if (button == BUTTON_A && setInputType(InputType::GAMEPAD)) return false; diff --git a/library/lib/core/input.cpp b/library/lib/core/input.cpp index 019e048d..281bdbb8 100644 --- a/library/lib/core/input.cpp +++ b/library/lib/core/input.cpp @@ -21,34 +21,31 @@ namespace brls Touch InputManager::computeTouchState(RawTouch currentTouch, Touch lastFrameState) { - Touch result = {}; - if (currentTouch.pressed) { - result.position = currentTouch.position; + lastFrameState.position = currentTouch.position; if (lastFrameState.phase == TouchPhase::START || lastFrameState.phase == TouchPhase::STAY) { - result.phase = TouchPhase::STAY; + lastFrameState.phase = TouchPhase::STAY; } else { - result.phase = TouchPhase::START; + lastFrameState.phase = TouchPhase::START; } } else { - result.position = lastFrameState.position; if (lastFrameState.phase == TouchPhase::END || lastFrameState.phase == TouchPhase::NONE) { - result.phase = TouchPhase::NONE; + lastFrameState.phase = TouchPhase::NONE; } else { - result.phase = TouchPhase::END; + lastFrameState.phase = TouchPhase::END; } } - return result; + return lastFrameState; } } // namespace brls \ No newline at end of file From 29e6b39d84fbc631c70da45de2167dd4fb63d321 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Thu, 18 Mar 2021 19:12:44 +0300 Subject: [PATCH 34/59] WIP: recycler_frame --- demo/recycling_list_tab.cpp | 37 +++++ demo/recycling_list_tab.hpp | 20 +++ library/include/borealis.hpp | 1 + library/include/borealis/views/recycler.hpp | 74 +++++++++ library/lib/core/application.cpp | 2 + library/lib/core/box.cpp | 5 +- library/lib/core/view.cpp | 4 +- library/lib/views/recycler.cpp | 159 ++++++++++++++++++++ library/lib/views/scrolling_frame.cpp | 2 - library/meson.build | 1 + resources/xml/cells/cell.xml | 27 ++++ resources/xml/tabs/recycling_list.xml | 8 +- 12 files changed, 331 insertions(+), 9 deletions(-) create mode 100644 library/include/borealis/views/recycler.hpp create mode 100644 library/lib/views/recycler.cpp create mode 100644 resources/xml/cells/cell.xml diff --git a/demo/recycling_list_tab.cpp b/demo/recycling_list_tab.cpp index 4c4a82bf..fcad81c9 100644 --- a/demo/recycling_list_tab.cpp +++ b/demo/recycling_list_tab.cpp @@ -16,10 +16,47 @@ #include "recycling_list_tab.hpp" +RecyclerCell::RecyclerCell() +{ + this->inflateFromXMLRes("xml/cells/cell.xml"); +} + +RecyclerCell* RecyclerCell::create() +{ + return new RecyclerCell(); +} + +// DATA SOURCE + +int DataSource::numberOfRows() +{ + return 20; +} + +brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, int row) +{ + RecyclerCell* item = (RecyclerCell*) recycler->dequeueReusableCell("Cell"); + item->label->setText("Item #" + std::to_string(row)); + if (row == 7) + item->label->setText("Item # JM FKNKLmf knlwkenf jkN Fklmew kfnmL Fl enfklMEK MlKWNMF lkem KmLK NefnlkMElkf mlkwemf lkMLKFEm KLMlkf mLKMF lkMSElk fmLKFM lkesb fhhsk gmNLKmkS fe"); + return item; +} + +// RECYCLER VIEW + RecyclingListTab::RecyclingListTab() { // Inflate the tab from the XML file this->inflateFromXMLRes("xml/tabs/recycling_list.xml"); + + recycler->registerCell("Cell", [](){ return RecyclerCell::create(); }); + recycler->setDataSource(new DataSource()); + + recycler->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureStatus status) + { + this->recycler->reloadData(); + return false; + }, true)); } brls::View* RecyclingListTab::create() diff --git a/demo/recycling_list_tab.hpp b/demo/recycling_list_tab.hpp index c3b09da5..65213009 100644 --- a/demo/recycling_list_tab.hpp +++ b/demo/recycling_list_tab.hpp @@ -18,10 +18,30 @@ #include +class RecyclerCell : public brls::RecyclerCell +{ + public: + RecyclerCell(); + + BRLS_BIND(brls::Rectangle, accent, "brls/sidebar/item_accent"); + BRLS_BIND(brls::Label, label, "brls/sidebar/item_label"); + + static RecyclerCell* create(); +}; + +class DataSource: public brls::RecyclerDataSource +{ +public: + int numberOfRows() override; + brls::RecyclerCell* cellForRow(brls::RecyclerFrame* recycler, int row) override; +}; + class RecyclingListTab : public brls::Box { public: RecyclingListTab(); static brls::View* create(); +private: + BRLS_BIND(brls::RecyclerFrame, recycler, "recycler"); }; diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index 9d8cda11..59f94ea9 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp new file mode 100644 index 00000000..0e53012a --- /dev/null +++ b/library/include/borealis/views/recycler.hpp @@ -0,0 +1,74 @@ +/* + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace brls +{ + +class RecyclerCell : public Box +{ + public: + std::string identifier; + static RecyclerCell* create(); +}; + +class RecyclerFrame; +class RecyclerDataSource +{ +public: + virtual int numberOfRows() { return 0; } + virtual RecyclerCell* cellForRow(RecyclerFrame* recycler, int row) { return nullptr; } +}; + +class RecyclerFrame : public ScrollingFrame +{ + public: + RecyclerFrame(); + + void onLayout() override; + void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; + void setDataSource(RecyclerDataSource* source); + + void reloadData(); + void registerCell(std::string identifier, std::function allocation); + RecyclerCell* dequeueReusableCell(std::string identifier); + + static View* create(); +private: + Box* contentBox; + RecyclerDataSource* dataSource; + std::vector cacheFramesData; + std::vector visibleCells; + std::map*> queueMap; + std::map> registerMap; + + bool checkWidth(); + + void cacheCellFrames(); + void queueReusableCell(RecyclerCell* cell); +}; + +} // namespace brls diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 4a7bde44..a7bbf1ba 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -809,6 +810,7 @@ void Application::registerBuiltInXMLViews() Application::registerXMLView("brls:Sidebar", Sidebar::create); Application::registerXMLView("brls:Header", Header::create); Application::registerXMLView("brls:ScrollingFrame", ScrollingFrame::create); + Application::registerXMLView("brls:RecyclerFrame", RecyclerFrame::create); Application::registerXMLView("brls:Image", Image::create); Application::registerXMLView("brls:Padding", Padding::create); Application::registerXMLView("brls:Button", Button::create); diff --git a/library/lib/core/box.cpp b/library/lib/core/box.cpp index 1e5b2c77..157b138a 100644 --- a/library/lib/core/box.cpp +++ b/library/lib/core/box.cpp @@ -209,11 +209,12 @@ void Box::removeView(View* view) return; // Remove it - YGNodeRemoveChild(this->ygNode, view->getYGNode()); + if (!view->isDetached()) + YGNodeRemoveChild(this->ygNode, view->getYGNode()); this->children.erase(this->children.begin() + index); view->willDisappear(true); - delete view; +// delete view; this->invalidate(); } diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index faaf0827..1ca328ac 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -1034,14 +1034,14 @@ Rect View::getFrame() float View::getX() { if (this->hasParent()) - return this->getParent()->getX() + YGNodeLayoutGetLeft(this->ygNode) + this->translation.x; + return this->getParent()->getX() + YGNodeLayoutGetLeft(this->ygNode) + this->translation.x + (isDetached() ? this->detachedOrigin.x : 0); return YGNodeLayoutGetLeft(this->ygNode) + this->translation.x; } float View::getY() { if (this->hasParent()) - return this->getParent()->getY() + YGNodeLayoutGetTop(this->ygNode) + this->translation.y; + return this->getParent()->getY() + YGNodeLayoutGetTop(this->ygNode) + this->translation.y + (isDetached() ? this->detachedOrigin.y : 0); return YGNodeLayoutGetTop(this->ygNode) + this->translation.y; } diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp new file mode 100644 index 00000000..d9980ef9 --- /dev/null +++ b/library/lib/views/recycler.cpp @@ -0,0 +1,159 @@ +/* + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include + +namespace brls +{ + +RecyclerCell* RecyclerCell::create() +{ + return new RecyclerCell(); +} + +RecyclerFrame::RecyclerFrame() +{ + Style style = Application::getStyle(); + + this->setScrollingBehavior(ScrollingBehavior::CENTERED); + + // Create content box + this->contentBox = new Box(Axis::COLUMN); + + this->contentBox->setPadding( + style["brls/sidebar/padding_top"], + style["brls/sidebar/padding_right"], + style["brls/sidebar/padding_bottom"], + style["brls/sidebar/padding_left"]); + + this->setContentView(this->contentBox); +} + +void RecyclerFrame::onLayout() +{ + ScrollingFrame::onLayout(); + if (checkWidth()) + reloadData(); +} + +void RecyclerFrame::setDataSource(RecyclerDataSource* source) +{ + this->dataSource = source; + if (checkWidth()) + reloadData(); +} + +void RecyclerFrame::reloadData() +{ + auto children = this->contentBox->getChildren(); + for(auto const& child: children) + { + queueReusableCell((RecyclerCell*) child); + this->contentBox->removeView(child); + } + + if (dataSource) + { + cacheCellFrames(); + for (int i = 0; i < dataSource->numberOfRows(); i++) + { + RecyclerCell* cell = dataSource->cellForRow(this, i); + cell->setMaxWidth(this->getWidth()); + auto frame = cacheFramesData.at(i); + cell->setDetachedPosition(frame.getMinX(), frame.getMinY()); + this->contentBox->addView(cell, i); + } + } +} + +void RecyclerFrame::registerCell(std::string identifier, std::function allocation) +{ + queueMap.insert(std::make_pair(identifier, new std::vector())); + registerMap.insert(std::make_pair(identifier, allocation)); +} + +RecyclerCell* RecyclerFrame::dequeueReusableCell(std::string identifier) +{ + RecyclerCell* cell = nullptr; + auto it = queueMap.find(identifier); + + if (it != queueMap.end()) { + std::vector* vector = it->second; + if (!vector->empty()) { + cell = vector->back(); + vector->pop_back(); + } else { + cell = registerMap.at(identifier)(); + cell->identifier = identifier; + cell->detach(); + } + } + + return cell; +} + +void RecyclerFrame::queueReusableCell(RecyclerCell* cell) +{ + queueMap.at(cell->identifier)->push_back(cell); +} + +void RecyclerFrame::cacheCellFrames() +{ + cacheFramesData.clear(); + Point currentOrigin; + if (dataSource) + { + for (int i = 0; i < dataSource->numberOfRows(); i++) + { + RecyclerCell* cell = dataSource->cellForRow(this, i); + float a = this->getWidth(); + cell->setMaxWidth(a); + Rect frame = cell->getFrame(); + frame.origin = currentOrigin; + cacheFramesData.push_back(frame); + currentOrigin.y += frame.getHeight(); + queueReusableCell(cell); + } + contentBox->setHeight(currentOrigin.y); + } +} + +bool RecyclerFrame::checkWidth() +{ + float width = getWidth(); + static float oldWidth = width; + if (oldWidth != width && width != 0) + { + oldWidth = width; + return true; + } + oldWidth = width; + return false; +} + + +void RecyclerFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) +{ + ScrollingFrame::draw(vg, x, y, width, height, style, ctx); +} + +View* RecyclerFrame::create() +{ + return new RecyclerFrame(); +} + +} // namespace brls diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 722463b9..0ed50849 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -146,7 +146,6 @@ void ScrollingFrame::setContentView(View* view) view->detach(); view->setCulled(false); view->setMaxWidth(this->getWidth()); - view->setDetachedPosition(this->getX(), this->getY()); Box::addView(view); // will invalidate the scrolling box, hence calling onLayout and invalidating the contentView } @@ -156,7 +155,6 @@ void ScrollingFrame::onLayout() if (this->contentView) { this->contentView->setMaxWidth(this->getWidth()); - this->contentView->setDetachedPosition(this->getX(), this->getY()); this->contentView->invalidate(); } } diff --git a/library/meson.build b/library/meson.build index d1c1b6d5..e4f73702 100644 --- a/library/meson.build +++ b/library/meson.build @@ -36,6 +36,7 @@ borealis_files = files( 'lib/views/applet_frame.cpp', 'lib/views/tab_frame.cpp', 'lib/views/rectangle.cpp', + 'lib/views/recycler.cpp', 'lib/views/sidebar.cpp', 'lib/views/label.cpp', 'lib/views/button.cpp', diff --git a/resources/xml/cells/cell.xml b/resources/xml/cells/cell.xml new file mode 100644 index 00000000..91a6677e --- /dev/null +++ b/resources/xml/cells/cell.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/resources/xml/tabs/recycling_list.xml b/resources/xml/tabs/recycling_list.xml index 8506f5a9..c07e4be5 100644 --- a/resources/xml/tabs/recycling_list.xml +++ b/resources/xml/tabs/recycling_list.xml @@ -2,10 +2,12 @@ width="auto" height="auto"> - + grow="1.0" > + + From a885e3b4369ab39f049bdd4f012134a015ce625a Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Thu, 18 Mar 2021 19:22:52 +0300 Subject: [PATCH 35/59] Format fix --- demo/recycling_list_tab.cpp | 14 ++++----- demo/recycling_list_tab.hpp | 15 ++++++---- library/include/borealis.hpp | 2 +- library/include/borealis/views/recycler.hpp | 20 ++++++------- library/lib/core/application.cpp | 2 +- library/lib/core/box.cpp | 2 +- library/lib/views/recycler.cpp | 33 +++++++++++---------- 7 files changed, 47 insertions(+), 41 deletions(-) diff --git a/demo/recycling_list_tab.cpp b/demo/recycling_list_tab.cpp index fcad81c9..34cb2f88 100644 --- a/demo/recycling_list_tab.cpp +++ b/demo/recycling_list_tab.cpp @@ -35,7 +35,7 @@ int DataSource::numberOfRows() brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, int row) { - RecyclerCell* item = (RecyclerCell*) recycler->dequeueReusableCell("Cell"); + RecyclerCell* item = (RecyclerCell*)recycler->dequeueReusableCell("Cell"); item->label->setText("Item #" + std::to_string(row)); if (row == 7) item->label->setText("Item # JM FKNKLmf knlwkenf jkN Fklmew kfnmL Fl enfklMEK MlKWNMF lkem KmLK NefnlkMElkf mlkwemf lkMLKFEm KLMlkf mLKMF lkMSElk fmLKFM lkesb fhhsk gmNLKmkS fe"); @@ -48,15 +48,15 @@ RecyclingListTab::RecyclingListTab() { // Inflate the tab from the XML file this->inflateFromXMLRes("xml/tabs/recycling_list.xml"); - - recycler->registerCell("Cell", [](){ return RecyclerCell::create(); }); + + recycler->registerCell("Cell", []() { return RecyclerCell::create(); }); recycler->setDataSource(new DataSource()); - - recycler->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureStatus status) - { + + recycler->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureStatus status) { this->recycler->reloadData(); return false; - }, true)); + }, + true)); } brls::View* RecyclingListTab::create() diff --git a/demo/recycling_list_tab.hpp b/demo/recycling_list_tab.hpp index 65213009..6f399cb5 100644 --- a/demo/recycling_list_tab.hpp +++ b/demo/recycling_list_tab.hpp @@ -18,20 +18,22 @@ #include -class RecyclerCell : public brls::RecyclerCell +class RecyclerCell + : public brls::RecyclerCell { public: RecyclerCell(); - + BRLS_BIND(brls::Rectangle, accent, "brls/sidebar/item_accent"); BRLS_BIND(brls::Label, label, "brls/sidebar/item_label"); - + static RecyclerCell* create(); }; -class DataSource: public brls::RecyclerDataSource +class DataSource + : public brls::RecyclerDataSource { -public: + public: int numberOfRows() override; brls::RecyclerCell* cellForRow(brls::RecyclerFrame* recycler, int row) override; }; @@ -42,6 +44,7 @@ class RecyclingListTab : public brls::Box RecyclingListTab(); static brls::View* create(); -private: + + private: BRLS_BIND(brls::RecyclerFrame, recycler, "recycler"); }; diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index 59f94ea9..04680dfe 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -50,8 +50,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index 0e53012a..0d25714c 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -16,14 +16,13 @@ #pragma once -#include -#include -#include - #include #include #include #include +#include +#include +#include namespace brls { @@ -38,7 +37,7 @@ class RecyclerCell : public Box class RecyclerFrame; class RecyclerDataSource { -public: + public: virtual int numberOfRows() { return 0; } virtual RecyclerCell* cellForRow(RecyclerFrame* recycler, int row) { return nullptr; } }; @@ -47,26 +46,27 @@ class RecyclerFrame : public ScrollingFrame { public: RecyclerFrame(); - + void onLayout() override; void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; void setDataSource(RecyclerDataSource* source); - + void reloadData(); void registerCell(std::string identifier, std::function allocation); RecyclerCell* dequeueReusableCell(std::string identifier); static View* create(); -private: + + private: Box* contentBox; RecyclerDataSource* dataSource; std::vector cacheFramesData; std::vector visibleCells; std::map*> queueMap; std::map> registerMap; - + bool checkWidth(); - + void cacheCellFrames(); void queueReusableCell(RecyclerCell* cell); }; diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index a7bbf1ba..0cb405ed 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -31,9 +31,9 @@ #include #include #include +#include #include #include -#include #include #include diff --git a/library/lib/core/box.cpp b/library/lib/core/box.cpp index 157b138a..987b7bef 100644 --- a/library/lib/core/box.cpp +++ b/library/lib/core/box.cpp @@ -214,7 +214,7 @@ void Box::removeView(View* view) this->children.erase(this->children.begin() + index); view->willDisappear(true); -// delete view; + // delete view; this->invalidate(); } diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index d9980ef9..9d2327eb 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -60,12 +60,12 @@ void RecyclerFrame::setDataSource(RecyclerDataSource* source) void RecyclerFrame::reloadData() { auto children = this->contentBox->getChildren(); - for(auto const& child: children) + for (auto const& child : children) { - queueReusableCell((RecyclerCell*) child); + queueReusableCell((RecyclerCell*)child); this->contentBox->removeView(child); } - + if (dataSource) { cacheCellFrames(); @@ -80,7 +80,7 @@ void RecyclerFrame::reloadData() } } -void RecyclerFrame::registerCell(std::string identifier, std::function allocation) +void RecyclerFrame::registerCell(std::string identifier, std::function allocation) { queueMap.insert(std::make_pair(identifier, new std::vector())); registerMap.insert(std::make_pair(identifier, allocation)); @@ -89,20 +89,24 @@ void RecyclerFrame::registerCell(std::string identifier, std::function* vector = it->second; - if (!vector->empty()) { + if (!vector->empty()) + { cell = vector->back(); vector->pop_back(); - } else { - cell = registerMap.at(identifier)(); + } + else + { + cell = registerMap.at(identifier)(); cell->identifier = identifier; cell->detach(); } } - + return cell; } @@ -120,9 +124,9 @@ void RecyclerFrame::cacheCellFrames() for (int i = 0; i < dataSource->numberOfRows(); i++) { RecyclerCell* cell = dataSource->cellForRow(this, i); - float a = this->getWidth(); + float a = this->getWidth(); cell->setMaxWidth(a); - Rect frame = cell->getFrame(); + Rect frame = cell->getFrame(); frame.origin = currentOrigin; cacheFramesData.push_back(frame); currentOrigin.y += frame.getHeight(); @@ -134,7 +138,7 @@ void RecyclerFrame::cacheCellFrames() bool RecyclerFrame::checkWidth() { - float width = getWidth(); + float width = getWidth(); static float oldWidth = width; if (oldWidth != width && width != 0) { @@ -145,7 +149,6 @@ bool RecyclerFrame::checkWidth() return false; } - void RecyclerFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) { ScrollingFrame::draw(vg, x, y, width, height, style, ctx); From db3c4b7aee5ce619c6ac0328d9549fc205a80bcd Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Thu, 18 Mar 2021 22:44:34 +0300 Subject: [PATCH 36/59] Complete RecyclerFrame - needs refactoring --- library/include/borealis/core/geometry.hpp | 12 ++- library/include/borealis/views/recycler.hpp | 6 +- .../borealis/views/scrolling_frame.hpp | 2 + library/lib/core/geometry.cpp | 10 +++ library/lib/views/recycler.cpp | 74 ++++++++++++++++++- library/lib/views/scrolling_frame.cpp | 7 ++ 6 files changed, 106 insertions(+), 5 deletions(-) diff --git a/library/include/borealis/core/geometry.hpp b/library/include/borealis/core/geometry.hpp index 170eaba3..13b8d293 100644 --- a/library/include/borealis/core/geometry.hpp +++ b/library/include/borealis/core/geometry.hpp @@ -97,11 +97,17 @@ struct Rect bool operator==(const Rect& other) const; - // Returns true if point is inside this Rect + // Returns true if point is inside this Rect. bool pointInside(Point point); - // Returns string with description of current Rect + // Returns string with description of current Rect. std::string describe(); + + // Returns true if two rects have shared area. + bool collideWith(const Rect& other) const; + + // Returns Rect with offset by presented Point. + Rect offsetBy(const Point& origin) const; }; -} // namespace brls \ No newline at end of file +} // namespace brls diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index 0d25714c..3da46721 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -30,6 +30,7 @@ namespace brls class RecyclerCell : public Box { public: + int indexPath; std::string identifier; static RecyclerCell* create(); }; @@ -61,14 +62,17 @@ class RecyclerFrame : public ScrollingFrame Box* contentBox; RecyclerDataSource* dataSource; std::vector cacheFramesData; - std::vector visibleCells; + std::map visibleCells; std::map*> queueMap; std::map> registerMap; bool checkWidth(); + void cellRecycling(); void cacheCellFrames(); void queueReusableCell(RecyclerCell* cell); + + uint visibleMin, visibleMax; }; } // namespace brls diff --git a/library/include/borealis/views/scrolling_frame.hpp b/library/include/borealis/views/scrolling_frame.hpp index 7a7d3831..ae9504dc 100644 --- a/library/include/borealis/views/scrolling_frame.hpp +++ b/library/include/borealis/views/scrolling_frame.hpp @@ -54,6 +54,8 @@ class ScrollingFrame : public Box void setPaddingBottom(float bottom) override; void setPaddingLeft(float left) override; + Rect getVisibleFrame(); + /** * Sets the content view of this scrolling box. There can only be one * content view per scrolling box at a time. diff --git a/library/lib/core/geometry.cpp b/library/lib/core/geometry.cpp index 74cc26ff..6cda449c 100644 --- a/library/lib/core/geometry.cpp +++ b/library/lib/core/geometry.cpp @@ -139,4 +139,14 @@ std::string Rect::describe() return "X: " + std::to_string((int)getMinX()) + ", Y: " + std::to_string((int)getMinY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight()); } +bool Rect::collideWith(const Rect& other) const +{ + return !((getMinX() > other.getMaxX() || getMaxX() < other.getMinX()) || (getMinY() > other.getMaxY() || getMaxY() < other.getMinY())); +} + +Rect Rect::offsetBy(const Point& origin) const +{ + return Rect(this->origin + origin, this->size); +} + } // namespace brls diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 9d2327eb..d94e97bf 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -66,16 +66,33 @@ void RecyclerFrame::reloadData() this->contentBox->removeView(child); } + visibleCells.clear(); + visibleMin = UINT_MAX; + visibleMax = 0; + if (dataSource) { cacheCellFrames(); + Rect frame = getFrame(); for (int i = 0; i < dataSource->numberOfRows(); i++) { + if (!cacheFramesData[i].offsetBy(frame.origin).collideWith(frame)) + continue; + RecyclerCell* cell = dataSource->cellForRow(this, i); cell->setMaxWidth(this->getWidth()); auto frame = cacheFramesData.at(i); cell->setDetachedPosition(frame.getMinX(), frame.getMinY()); - this->contentBox->addView(cell, i); + cell->indexPath = i; + this->contentBox->addView(cell); + + visibleCells.insert(std::make_pair(i, cell)); + + if (i < visibleMin) + visibleMin = i; + + if (i > visibleMax) + visibleMax = i; } } } @@ -149,8 +166,63 @@ bool RecyclerFrame::checkWidth() return false; } +void RecyclerFrame::cellRecycling() +{ + Rect frame = getFrame(); + Rect visibleFrame = getVisibleFrame(); + + while (visibleCells.find(visibleMin) != visibleCells.end() && !visibleCells[visibleMin]->getFrame().collideWith(visibleFrame)) + { + RecyclerCell* cell = (RecyclerCell*)visibleCells[visibleMin]; + queueReusableCell(cell); + this->contentBox->removeView(cell); + visibleCells.erase(cell->indexPath); + visibleMin++; + } + + while (visibleCells.find(visibleMax) != visibleCells.end() && !visibleCells[visibleMax]->getFrame().collideWith(visibleFrame)) + { + RecyclerCell* cell = (RecyclerCell*)visibleCells[visibleMax]; + queueReusableCell(cell); + this->contentBox->removeView(cell); + visibleCells.erase(cell->indexPath); + visibleMax--; + } + + while (visibleMin - 1 < cacheFramesData.size() && cacheFramesData[visibleMin - 1].offsetBy(frame.origin).collideWith(visibleFrame)) + { + int index = visibleMin - 1; + + RecyclerCell* cell = dataSource->cellForRow(this, index); + cell->setMaxWidth(this->getWidth()); + auto frame = cacheFramesData.at(index); + cell->setDetachedPosition(frame.getMinX(), frame.getMinY()); + cell->indexPath = index; + this->contentBox->addView(cell); + + visibleCells.insert(std::make_pair(index, cell)); + visibleMin = index; + } + + while (visibleMax + 1 < cacheFramesData.size() && cacheFramesData[visibleMax + 1].offsetBy(frame.origin).collideWith(visibleFrame)) + { + int index = visibleMax + 1; + + RecyclerCell* cell = dataSource->cellForRow(this, index); + cell->setMaxWidth(this->getWidth()); + auto frame = cacheFramesData.at(index); + cell->setDetachedPosition(frame.getMinX(), frame.getMinY()); + cell->indexPath = index; + this->contentBox->addView(cell); + + visibleCells.insert(std::make_pair(index, cell)); + visibleMax = index; + } +} + void RecyclerFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) { + cellRecycling(); ScrollingFrame::draw(vg, x, y, width, height, style, ctx); } diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 0ed50849..c18d8262 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -294,6 +294,13 @@ bool ScrollingFrame::updateScrolling(bool animated) return true; } +Rect ScrollingFrame::getVisibleFrame() +{ + Rect frame = getFrame(); + frame.origin.y += this->scrollY * this->getContentHeight(); + return frame; +} + #define NO_PADDING fatal("Padding is not supported by brls:ScrollingFrame, please set padding on the content view instead"); void ScrollingFrame::setPadding(float top, float right, float bottom, float left) From 10cfcec77662b65867f864dc0b34225918c5f294 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Fri, 19 Mar 2021 21:07:09 +0300 Subject: [PATCH 37/59] Simplify scrolling limits --- library/lib/views/scrolling_frame.cpp | 56 ++++++++------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 722463b9..c772a1b1 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -41,54 +41,16 @@ ScrollingFrame::ScrollingFrame() if (state.state == GestureState::START) startY = this->scrollY * contentHeight; - float newScroll = (startY - (state.position.y - state.startPosition.y)) / contentHeight; - float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; - - // Bottom boundary - if (newScroll > bottomLimit) - newScroll = bottomLimit + (newScroll - bottomLimit) / 5.0f; - - // Top boundary - if (newScroll < 0.0f) - newScroll = newScroll / 5.0f; + float newScroll = (startY - (state.position.y - state.startPosition.y)) / contentHeight; // Start animation if (state.state != GestureState::END) startScrolling(false, newScroll); else { - if (this->scrollY < 0) - { - startScrolling(true, 0); - return; - } - - float bottomLimit = contentHeight - this->getScrollingAreaHeight(); - float bottomLimitNormal = bottomLimit / contentHeight; - - if (this->scrollY > bottomLimitNormal) - { - startScrolling(true, bottomLimitNormal); - return; - } - float time = state.acceleration.time.y * 1000.0f; float newPos = this->scrollY * contentHeight + state.acceleration.distance.y; - // Bottom boundary - if (newPos > bottomLimit) - { - time = time * (1 - fabs(newPos - bottomLimit) / fabs(state.acceleration.distance.y)); - newPos = bottomLimit; - } - - // Top boundary - if (newPos < 0) - { - time = time * (1 - fabs(newPos) / fabs(state.acceleration.distance.y)); - newPos = 0; - } - newScroll = newPos / contentHeight; if (newScroll == this->scrollY || time < 100) @@ -249,7 +211,21 @@ float ScrollingFrame::getContentHeight() void ScrollingFrame::scrollAnimationTick() { if (this->contentView) - this->contentView->setTranslationY(-(this->scrollY * this->getContentHeight())); + { + float contentHeight = this->getContentHeight(); + float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; + + if (this->scrollY < 0) + this->scrollY = 0; + + if (this->scrollY > bottomLimit) + this->scrollY = bottomLimit; + + if (contentHeight <= getHeight()) + this->scrollY = 0; + + this->contentView->setTranslationY(-(this->scrollY * contentHeight)); + } } void ScrollingFrame::onChildFocusGained(View* directChild, View* focusedView) From 0b6d6509e1d101d8ed9fdf37cc206077d01f819b Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sat, 20 Mar 2021 04:17:16 +0300 Subject: [PATCH 38/59] Improved algorithm --- demo/recycling_list_tab.cpp | 2 +- library/include/borealis/core/view.hpp | 8 + library/include/borealis/views/recycler.hpp | 8 +- .../borealis/views/scrolling_frame.hpp | 7 + library/lib/views/recycler.cpp | 139 ++++++++++-------- library/lib/views/scrolling_frame.cpp | 6 + 6 files changed, 109 insertions(+), 61 deletions(-) diff --git a/demo/recycling_list_tab.cpp b/demo/recycling_list_tab.cpp index 34cb2f88..8bccf009 100644 --- a/demo/recycling_list_tab.cpp +++ b/demo/recycling_list_tab.cpp @@ -30,7 +30,7 @@ RecyclerCell* RecyclerCell::create() int DataSource::numberOfRows() { - return 20; + return 30; } brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, int row) diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index aad06dd5..da50e820 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -971,6 +971,14 @@ class View * Sets the position of the view, if detached. */ void setDetachedPosition(float x, float y); + + /** + * Gets detached position of the view. + */ + Point getDetachedPosition() const + { + return detachedOrigin; + } void setParent(Box* parent, void* parentUserdata = nullptr); Box* getParent(); diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index 3da46721..a9f8a9ec 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -41,6 +41,7 @@ class RecyclerDataSource public: virtual int numberOfRows() { return 0; } virtual RecyclerCell* cellForRow(RecyclerFrame* recycler, int row) { return nullptr; } + virtual float cellHeightForRow(int row) { return -1; } }; class RecyclerFrame : public ScrollingFrame @@ -55,13 +56,16 @@ class RecyclerFrame : public ScrollingFrame void reloadData(); void registerCell(std::string identifier, std::function allocation); RecyclerCell* dequeueReusableCell(std::string identifier); + + float estimatedCellHeight = 44; static View* create(); private: Box* contentBox; + Rect renderedFrame; RecyclerDataSource* dataSource; - std::vector cacheFramesData; + std::vector cacheFramesData; std::map visibleCells; std::map*> queueMap; std::map> registerMap; @@ -71,6 +75,8 @@ class RecyclerFrame : public ScrollingFrame void cellRecycling(); void cacheCellFrames(); void queueReusableCell(RecyclerCell* cell); + + void addCellAt(int index, int downSide = true); uint visibleMin, visibleMax; }; diff --git a/library/include/borealis/views/scrolling_frame.hpp b/library/include/borealis/views/scrolling_frame.hpp index ae9504dc..c38695b1 100644 --- a/library/include/borealis/views/scrolling_frame.hpp +++ b/library/include/borealis/views/scrolling_frame.hpp @@ -67,6 +67,13 @@ class ScrollingFrame : public Box * Default is NATURAL. */ void setScrollingBehavior(ScrollingBehavior behavior); + + float getScroll() const + { + return scrollY; + } + + void setScroll(float value); static View* create(); diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index d94e97bf..2ccbaea6 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -69,6 +69,11 @@ void RecyclerFrame::reloadData() visibleCells.clear(); visibleMin = UINT_MAX; visibleMax = 0; + + renderedFrame = Rect(); + renderedFrame.size.width = getWidth(); + + setScroll(0); if (dataSource) { @@ -76,23 +81,9 @@ void RecyclerFrame::reloadData() Rect frame = getFrame(); for (int i = 0; i < dataSource->numberOfRows(); i++) { - if (!cacheFramesData[i].offsetBy(frame.origin).collideWith(frame)) - continue; - - RecyclerCell* cell = dataSource->cellForRow(this, i); - cell->setMaxWidth(this->getWidth()); - auto frame = cacheFramesData.at(i); - cell->setDetachedPosition(frame.getMinX(), frame.getMinY()); - cell->indexPath = i; - this->contentBox->addView(cell); - - visibleCells.insert(std::make_pair(i, cell)); - - if (i < visibleMin) - visibleMin = i; - - if (i > visibleMax) - visibleMax = i; + addCellAt(i); + if (renderedFrame.getMaxY() > frame.getMaxY()) + break; } } } @@ -135,19 +126,19 @@ void RecyclerFrame::queueReusableCell(RecyclerCell* cell) void RecyclerFrame::cacheCellFrames() { cacheFramesData.clear(); + Rect frame = getFrame(); Point currentOrigin; + if (dataSource) { for (int i = 0; i < dataSource->numberOfRows(); i++) { - RecyclerCell* cell = dataSource->cellForRow(this, i); - float a = this->getWidth(); - cell->setMaxWidth(a); - Rect frame = cell->getFrame(); - frame.origin = currentOrigin; - cacheFramesData.push_back(frame); - currentOrigin.y += frame.getHeight(); - queueReusableCell(cell); + float height = dataSource->cellHeightForRow(i); + if (height == -1) + height = estimatedCellHeight; + + cacheFramesData.push_back(Size(frame.getWidth(), height)); + currentOrigin.y += height; } contentBox->setHeight(currentOrigin.y); } @@ -170,53 +161,83 @@ void RecyclerFrame::cellRecycling() { Rect frame = getFrame(); Rect visibleFrame = getVisibleFrame(); + visibleFrame.origin.y -= frame.origin.y; - while (visibleCells.find(visibleMin) != visibleCells.end() && !visibleCells[visibleMin]->getFrame().collideWith(visibleFrame)) + while (true) { - RecyclerCell* cell = (RecyclerCell*)visibleCells[visibleMin]; - queueReusableCell(cell); - this->contentBox->removeView(cell); - visibleCells.erase(cell->indexPath); + auto minCell = visibleCells.find(visibleMin); + if (minCell == visibleCells.end() || minCell->second->getDetachedPosition().y + minCell->second->getHeight() >= visibleFrame.getMinY()) + break; + + queueReusableCell(minCell->second); + this->contentBox->removeView(minCell->second); + visibleCells.erase(minCell->second->indexPath); + + float cellHeight = minCell->second->getHeight(); + renderedFrame.origin.y += cellHeight; + renderedFrame.size.height -= cellHeight; + visibleMin++; } - - while (visibleCells.find(visibleMax) != visibleCells.end() && !visibleCells[visibleMax]->getFrame().collideWith(visibleFrame)) + + while (true) { - RecyclerCell* cell = (RecyclerCell*)visibleCells[visibleMax]; - queueReusableCell(cell); - this->contentBox->removeView(cell); - visibleCells.erase(cell->indexPath); + auto maxCell = visibleCells.find(visibleMax); + if (maxCell == visibleCells.end() || maxCell->second->getDetachedPosition().y <= visibleFrame.getMaxY()) + break; + + queueReusableCell(maxCell->second); + this->contentBox->removeView(maxCell->second); + visibleCells.erase(maxCell->second->indexPath); + + float cellHeight = maxCell->second->getHeight(); + renderedFrame.size.height -= cellHeight; + visibleMax--; } - - while (visibleMin - 1 < cacheFramesData.size() && cacheFramesData[visibleMin - 1].offsetBy(frame.origin).collideWith(visibleFrame)) + + while (visibleMin - 1 < cacheFramesData.size() && renderedFrame.getMinY() > visibleFrame.getMinY()) { - int index = visibleMin - 1; - - RecyclerCell* cell = dataSource->cellForRow(this, index); - cell->setMaxWidth(this->getWidth()); - auto frame = cacheFramesData.at(index); - cell->setDetachedPosition(frame.getMinX(), frame.getMinY()); - cell->indexPath = index; - this->contentBox->addView(cell); - - visibleCells.insert(std::make_pair(index, cell)); - visibleMin = index; + int i = visibleMin - 1; + addCellAt(i, false); } - - while (visibleMax + 1 < cacheFramesData.size() && cacheFramesData[visibleMax + 1].offsetBy(frame.origin).collideWith(visibleFrame)) + + while (visibleMax + 1 < cacheFramesData.size() && renderedFrame.getMaxY() < visibleFrame.getMaxY()) { - int index = visibleMax + 1; + int i = visibleMax + 1; + addCellAt(i, true); + } +} + +void RecyclerFrame::addCellAt(int index, int downSide) +{ + RecyclerCell* cell = dataSource->cellForRow(this, index); + cell->setMaxWidth(renderedFrame.getWidth()); + Point cellOrigin = Point(renderedFrame.getMinX(), downSide ? renderedFrame.getMaxY() : renderedFrame.getMinY() - cell->getHeight()); + cell->setDetachedPosition(cellOrigin.x, cellOrigin.y); + cell->indexPath = index; + this->contentBox->addView(cell); + + visibleCells.insert(std::make_pair(index, cell)); - RecyclerCell* cell = dataSource->cellForRow(this, index); - cell->setMaxWidth(this->getWidth()); - auto frame = cacheFramesData.at(index); - cell->setDetachedPosition(frame.getMinX(), frame.getMinY()); - cell->indexPath = index; - this->contentBox->addView(cell); + if (index < visibleMin) + visibleMin = index; - visibleCells.insert(std::make_pair(index, cell)); + if (index > visibleMax) visibleMax = index; + + Rect cellFrame = cell->getFrame(); + + if (!downSide) + renderedFrame.origin.y -= cellFrame.getHeight(); + + renderedFrame.size.height += cellFrame.getHeight(); + + if (cellFrame.getHeight() != cacheFramesData[index].height) + { + float delta = cellFrame.getHeight() - cacheFramesData[index].height; + contentBox->setHeight(contentBox->getHeight() + delta); + cacheFramesData[index].height = cellFrame.getHeight(); } } diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 2d87a1c3..60fd94dd 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -206,6 +206,12 @@ float ScrollingFrame::getContentHeight() return this->contentView->getHeight(); } +void ScrollingFrame::setScroll(float value) +{ + scrollY = value; + scrollAnimationTick(); +} + void ScrollingFrame::scrollAnimationTick() { if (this->contentView) From 7b88f65cebc34d100eb2e15f417309417a3d453d Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sat, 20 Mar 2021 04:24:17 +0300 Subject: [PATCH 39/59] Switch fix --- demo/recycling_list_tab.cpp | 2 +- library/include/borealis/views/recycler.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/recycling_list_tab.cpp b/demo/recycling_list_tab.cpp index 8bccf009..3fcf0391 100644 --- a/demo/recycling_list_tab.cpp +++ b/demo/recycling_list_tab.cpp @@ -30,7 +30,7 @@ RecyclerCell* RecyclerCell::create() int DataSource::numberOfRows() { - return 30; + return 1000; } brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, int row) diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index a9f8a9ec..e9f67133 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -78,7 +78,7 @@ class RecyclerFrame : public ScrollingFrame void addCellAt(int index, int downSide = true); - uint visibleMin, visibleMax; + uint32_t visibleMin, visibleMax; }; } // namespace brls From 8e47b5370366f9e029e162c171201d8ebd9974d2 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sat, 20 Mar 2021 14:06:21 +0300 Subject: [PATCH 40/59] Added padding to recycler frame --- library/include/borealis/views/recycler.hpp | 21 ++++- library/lib/views/recycler.cpp | 96 ++++++++++++++++----- resources/xml/tabs/recycling_list.xml | 6 +- 3 files changed, 97 insertions(+), 26 deletions(-) diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index e9f67133..9c47d268 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -48,23 +48,33 @@ class RecyclerFrame : public ScrollingFrame { public: RecyclerFrame(); - - void onLayout() override; + void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; + void onLayout() override; + void setPadding(float padding) override; + void setPadding(float top, float right, float bottom, float left) override; + void setPaddingTop(float top) override; + void setPaddingRight(float right) override; + void setPaddingBottom(float bottom) override; + void setPaddingLeft(float left) override; + void setDataSource(RecyclerDataSource* source); void reloadData(); void registerCell(std::string identifier, std::function allocation); RecyclerCell* dequeueReusableCell(std::string identifier); + // Used for initial recycler's frame calculation float estimatedCellHeight = 44; static View* create(); private: + RecyclerDataSource* dataSource = nullptr; + + Box* contentBox; Rect renderedFrame; - RecyclerDataSource* dataSource; std::vector cacheFramesData; std::map visibleCells; std::map*> queueMap; @@ -79,6 +89,11 @@ class RecyclerFrame : public ScrollingFrame void addCellAt(int index, int downSide = true); uint32_t visibleMin, visibleMax; + + float paddingTop = 0; + float paddingRight = 0; + float paddingBottom = 0; + float paddingLeft = 0; }; } // namespace brls diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 2ccbaea6..5e2c56ed 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -27,19 +27,32 @@ RecyclerCell* RecyclerCell::create() RecyclerFrame::RecyclerFrame() { - Style style = Application::getStyle(); - + // Padding + this->registerFloatXMLAttribute("paddingTop", [this](float value) { + this->setPaddingTop(value); + }); + + this->registerFloatXMLAttribute("paddingRight", [this](float value) { + this->setPaddingRight(value); + }); + + this->registerFloatXMLAttribute("paddingBottom", [this](float value) { + this->setPaddingBottom(value); + }); + + this->registerFloatXMLAttribute("paddingLeft", [this](float value) { + this->setPaddingLeft(value); + }); + + this->registerFloatXMLAttribute("padding", [this](float value) { + this->setPadding(value); + }); + this->setScrollingBehavior(ScrollingBehavior::CENTERED); // Create content box this->contentBox = new Box(Axis::COLUMN); - - this->contentBox->setPadding( - style["brls/sidebar/padding_top"], - style["brls/sidebar/padding_right"], - style["brls/sidebar/padding_bottom"], - style["brls/sidebar/padding_left"]); - + this->contentBox->setPadding(100,100,100,100); this->setContentView(this->contentBox); } @@ -140,7 +153,7 @@ void RecyclerFrame::cacheCellFrames() cacheFramesData.push_back(Size(frame.getWidth(), height)); currentOrigin.y += height; } - contentBox->setHeight(currentOrigin.y); + contentBox->setHeight(currentOrigin.y + paddingTop + paddingBottom); } } @@ -168,14 +181,14 @@ void RecyclerFrame::cellRecycling() auto minCell = visibleCells.find(visibleMin); if (minCell == visibleCells.end() || minCell->second->getDetachedPosition().y + minCell->second->getHeight() >= visibleFrame.getMinY()) break; - - queueReusableCell(minCell->second); - this->contentBox->removeView(minCell->second); - visibleCells.erase(minCell->second->indexPath); float cellHeight = minCell->second->getHeight(); renderedFrame.origin.y += cellHeight; renderedFrame.size.height -= cellHeight; + + queueReusableCell(minCell->second); + this->contentBox->removeView(minCell->second); + visibleCells.erase(minCell->second->indexPath); visibleMin++; } @@ -185,24 +198,24 @@ void RecyclerFrame::cellRecycling() auto maxCell = visibleCells.find(visibleMax); if (maxCell == visibleCells.end() || maxCell->second->getDetachedPosition().y <= visibleFrame.getMaxY()) break; - - queueReusableCell(maxCell->second); - this->contentBox->removeView(maxCell->second); - visibleCells.erase(maxCell->second->indexPath); float cellHeight = maxCell->second->getHeight(); renderedFrame.size.height -= cellHeight; + queueReusableCell(maxCell->second); + this->contentBox->removeView(maxCell->second); + visibleCells.erase(maxCell->second->indexPath); + visibleMax--; } - while (visibleMin - 1 < cacheFramesData.size() && renderedFrame.getMinY() > visibleFrame.getMinY()) + while (visibleMin - 1 < cacheFramesData.size() && renderedFrame.getMinY() > visibleFrame.getMinY() - paddingTop) { int i = visibleMin - 1; addCellAt(i, false); } - while (visibleMax + 1 < cacheFramesData.size() && renderedFrame.getMaxY() < visibleFrame.getMaxY()) + while (visibleMax + 1 < cacheFramesData.size() && renderedFrame.getMaxY() < visibleFrame.getMaxY() - paddingBottom) { int i = visibleMax + 1; addCellAt(i, true); @@ -212,8 +225,8 @@ void RecyclerFrame::cellRecycling() void RecyclerFrame::addCellAt(int index, int downSide) { RecyclerCell* cell = dataSource->cellForRow(this, index); - cell->setMaxWidth(renderedFrame.getWidth()); - Point cellOrigin = Point(renderedFrame.getMinX(), downSide ? renderedFrame.getMaxY() : renderedFrame.getMinY() - cell->getHeight()); + cell->setMaxWidth(renderedFrame.getWidth() - paddingLeft - paddingRight); + Point cellOrigin = Point(renderedFrame.getMinX() + paddingLeft, (downSide ? renderedFrame.getMaxY() : renderedFrame.getMinY() - cell->getHeight()) + paddingTop); cell->setDetachedPosition(cellOrigin.x, cellOrigin.y); cell->indexPath = index; this->contentBox->addView(cell); @@ -247,6 +260,45 @@ void RecyclerFrame::draw(NVGcontext* vg, float x, float y, float width, float he ScrollingFrame::draw(vg, x, y, width, height, style, ctx); } +void RecyclerFrame::setPadding(float padding) +{ + this->setPadding(padding, padding, padding, padding); +} + +void RecyclerFrame::setPadding(float top, float right, float bottom, float left) +{ + paddingTop = top; + paddingRight = right; + paddingBottom = bottom; + paddingLeft = left; + + this->reloadData(); +} + +void RecyclerFrame::setPaddingTop(float top) +{ + paddingTop = top; + this->reloadData(); +} + +void RecyclerFrame::setPaddingRight(float right) +{ + paddingRight = right; + this->reloadData(); +} + +void RecyclerFrame::setPaddingBottom(float bottom) +{ + paddingBottom = bottom; + this->reloadData(); +} + +void RecyclerFrame::setPaddingLeft(float left) +{ + paddingLeft = left; + this->reloadData(); +} + View* RecyclerFrame::create() { return new RecyclerFrame(); diff --git a/resources/xml/tabs/recycling_list.xml b/resources/xml/tabs/recycling_list.xml index c07e4be5..c4f0cccb 100644 --- a/resources/xml/tabs/recycling_list.xml +++ b/resources/xml/tabs/recycling_list.xml @@ -6,7 +6,11 @@ id="recycler" width="auto" height="auto" - grow="1.0" > + grow="1.0" + paddingTop="@style/brls/sidebar/padding_top" + paddingRight="@style/brls/sidebar/padding_right" + paddingBottom="@style/brls/sidebar/padding_bottom" + paddingLeft="@style/brls/sidebar/padding_left"> From fc4baf30478179a1199f9bf69dcadc640a78d0ca Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sat, 20 Mar 2021 20:38:06 +0300 Subject: [PATCH 41/59] Added navigation inside recycler frame --- library/include/borealis/core/box.hpp | 1 + library/include/borealis/core/view.hpp | 2 +- library/include/borealis/views/recycler.hpp | 89 +++++++--- .../borealis/views/scrolling_frame.hpp | 4 +- library/lib/core/box.cpp | 5 + library/lib/views/recycler.cpp | 162 ++++++++++++------ 6 files changed, 184 insertions(+), 79 deletions(-) diff --git a/library/include/borealis/core/box.hpp b/library/include/borealis/core/box.hpp index a3edfcf3..16bbbe6b 100644 --- a/library/include/borealis/core/box.hpp +++ b/library/include/borealis/core/box.hpp @@ -158,6 +158,7 @@ class Box : public View void setDirection(Direction direction); void setAxis(Axis axis); + Axis getAxis() const; std::vector& getChildren(); diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index da50e820..c8c47487 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -971,7 +971,7 @@ class View * Sets the position of the view, if detached. */ void setDetachedPosition(float x, float y); - + /** * Gets detached position of the view. */ diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index 9c47d268..204db6be 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -30,9 +30,30 @@ namespace brls class RecyclerCell : public Box { public: - int indexPath; - std::string identifier; + /* + * Cell's position inside recycler frame + */ + int getIndexPath() const { return indexPath; } + + /* + * DO NOT USE! FOR INTERNAL USAGE ONLY! + */ + void setIndexPath(int value) { indexPath = value; } + + /* + * A string used to identify a cell that is reusable. + */ + std::string reuseIdentifier; + + /* + * Prepares a reusable cell for reuse by the recycler frame's data source. + */ + virtual void prepareForReuse() { } + static RecyclerCell* create(); + + private: + int indexPath; }; class RecyclerFrame; @@ -41,14 +62,27 @@ class RecyclerDataSource public: virtual int numberOfRows() { return 0; } virtual RecyclerCell* cellForRow(RecyclerFrame* recycler, int row) { return nullptr; } + + /* + * Used to provide row height. + * Return -1 to use autoscaling. + */ virtual float cellHeightForRow(int row) { return -1; } }; +class RecyclerContentBox : public Box +{ + public: + RecyclerContentBox(); + View* getNextFocus(FocusDirection direction, View* currentView) override; +}; + +// Custom Box for propper recycling navigation class RecyclerFrame : public ScrollingFrame { public: RecyclerFrame(); - + void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; void onLayout() override; void setPadding(float padding) override; @@ -57,43 +91,58 @@ class RecyclerFrame : public ScrollingFrame void setPaddingRight(float right) override; void setPaddingBottom(float bottom) override; void setPaddingLeft(float left) override; - + + /* + * Set an object that acts as the data source of the recycler frame. + */ void setDataSource(RecyclerDataSource* source); + /* + * Reloads the rows of the recycler frame. + */ void reloadData(); + + /* + * Registers a class for use in creating new recycler cells. + */ void registerCell(std::string identifier, std::function allocation); + + /* + * Returns a reusable recycler-frame cell object for the specified reuse identifier + */ RecyclerCell* dequeueReusableCell(std::string identifier); - - // Used for initial recycler's frame calculation - float estimatedCellHeight = 44; + + /* + * Used for initial recycler's frame calculation if rows autoscaling selected. + * To provide more accurate height implement DataSource->cellHeightForRow(). + */ + float estimatedRowHeight = 44; static View* create(); private: RecyclerDataSource* dataSource = nullptr; - - + + uint32_t visibleMin, visibleMax; + + float paddingTop = 0; + float paddingRight = 0; + float paddingBottom = 0; + float paddingLeft = 0; + Box* contentBox; Rect renderedFrame; std::vector cacheFramesData; - std::map visibleCells; std::map*> queueMap; - std::map> registerMap; + std::map> allocationMap; bool checkWidth(); - void cellRecycling(); void cacheCellFrames(); + void cellsRecyclingLoop(); void queueReusableCell(RecyclerCell* cell); - - void addCellAt(int index, int downSide = true); - uint32_t visibleMin, visibleMax; - - float paddingTop = 0; - float paddingRight = 0; - float paddingBottom = 0; - float paddingLeft = 0; + void addCellAt(int index, int downSide); }; } // namespace brls diff --git a/library/include/borealis/views/scrolling_frame.hpp b/library/include/borealis/views/scrolling_frame.hpp index c38695b1..5564c4c4 100644 --- a/library/include/borealis/views/scrolling_frame.hpp +++ b/library/include/borealis/views/scrolling_frame.hpp @@ -67,12 +67,12 @@ class ScrollingFrame : public Box * Default is NATURAL. */ void setScrollingBehavior(ScrollingBehavior behavior); - + float getScroll() const { return scrollY; } - + void setScroll(float value); static View* create(); diff --git a/library/lib/core/box.cpp b/library/lib/core/box.cpp index 987b7bef..cc7ddc55 100644 --- a/library/lib/core/box.cpp +++ b/library/lib/core/box.cpp @@ -460,6 +460,11 @@ void Box::setAxis(Axis axis) this->invalidate(); } +Axis Box::getAxis() const +{ + return axis; +} + void Box::setDirection(Direction direction) { switch (direction) diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 5e2c56ed..7d66c4c4 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -25,6 +25,41 @@ RecyclerCell* RecyclerCell::create() return new RecyclerCell(); } +RecyclerContentBox::RecyclerContentBox() + : Box(Axis::COLUMN) +{ +} + +View* RecyclerContentBox::getNextFocus(FocusDirection direction, View* currentView) +{ + void* parentUserData = currentView->getParentUserData(); + + // Return nullptr immediately if focus direction mismatches the box axis (clang-format refuses to split it in multiple lines...) + if ((this->getAxis() == Axis::ROW && direction != FocusDirection::LEFT && direction != FocusDirection::RIGHT) || (this->getAxis() == Axis::COLUMN && direction != FocusDirection::UP && direction != FocusDirection::DOWN)) + { + return nullptr; + } + + // Traverse the children + size_t offset = 1; // which way we are going in the children list + + if ((this->getAxis() == Axis::ROW && direction == FocusDirection::LEFT) || (this->getAxis() == Axis::COLUMN && direction == FocusDirection::UP)) + { + offset = -1; + } + + size_t currentFocusIndex = *((size_t*)parentUserData) + offset; + View* currentFocus = nullptr; + + for (auto it : getChildren()) + { + if (*((size_t*)it->getParentUserData()) == currentFocusIndex) + currentFocus = it->getDefaultFocus(); + } + + return currentFocus; +} + RecyclerFrame::RecyclerFrame() { // Padding @@ -47,22 +82,15 @@ RecyclerFrame::RecyclerFrame() this->registerFloatXMLAttribute("padding", [this](float value) { this->setPadding(value); }); - + this->setScrollingBehavior(ScrollingBehavior::CENTERED); // Create content box - this->contentBox = new Box(Axis::COLUMN); - this->contentBox->setPadding(100,100,100,100); + this->contentBox = new RecyclerContentBox(); + this->contentBox->setPadding(100, 100, 100, 100); this->setContentView(this->contentBox); } -void RecyclerFrame::onLayout() -{ - ScrollingFrame::onLayout(); - if (checkWidth()) - reloadData(); -} - void RecyclerFrame::setDataSource(RecyclerDataSource* source) { this->dataSource = source; @@ -79,13 +107,12 @@ void RecyclerFrame::reloadData() this->contentBox->removeView(child); } - visibleCells.clear(); visibleMin = UINT_MAX; visibleMax = 0; - - renderedFrame = Rect(); + + renderedFrame = Rect(); renderedFrame.size.width = getWidth(); - + setScroll(0); if (dataSource) @@ -94,7 +121,7 @@ void RecyclerFrame::reloadData() Rect frame = getFrame(); for (int i = 0; i < dataSource->numberOfRows(); i++) { - addCellAt(i); + addCellAt(i, true); if (renderedFrame.getMaxY() > frame.getMaxY()) break; } @@ -104,7 +131,7 @@ void RecyclerFrame::reloadData() void RecyclerFrame::registerCell(std::string identifier, std::function allocation) { queueMap.insert(std::make_pair(identifier, new std::vector())); - registerMap.insert(std::make_pair(identifier, allocation)); + allocationMap.insert(std::make_pair(identifier, allocation)); } RecyclerCell* RecyclerFrame::dequeueReusableCell(std::string identifier) @@ -122,18 +149,19 @@ RecyclerCell* RecyclerFrame::dequeueReusableCell(std::string identifier) } else { - cell = registerMap.at(identifier)(); - cell->identifier = identifier; + cell = allocationMap.at(identifier)(); + cell->reuseIdentifier = identifier; cell->detach(); } } + cell->prepareForReuse(); return cell; } void RecyclerFrame::queueReusableCell(RecyclerCell* cell) { - queueMap.at(cell->identifier)->push_back(cell); + queueMap.at(cell->reuseIdentifier)->push_back(cell); } void RecyclerFrame::cacheCellFrames() @@ -141,15 +169,15 @@ void RecyclerFrame::cacheCellFrames() cacheFramesData.clear(); Rect frame = getFrame(); Point currentOrigin; - + if (dataSource) { for (int i = 0; i < dataSource->numberOfRows(); i++) { float height = dataSource->cellHeightForRow(i); if (height == -1) - height = estimatedCellHeight; - + height = estimatedRowHeight; + cacheFramesData.push_back(Size(frame.getWidth(), height)); currentOrigin.y += height; } @@ -170,7 +198,7 @@ bool RecyclerFrame::checkWidth() return false; } -void RecyclerFrame::cellRecycling() +void RecyclerFrame::cellsRecyclingLoop() { Rect frame = getFrame(); Rect visibleFrame = getVisibleFrame(); @@ -178,43 +206,49 @@ void RecyclerFrame::cellRecycling() while (true) { - auto minCell = visibleCells.find(visibleMin); - if (minCell == visibleCells.end() || minCell->second->getDetachedPosition().y + minCell->second->getHeight() >= visibleFrame.getMinY()) + RecyclerCell* minCell = nullptr; + for (auto it : getChildren()) + if (*((size_t*)it->getParentUserData()) == visibleMin) + minCell = (RecyclerCell*)it; + + if (!minCell || minCell->getDetachedPosition().y + minCell->getHeight() >= visibleFrame.getMinY()) break; - - float cellHeight = minCell->second->getHeight(); + + float cellHeight = minCell->getHeight(); renderedFrame.origin.y += cellHeight; renderedFrame.size.height -= cellHeight; - - queueReusableCell(minCell->second); - this->contentBox->removeView(minCell->second); - visibleCells.erase(minCell->second->indexPath); - + + queueReusableCell(minCell); + this->contentBox->removeView(minCell); + visibleMin++; } - + while (true) { - auto maxCell = visibleCells.find(visibleMax); - if (maxCell == visibleCells.end() || maxCell->second->getDetachedPosition().y <= visibleFrame.getMaxY()) + RecyclerCell* maxCell = nullptr; + for (auto it : getChildren()) + if (*((size_t*)it->getParentUserData()) == visibleMax) + maxCell = (RecyclerCell*)it; + + if (!maxCell || maxCell->getDetachedPosition().y <= visibleFrame.getMaxY()) break; - - float cellHeight = maxCell->second->getHeight(); + + float cellHeight = maxCell->getHeight(); renderedFrame.size.height -= cellHeight; - - queueReusableCell(maxCell->second); - this->contentBox->removeView(maxCell->second); - visibleCells.erase(maxCell->second->indexPath); - + + queueReusableCell(maxCell); + this->contentBox->removeView(maxCell); + visibleMax--; } - + while (visibleMin - 1 < cacheFramesData.size() && renderedFrame.getMinY() > visibleFrame.getMinY() - paddingTop) { int i = visibleMin - 1; addCellAt(i, false); } - + while (visibleMax + 1 < cacheFramesData.size() && renderedFrame.getMaxY() < visibleFrame.getMaxY() - paddingBottom) { int i = visibleMax + 1; @@ -228,24 +262,33 @@ void RecyclerFrame::addCellAt(int index, int downSide) cell->setMaxWidth(renderedFrame.getWidth() - paddingLeft - paddingRight); Point cellOrigin = Point(renderedFrame.getMinX() + paddingLeft, (downSide ? renderedFrame.getMaxY() : renderedFrame.getMinY() - cell->getHeight()) + paddingTop); cell->setDetachedPosition(cellOrigin.x, cellOrigin.y); - cell->indexPath = index; - this->contentBox->addView(cell); + cell->setIndexPath(index); + + this->contentBox->getChildren().insert(this->contentBox->getChildren().end(), cell); - visibleCells.insert(std::make_pair(index, cell)); + // Allocate and set parent userdata + size_t* userdata = (size_t*)malloc(sizeof(size_t)); + *userdata = index; + + cell->setParent(this->contentBox, userdata); + + // Layout and events + this->contentBox->invalidate(); + cell->View::willAppear(); if (index < visibleMin) visibleMin = index; if (index > visibleMax) visibleMax = index; - + Rect cellFrame = cell->getFrame(); - + if (!downSide) renderedFrame.origin.y -= cellFrame.getHeight(); - + renderedFrame.size.height += cellFrame.getHeight(); - + if (cellFrame.getHeight() != cacheFramesData[index].height) { float delta = cellFrame.getHeight() - cacheFramesData[index].height; @@ -254,9 +297,16 @@ void RecyclerFrame::addCellAt(int index, int downSide) } } +void RecyclerFrame::onLayout() +{ + ScrollingFrame::onLayout(); + if (checkWidth()) + reloadData(); +} + void RecyclerFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) { - cellRecycling(); + cellsRecyclingLoop(); ScrollingFrame::draw(vg, x, y, width, height, style, ctx); } @@ -267,11 +317,11 @@ void RecyclerFrame::setPadding(float padding) void RecyclerFrame::setPadding(float top, float right, float bottom, float left) { - paddingTop = top; - paddingRight = right; + paddingTop = top; + paddingRight = right; paddingBottom = bottom; - paddingLeft = left; - + paddingLeft = left; + this->reloadData(); } From bf2f59fc747e0e380a56f36b029ae17bc30b6a63 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sun, 21 Mar 2021 02:05:54 +0300 Subject: [PATCH 42/59] Added cell selection data source event --- demo/recycling_list_tab.cpp | 15 +++-- demo/recycling_list_tab.hpp | 1 + library/include/borealis/views/recycler.hpp | 24 ++++++- library/lib/views/recycler.cpp | 70 +++++++++++++++++++-- resources/xml/cells/cell.xml | 4 +- 5 files changed, 99 insertions(+), 15 deletions(-) diff --git a/demo/recycling_list_tab.cpp b/demo/recycling_list_tab.cpp index 3fcf0391..f7b3fa5c 100644 --- a/demo/recycling_list_tab.cpp +++ b/demo/recycling_list_tab.cpp @@ -30,7 +30,7 @@ RecyclerCell* RecyclerCell::create() int DataSource::numberOfRows() { - return 1000; + return 30; } brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, int row) @@ -38,10 +38,15 @@ brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, int ro RecyclerCell* item = (RecyclerCell*)recycler->dequeueReusableCell("Cell"); item->label->setText("Item #" + std::to_string(row)); if (row == 7) - item->label->setText("Item # JM FKNKLmf knlwkenf jkN Fklmew kfnmL Fl enfklMEK MlKWNMF lkem KmLK NefnlkMElkf mlkwemf lkMLKFEm KLMlkf mLKMF lkMSElk fmLKFM lkesb fhhsk gmNLKmkS fe"); + item->label->setText("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."); return item; } +void DataSource::didSelectRowAt(int row) +{ + brls::Logger::info("Cell #" + std::to_string(row) + " selected."); +} + // RECYCLER VIEW RecyclingListTab::RecyclingListTab() @@ -51,12 +56,6 @@ RecyclingListTab::RecyclingListTab() recycler->registerCell("Cell", []() { return RecyclerCell::create(); }); recycler->setDataSource(new DataSource()); - - recycler->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureStatus status) { - this->recycler->reloadData(); - return false; - }, - true)); } brls::View* RecyclingListTab::create() diff --git a/demo/recycling_list_tab.hpp b/demo/recycling_list_tab.hpp index 6f399cb5..b97c0134 100644 --- a/demo/recycling_list_tab.hpp +++ b/demo/recycling_list_tab.hpp @@ -36,6 +36,7 @@ class DataSource public: int numberOfRows() override; brls::RecyclerCell* cellForRow(brls::RecyclerFrame* recycler, int row) override; + void didSelectRowAt(int row) override; }; class RecyclingListTab : public brls::Box diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index 204db6be..2025f91c 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -30,6 +30,8 @@ namespace brls class RecyclerCell : public Box { public: + RecyclerCell(); + /* * Cell's position inside recycler frame */ @@ -60,14 +62,26 @@ class RecyclerFrame; class RecyclerDataSource { public: + /* + * Tells the data source to return the number of rows in a table view. + */ virtual int numberOfRows() { return 0; } + + /* + * Asks the data source for a cell to insert in a particular location of the recycler frame. + */ virtual RecyclerCell* cellForRow(RecyclerFrame* recycler, int row) { return nullptr; } /* - * Used to provide row height. + * Asks the data source for the height to use for a row in a specified location. * Return -1 to use autoscaling. */ - virtual float cellHeightForRow(int row) { return -1; } + virtual float heightForRow(int row) { return -1; } + + /* + * Tells the data source a row is selected. + */ + virtual void didSelectRowAt(int row) { } }; class RecyclerContentBox : public Box @@ -82,6 +96,7 @@ class RecyclerFrame : public ScrollingFrame { public: RecyclerFrame(); + ~RecyclerFrame(); void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; void onLayout() override; @@ -97,6 +112,11 @@ class RecyclerFrame : public ScrollingFrame */ void setDataSource(RecyclerDataSource* source); + /* + * Get an object that acts as the data source of the recycler frame. + */ + RecyclerDataSource* getDataSource() const; + /* * Reloads the rows of the recycler frame. */ diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 7d66c4c4..09b7eff2 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -15,11 +15,54 @@ */ #include +#include #include namespace brls { +RecyclerCell::RecyclerCell() +{ + this->registerClickAction([this](View* view) { + RecyclerFrame* recycler = dynamic_cast(getParent()->getParent()); + if (recycler) + recycler->getDataSource()->didSelectRowAt(indexPath); + return true; + }); + + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureStatus status) { + Application::giveFocus(this); + for (auto& action : getActions()) + { + if (action.button != static_cast(BUTTON_A)) + continue; + + if (action.available) + { + this->playClickAnimation(status.state != GestureState::UNSURE); + + switch (status.state) + { + case GestureState::UNSURE: + Application::getAudioPlayer()->play(SOUND_FOCUS_CHANGE); + break; + case GestureState::FAILED: + case GestureState::INTERRUPTED: + Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); + break; + case GestureState::END: + if (action.actionListener(this)) + Application::getAudioPlayer()->play(action.sound); + break; + } + return false; + } + } + return true; + }, + false)); +} + RecyclerCell* RecyclerCell::create() { return new RecyclerCell(); @@ -87,17 +130,33 @@ RecyclerFrame::RecyclerFrame() // Create content box this->contentBox = new RecyclerContentBox(); - this->contentBox->setPadding(100, 100, 100, 100); this->setContentView(this->contentBox); } +RecyclerFrame::~RecyclerFrame() +{ + if (this->dataSource) + delete dataSource; + + for (auto it : queueMap) + delete it.second; +} + void RecyclerFrame::setDataSource(RecyclerDataSource* source) { + if (this->dataSource) + delete this->dataSource; + this->dataSource = source; if (checkWidth()) reloadData(); } +RecyclerDataSource* RecyclerFrame::getDataSource() const +{ + return this->dataSource; +} + void RecyclerFrame::reloadData() { auto children = this->contentBox->getChildren(); @@ -155,7 +214,9 @@ RecyclerCell* RecyclerFrame::dequeueReusableCell(std::string identifier) } } - cell->prepareForReuse(); + if (cell) + cell->prepareForReuse(); + return cell; } @@ -174,7 +235,7 @@ void RecyclerFrame::cacheCellFrames() { for (int i = 0; i < dataSource->numberOfRows(); i++) { - float height = dataSource->cellHeightForRow(i); + float height = dataSource->heightForRow(i); if (height == -1) height = estimatedRowHeight; @@ -259,7 +320,7 @@ void RecyclerFrame::cellsRecyclingLoop() void RecyclerFrame::addCellAt(int index, int downSide) { RecyclerCell* cell = dataSource->cellForRow(this, index); - cell->setMaxWidth(renderedFrame.getWidth() - paddingLeft - paddingRight); + cell->setWidth(renderedFrame.getWidth() - paddingLeft - paddingRight); Point cellOrigin = Point(renderedFrame.getMinX() + paddingLeft, (downSide ? renderedFrame.getMaxY() : renderedFrame.getMinY() - cell->getHeight()) + paddingTop); cell->setDetachedPosition(cellOrigin.x, cellOrigin.y); cell->setIndexPath(index); @@ -300,6 +361,7 @@ void RecyclerFrame::addCellAt(int index, int downSide) void RecyclerFrame::onLayout() { ScrollingFrame::onLayout(); + this->contentBox->setWidth(this->getWidth()); if (checkWidth()) reloadData(); } diff --git a/resources/xml/cells/cell.xml b/resources/xml/cells/cell.xml index 91a6677e..347f87f1 100644 --- a/resources/xml/cells/cell.xml +++ b/resources/xml/cells/cell.xml @@ -1,7 +1,9 @@ + focusable="true" + paddingTop="12.5" + paddingBottom="12.5"> Date: Mon, 22 Mar 2021 02:49:12 +0300 Subject: [PATCH 43/59] Fix cells destroying --- library/lib/views/recycler.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 09b7eff2..9dc8b77a 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -216,7 +216,7 @@ RecyclerCell* RecyclerFrame::dequeueReusableCell(std::string identifier) if (cell) cell->prepareForReuse(); - + return cell; } @@ -268,7 +268,7 @@ void RecyclerFrame::cellsRecyclingLoop() while (true) { RecyclerCell* minCell = nullptr; - for (auto it : getChildren()) + for (auto it : contentBox->getChildren()) if (*((size_t*)it->getParentUserData()) == visibleMin) minCell = (RecyclerCell*)it; @@ -282,13 +282,15 @@ void RecyclerFrame::cellsRecyclingLoop() queueReusableCell(minCell); this->contentBox->removeView(minCell); + Logger::debug("Cell #" + std::to_string(visibleMin) + " - destroyed"); + visibleMin++; } while (true) { RecyclerCell* maxCell = nullptr; - for (auto it : getChildren()) + for (auto it : contentBox->getChildren()) if (*((size_t*)it->getParentUserData()) == visibleMax) maxCell = (RecyclerCell*)it; @@ -301,6 +303,8 @@ void RecyclerFrame::cellsRecyclingLoop() queueReusableCell(maxCell); this->contentBox->removeView(maxCell); + Logger::debug("Cell #" + std::to_string(visibleMax) + " - destroyed"); + visibleMax--; } @@ -356,6 +360,8 @@ void RecyclerFrame::addCellAt(int index, int downSide) contentBox->setHeight(contentBox->getHeight() + delta); cacheFramesData[index].height = cellFrame.getHeight(); } + + Logger::debug("Cell #" + std::to_string(index) + " - added"); } void RecyclerFrame::onLayout() From 8bb51a9530be6997490da8b0ab84b456a8b884c2 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Wed, 24 Mar 2021 01:40:04 +0300 Subject: [PATCH 44/59] PR review fixes --- library/include/borealis/core/geometry.hpp | 2 +- library/include/borealis/core/gesture.hpp | 4 +++- library/include/borealis/core/input.hpp | 10 +++++----- .../borealis/core/touch/pan_gesture.hpp | 12 ++++++++---- .../borealis/core/touch/tap_gesture.hpp | 2 +- library/include/borealis/core/view.hpp | 2 +- .../borealis/platforms/glfw/glfw_input.hpp | 2 +- .../borealis/platforms/switch/switch_input.hpp | 6 +++--- library/lib/core/application.cpp | 18 ++++++++---------- library/lib/core/box.cpp | 2 +- library/lib/core/geometry.cpp | 2 +- library/lib/core/gesture.cpp | 2 +- library/lib/core/input.cpp | 10 +--------- library/lib/core/touch/pan_gesture.cpp | 14 +++++++------- library/lib/core/touch/tap_gesture.cpp | 2 +- library/lib/core/view.cpp | 2 +- library/lib/platforms/glfw/glfw_input.cpp | 2 +- library/lib/platforms/switch/switch_input.cpp | 2 +- 18 files changed, 46 insertions(+), 50 deletions(-) diff --git a/library/include/borealis/core/geometry.hpp b/library/include/borealis/core/geometry.hpp index 170eaba3..393411ae 100644 --- a/library/include/borealis/core/geometry.hpp +++ b/library/include/borealis/core/geometry.hpp @@ -16,7 +16,7 @@ #pragma once -#include +#include namespace brls { diff --git a/library/include/borealis/core/gesture.hpp b/library/include/borealis/core/gesture.hpp index e2e38039..944d1c5b 100644 --- a/library/include/borealis/core/gesture.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -39,8 +39,10 @@ enum class GestureState class GestureRecognizer { public: + virtual ~GestureRecognizer() { } + // Main recognition loop, for internal usage only, should not be called anywhere, but Application - virtual GestureState recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound); + virtual GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound); // Interrupt this recognizer // If onlyIfUnsureState == true recognizer will be interupted diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index d50c9a99..1244fb24 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -85,14 +85,14 @@ enum class TouchPhase }; // Contains raw touch data -struct RawTouch +struct RawTouchState { bool pressed; Point position; }; // Contains touch data filled with it's current phase -struct Touch +struct TouchState { TouchPhase phase; Point position; @@ -111,14 +111,14 @@ class InputManager virtual void updateControllerState(ControllerState* state) = 0; /** - * Called once every frame to fill the given Touch struct with the touch state. + * Called once every frame to fill the given RawTouchState struct with the raw touch data. */ - virtual void updateTouchState(RawTouch* state) = 0; + virtual void updateTouchState(RawTouchState* state) = 0; /** * Calculate current touch phase based on it's previous state */ - static Touch computeTouchState(RawTouch currentTouch, Touch lastFrameState); + static TouchState computeTouchState(RawTouchState currentTouch, TouchState lastFrameState); }; }; // namespace brls diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index acad3be5..47414ac8 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -16,6 +16,7 @@ #pragma once +#include #include namespace brls @@ -44,7 +45,7 @@ struct PanGestureStatus PanAcceleration acceleration; }; -typedef std::function PanGestureRespond; +typedef Event PanGestureEvent; // Axis of pan recognition start conditions enum class PanAxis @@ -63,8 +64,8 @@ enum class PanAxis class PanGestureRecognizer : public GestureRecognizer { public: - PanGestureRecognizer(PanGestureRespond respond, PanAxis axis); - GestureState recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) override; + PanGestureRecognizer(PanGestureEvent::Callback respond, PanAxis axis); + GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; // Get pan gesture axis PanAxis getAxis() const { return this->axis; } @@ -72,8 +73,11 @@ class PanGestureRecognizer : public GestureRecognizer // Get current state of recognizer PanGestureStatus getCurrentStatus(); + // Get pan gesture event + PanGestureEvent getPanGestureEvent() const { return panEvent; } + private: - PanGestureRespond respond; + PanGestureEvent panEvent; Point position; Point startPosition; Point delta; diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index 5c8e997f..be86d934 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -38,7 +38,7 @@ class TapGestureRecognizer : public GestureRecognizer { public: TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); - GestureState recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) override; + GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; // Get current state of recognizer TapGestureStatus getCurrentStatus(); diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index aad06dd5..7fa2144f 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -1061,7 +1061,7 @@ class View * that needs to play it's own click sound, * so play default is allowed */ - bool gestureRecognizerRequest(Touch touch, View* firstResponder); + bool gestureRecognizerRequest(TouchState touch, View* firstResponder); /** * Called each frame diff --git a/library/include/borealis/platforms/glfw/glfw_input.hpp b/library/include/borealis/platforms/glfw/glfw_input.hpp index 3c3d01d8..9a52c607 100644 --- a/library/include/borealis/platforms/glfw/glfw_input.hpp +++ b/library/include/borealis/platforms/glfw/glfw_input.hpp @@ -32,7 +32,7 @@ class GLFWInputManager : public InputManager void updateControllerState(ControllerState* state) override; - void updateTouchState(RawTouch* state) override; + void updateTouchState(RawTouchState* state) override; private: GLFWwindow* window; diff --git a/library/include/borealis/platforms/switch/switch_input.hpp b/library/include/borealis/platforms/switch/switch_input.hpp index 429ca003..d3a78bf2 100644 --- a/library/include/borealis/platforms/switch/switch_input.hpp +++ b/library/include/borealis/platforms/switch/switch_input.hpp @@ -1,6 +1,6 @@ /* Copyright 2021 natinusala - Copyright (C) 2021 XITRIX + Copyright (C) 2021 XITRIX Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -32,11 +32,11 @@ class SwitchInputManager : public InputManager void updateControllerState(ControllerState* state); - void updateTouchState(RawTouch* state); + void updateTouchState(RawTouchState* state); private: PadState padState; - Touch oldTouch; + TouchState oldTouch; }; } // namespace brls diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 4a7bde44..f40f42f6 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -163,15 +163,15 @@ bool Application::mainLoop() // Input ControllerState controllerState = {}; - RawTouch rawTouch = {}; + RawTouchState rawTouch = {}; InputManager* inputManager = Application::platform->getInputManager(); inputManager->updateTouchState(&rawTouch); inputManager->updateControllerState(&controllerState); - static Touch oldTouch; - Touch touchState = InputManager::computeTouchState(rawTouch, oldTouch); - oldTouch = touchState; + static TouchState oldTouch; + TouchState touchState = InputManager::computeTouchState(rawTouch, oldTouch); + oldTouch = touchState; // Touch controller events switch (touchState.phase) @@ -181,9 +181,10 @@ bool Application::mainLoop() Application::setInputType(InputType::TOUCH); // Search for first responder, which will be the root of recognition tree - firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] - ->getContentView() - ->hitTest(touchState.position); + if (Application::activitiesStack.size() > 0) + firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] + ->getContentView() + ->hitTest(touchState.position); break; case TouchPhase::NONE: firstResponder = nullptr; @@ -366,9 +367,7 @@ bool Application::setInputType(InputType type) Application::inputType = type; if (type == InputType::GAMEPAD) - { Application::currentFocus->onFocusGained(); - } return true; } @@ -405,7 +404,6 @@ bool Application::handleAction(char button) if (action.available) { - if (action.actionListener(hintParent)) { if (button == BUTTON_A) diff --git a/library/lib/core/box.cpp b/library/lib/core/box.cpp index 1e5b2c77..2cfa7937 100644 --- a/library/lib/core/box.cpp +++ b/library/lib/core/box.cpp @@ -319,7 +319,7 @@ View* Box::getDefaultFocus() View* Box::hitTest(Point point) { // Check if touch fits in view frame - if (getFrame().pointInside(point)) + if (this->getFrame().pointInside(point)) { Logger::debug(describe() + ": --- X: " + std::to_string((int)getX()) + ", Y: " + std::to_string((int)getY()) + ", W: " + std::to_string((int)getWidth()) + ", H: " + std::to_string((int)getHeight())); for (View* child : this->children) diff --git a/library/lib/core/geometry.cpp b/library/lib/core/geometry.cpp index 74cc26ff..21d0bb20 100644 --- a/library/lib/core/geometry.cpp +++ b/library/lib/core/geometry.cpp @@ -1,5 +1,5 @@ /* - Copyright 2021 natinusala + Copyright 2021 XITRIX Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/library/lib/core/gesture.cpp b/library/lib/core/gesture.cpp index 7a1ae37d..48fc7498 100644 --- a/library/lib/core/gesture.cpp +++ b/library/lib/core/gesture.cpp @@ -19,7 +19,7 @@ namespace brls { -GestureState GestureRecognizer::recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) +GestureState GestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) { return GestureState::FAILED; } diff --git a/library/lib/core/input.cpp b/library/lib/core/input.cpp index 281bdbb8..190a81d4 100644 --- a/library/lib/core/input.cpp +++ b/library/lib/core/input.cpp @@ -19,30 +19,22 @@ namespace brls { -Touch InputManager::computeTouchState(RawTouch currentTouch, Touch lastFrameState) +TouchState InputManager::computeTouchState(RawTouchState currentTouch, TouchState lastFrameState) { if (currentTouch.pressed) { lastFrameState.position = currentTouch.position; if (lastFrameState.phase == TouchPhase::START || lastFrameState.phase == TouchPhase::STAY) - { lastFrameState.phase = TouchPhase::STAY; - } else - { lastFrameState.phase = TouchPhase::START; - } } else { if (lastFrameState.phase == TouchPhase::END || lastFrameState.phase == TouchPhase::NONE) - { lastFrameState.phase = TouchPhase::NONE; - } else - { lastFrameState.phase = TouchPhase::END; - } } return lastFrameState; diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index f424f97a..c732bd82 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -24,13 +24,13 @@ namespace brls { -PanGestureRecognizer::PanGestureRecognizer(PanGestureRespond respond, PanAxis axis) - : respond(respond) - , axis(axis) +PanGestureRecognizer::PanGestureRecognizer(PanGestureEvent::Callback respond, PanAxis axis) + : axis(axis) { + panEvent.subscribe(respond); } -GestureState PanGestureRecognizer::recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) +GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) { if (!enabled) return GestureState::FAILED; @@ -41,8 +41,8 @@ GestureState PanGestureRecognizer::recognitionLoop(Touch touch, View* view, bool { if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { - if (respond && this->state != lastState) - this->respond(getCurrentStatus()); + if (this->state != lastState) + this->panEvent.fire(getCurrentStatus()); lastState = this->state; return this->state; @@ -117,7 +117,7 @@ GestureState PanGestureRecognizer::recognitionLoop(Touch touch, View* view, bool { PanGestureStatus state = getCurrentStatus(); state.acceleration = acceleration; - this->respond(state); + this->panEvent.fire(state); } break; diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index 6a9b64f0..e8d776fd 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -25,7 +25,7 @@ TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, bool callb { } -GestureState TapGestureRecognizer::recognitionLoop(Touch touch, View* view, bool* shouldPlayDefaultSound) +GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) { if (!enabled) return GestureState::FAILED; diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index faaf0827..5f0263c9 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -110,7 +110,7 @@ void View::addGestureRecognizer(GestureRecognizer* recognizer) this->gestureRecognizers.push_back(recognizer); } -bool View::gestureRecognizerRequest(Touch touch, View* firstResponder) +bool View::gestureRecognizerRequest(TouchState touch, View* firstResponder) { bool shouldPlayDefaultSound = touch.phase == TouchPhase::START; diff --git a/library/lib/platforms/glfw/glfw_input.cpp b/library/lib/platforms/glfw/glfw_input.cpp index 5f1b4c57..024e3421 100644 --- a/library/lib/platforms/glfw/glfw_input.cpp +++ b/library/lib/platforms/glfw/glfw_input.cpp @@ -112,7 +112,7 @@ void GLFWInputManager::updateControllerState(ControllerState* state) } } -void GLFWInputManager::updateTouchState(RawTouch* state) +void GLFWInputManager::updateTouchState(RawTouchState* state) { // Get touchscreen state double x, y; diff --git a/library/lib/platforms/switch/switch_input.cpp b/library/lib/platforms/switch/switch_input.cpp index 06327241..9017f01c 100644 --- a/library/lib/platforms/switch/switch_input.cpp +++ b/library/lib/platforms/switch/switch_input.cpp @@ -66,7 +66,7 @@ void SwitchInputManager::updateControllerState(ControllerState* state) } } -void SwitchInputManager::updateTouchState(RawTouch* state) +void SwitchInputManager::updateTouchState(RawTouchState* state) { // Get touchscreen state static HidTouchScreenState hidState = { 0 }; From 8d3ab5e5c69807697ae6eefbfd01d17c60f0a407 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Wed, 24 Mar 2021 01:57:05 +0300 Subject: [PATCH 45/59] Stop scrolling on tap --- library/lib/views/scrolling_frame.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index c772a1b1..6ea1b454 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -60,6 +61,14 @@ ScrollingFrame::ScrollingFrame() } }, PanAxis::VERTICAL)); + + // Stop scrolling on tap + addGestureRecognizer(new TapGestureRecognizer([this](brls::TapGestureStatus status) { + if (status.state == GestureState::UNSURE) + this->scrollY.stop(); + return true; + }, + false)); } void ScrollingFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) From a2f3899e3dfbadd5b4f651886a355e60208b5099 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Thu, 25 Mar 2021 01:43:09 +0300 Subject: [PATCH 46/59] Changed sound processing --- demo/captioned_image.cpp | 5 +- library/include/borealis/core/gesture.hpp | 25 +++++- .../borealis/core/touch/pan_gesture.hpp | 2 +- .../borealis/core/touch/tap_gesture.hpp | 32 +++++++- library/include/borealis/core/view.hpp | 2 +- library/lib/core/application.cpp | 12 +-- library/lib/core/gesture.cpp | 2 +- library/lib/core/touch/pan_gesture.cpp | 2 +- library/lib/core/touch/tap_gesture.cpp | 76 ++++++++++++++++--- library/lib/core/view.cpp | 14 ++-- library/lib/views/button.cpp | 32 +------- library/lib/views/scrolling_frame.cpp | 6 +- library/lib/views/sidebar.cpp | 17 ++--- 13 files changed, 148 insertions(+), 79 deletions(-) diff --git a/demo/captioned_image.cpp b/demo/captioned_image.cpp index 9257b473..af80a5a2 100644 --- a/demo/captioned_image.cpp +++ b/demo/captioned_image.cpp @@ -39,10 +39,7 @@ CaptionedImage::CaptionedImage() this->forwardXMLAttribute("caption", this->label, "text"); - this->addGestureRecognizer(new brls::TapGestureRecognizer([this](brls::TapGestureStatus status) { - brls::Application::giveFocus(this); - return true; - })); + this->addGestureRecognizer(new brls::TapGestureRecognizer(this, brls::TapGestureConfig(false, brls::SOUND_NONE, brls::SOUND_NONE, brls::SOUND_NONE))); } void CaptionedImage::onChildFocusGained(brls::View* directChild, brls::View* focusedView) diff --git a/library/include/borealis/core/gesture.hpp b/library/include/borealis/core/gesture.hpp index 944d1c5b..ebf80297 100644 --- a/library/include/borealis/core/gesture.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -17,6 +17,7 @@ #pragma once #include +#include #include namespace brls @@ -35,14 +36,34 @@ enum class GestureState FAILED, // Gesture failed conditions }; -// Superclass for all the other recognizers +/* +* Superclass for all recognizers +* +* To create a new type of gesture recognizer, you should implement +* recognitionLoop method. +* +* It should contain logic with changing gesture's state. +* Recognizers' first state is UNSURE, in that state calling stack cannot tell +* which gesture user tries to apply. I.E. user puts and holds finger on the screen, so it can't be +* told whether it's going to be a tap or a swipe +* +* If gesture has been recognized, change its state to START, but ONLY for the first frame, +* on the next frame it should be changed to STAY and remain the same until the end, then it +* should change state to END. +* +* When any recognizer changes its state to START, it sends an interrupt event to every recognizer in the stack, +* so don't forget to handle that case. +* +* If gesture does not apply to recognizer's pattern, change its state to FAILED. +* It could also be used as a placerholder when recognizer is not in use. +*/ class GestureRecognizer { public: virtual ~GestureRecognizer() { } // Main recognition loop, for internal usage only, should not be called anywhere, but Application - virtual GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound); + virtual GestureState recognitionLoop(TouchState touch, View* view, Sound* soundToPlay); // Interrupt this recognizer // If onlyIfUnsureState == true recognizer will be interupted diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index 47414ac8..8de1d636 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -65,7 +65,7 @@ class PanGestureRecognizer : public GestureRecognizer { public: PanGestureRecognizer(PanGestureEvent::Callback respond, PanAxis axis); - GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; + GestureState recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) override; // Get pan gesture axis PanAxis getAxis() const { return this->axis; } diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index be86d934..3929185f 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -21,12 +21,29 @@ namespace brls { +struct TapGestureConfig +{ + bool highlightOnSelect = true; + Sound unsureSound = SOUND_FOCUS_CHANGE; + Sound failedSound = SOUND_TOUCH_UNFOCUS; + Sound endSound = SOUND_CLICK; + + TapGestureConfig() {} + TapGestureConfig(bool highlightOnSelect, Sound unsureSound, Sound failedSound,Sound endSound) + { + this->highlightOnSelect = highlightOnSelect; + this->unsureSound = unsureSound; + this->failedSound = failedSound; + this->endSound = endSound; + } +}; + struct TapGestureStatus { GestureState state; // Gesture state Point position; // Current position }; -typedef std::function TapGestureRespond; +typedef std::function TapGestureRespond; // Tap recognizer // UNSURE: while touch moves inside of View bounds @@ -37,8 +54,16 @@ typedef std::function TapGestureRespond; class TapGestureRecognizer : public GestureRecognizer { public: - TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly = true); - GestureState recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) override; + // Simple ctor which uses View's primary action as response which will be called only on recognizer state END. + TapGestureRecognizer(View* view, TapGestureConfig config = TapGestureConfig()); + + // Simple ctor with custom response which will be called only on recognizer state END. + TapGestureRecognizer(View* view, std::function respond, TapGestureConfig config = TapGestureConfig()); + + // Complex ctor with fully controllable response. + TapGestureRecognizer(TapGestureRespond respond); + + GestureState recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) override; // Get current state of recognizer TapGestureStatus getCurrentStatus(); @@ -46,7 +71,6 @@ class TapGestureRecognizer : public GestureRecognizer private: TapGestureRespond respond; Point position; - bool callbackOnEndOnly; GestureState lastState; }; diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index 58561543..3516e78f 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -1069,7 +1069,7 @@ class View * that needs to play it's own click sound, * so play default is allowed */ - bool gestureRecognizerRequest(TouchState touch, View* firstResponder); + Sound gestureRecognizerRequest(TouchState touch, View* firstResponder); /** * Called each frame diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index d28690e7..435b2587 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -181,8 +181,8 @@ bool Application::mainLoop() // Search for first responder, which will be the root of recognition tree if (Application::activitiesStack.size() > 0) firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] - ->getContentView() - ->hitTest(touchState.position); + ->getContentView() + ->hitTest(touchState.position); break; case TouchPhase::NONE: firstResponder = nullptr; @@ -193,12 +193,14 @@ bool Application::mainLoop() if (firstResponder) { - if (firstResponder->gestureRecognizerRequest(touchState, firstResponder)) + Sound sound = firstResponder->gestureRecognizerRequest(touchState, firstResponder); + float pitch = 1; + if (sound == SOUND_TOUCH) { // Play touch sound with random pitch - float pitch = (rand() % 10) / 10.0f + 1.0f; - Application::getAudioPlayer()->play(SOUND_TOUCH, pitch); + pitch = (rand() % 10) / 10.0f + 1.0f; } + Application::getAudioPlayer()->play(sound, pitch); } // Trigger controller events diff --git a/library/lib/core/gesture.cpp b/library/lib/core/gesture.cpp index 48fc7498..ec09000a 100644 --- a/library/lib/core/gesture.cpp +++ b/library/lib/core/gesture.cpp @@ -19,7 +19,7 @@ namespace brls { -GestureState GestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) +GestureState GestureRecognizer::recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) { return GestureState::FAILED; } diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index c732bd82..254eb3f8 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -30,7 +30,7 @@ PanGestureRecognizer::PanGestureRecognizer(PanGestureEvent::Callback respond, Pa panEvent.subscribe(respond); } -GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) +GestureState PanGestureRecognizer::recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) { if (!enabled) return GestureState::FAILED; diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index e8d776fd..05917b58 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -19,13 +19,69 @@ namespace brls { -TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond, bool callbackOnEndOnly) +TapGestureRecognizer::TapGestureRecognizer(View* view, TapGestureConfig config) +{ + this->respond = [view, config](TapGestureStatus status, Sound* soundToPlay) { + Application::giveFocus(view); + for (auto& action : view->getActions()) + { + if (action.button != static_cast(BUTTON_A)) + continue; + + if (action.available) + { + if (config.highlightOnSelect) + view->playClickAnimation(status.state != GestureState::UNSURE); + + switch (status.state) + { + case GestureState::UNSURE: + *soundToPlay = config.unsureSound; + break; + case GestureState::FAILED: + case GestureState::INTERRUPTED: + *soundToPlay = config.failedSound; + break; + case GestureState::END: + if (action.actionListener(view)) + *soundToPlay = action.sound; + break; + } + } + } + }; +} + +TapGestureRecognizer::TapGestureRecognizer(View* view, std::function respond, TapGestureConfig config) +{ + this->respond = [view, respond, config](TapGestureStatus status, Sound* soundToPlay) { + Application::giveFocus(view); + if (config.highlightOnSelect) + view->playClickAnimation(status.state != GestureState::UNSURE); + + switch (status.state) + { + case GestureState::UNSURE: + *soundToPlay = config.unsureSound; + break; + case GestureState::FAILED: + case GestureState::INTERRUPTED: + *soundToPlay = config.failedSound; + break; + case GestureState::END: + *soundToPlay = config.endSound; + respond(); + break; + } + }; +} + +TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond) : respond(respond) - , callbackOnEndOnly(callbackOnEndOnly) { } -GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, bool* shouldPlayDefaultSound) +GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) { if (!enabled) return GestureState::FAILED; @@ -36,8 +92,8 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, { if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { - if (respond && !this->callbackOnEndOnly && this->state != lastState) - this->respond(getCurrentStatus()); + if (respond && this->state != lastState) + this->respond(getCurrentStatus(), soundToPlay); lastState = this->state; return this->state; @@ -50,8 +106,8 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, this->state = GestureState::UNSURE; this->position = touch.position; - if (respond && !this->callbackOnEndOnly) - *shouldPlayDefaultSound &= this->respond(getCurrentStatus()); + if (respond) + this->respond(getCurrentStatus(), soundToPlay); break; case TouchPhase::STAY: // Check if touch is out view's bounds @@ -59,14 +115,14 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, if (touch.position.x < view->getX() || touch.position.x > view->getX() + view->getWidth() || touch.position.y < view->getY() || touch.position.y > view->getY() + view->getHeight()) { this->state = GestureState::FAILED; - if (respond && !this->callbackOnEndOnly) - this->respond(getCurrentStatus()); + if (respond) + this->respond(getCurrentStatus(), soundToPlay); } break; case TouchPhase::END: this->state = GestureState::END; if (respond) - this->respond(getCurrentStatus()); + this->respond(getCurrentStatus(), soundToPlay); break; case TouchPhase::NONE: this->state = GestureState::FAILED; diff --git a/library/lib/core/view.cpp b/library/lib/core/view.cpp index 5040c90d..55780a6e 100644 --- a/library/lib/core/view.cpp +++ b/library/lib/core/view.cpp @@ -110,24 +110,28 @@ void View::addGestureRecognizer(GestureRecognizer* recognizer) this->gestureRecognizers.push_back(recognizer); } -bool View::gestureRecognizerRequest(TouchState touch, View* firstResponder) +Sound View::gestureRecognizerRequest(TouchState touch, View* firstResponder) { - bool shouldPlayDefaultSound = touch.phase == TouchPhase::START; + Sound soundToPlay = touch.phase == TouchPhase::START ? SOUND_TOUCH : SOUND_NONE; for (GestureRecognizer* recognizer : getGestureRecognizers()) { if (!recognizer->isEnabled()) continue; - GestureState state = recognizer->recognitionLoop(touch, this, &shouldPlayDefaultSound); + GestureState state = recognizer->recognitionLoop(touch, this, &soundToPlay); if (state == GestureState::START) firstResponder->interruptGestures(true); } + Sound parentSound = SOUND_NONE; if (parent) - shouldPlayDefaultSound &= parent->gestureRecognizerRequest(touch, firstResponder); + parentSound = parent->gestureRecognizerRequest(touch, firstResponder); - return shouldPlayDefaultSound; + if (soundToPlay == SOUND_NONE) + soundToPlay = parentSound; + + return soundToPlay; } void View::frame(FrameContext* ctx) diff --git a/library/lib/views/button.cpp b/library/lib/views/button.cpp index 536e153a..039f5590 100644 --- a/library/lib/views/button.cpp +++ b/library/lib/views/button.cpp @@ -81,37 +81,7 @@ Button::Button() this->applyStyle(); - this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureStatus status) { - Application::giveFocus(this); - for (auto& action : getActions()) - { - if (action.button != static_cast(BUTTON_A)) - continue; - - if (action.available) - { - this->playClickAnimation(status.state != GestureState::UNSURE); - - switch (status.state) - { - case GestureState::UNSURE: - Application::getAudioPlayer()->play(SOUND_FOCUS_CHANGE); - break; - case GestureState::FAILED: - case GestureState::INTERRUPTED: - Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); - break; - case GestureState::END: - if (action.actionListener(this)) - Application::getAudioPlayer()->play(action.sound); - break; - } - return false; - } - } - return true; - }, - false)); + this->addGestureRecognizer(new TapGestureRecognizer(this)); } void Button::applyStyle() diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 6ea1b454..e813313e 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -63,12 +63,10 @@ ScrollingFrame::ScrollingFrame() PanAxis::VERTICAL)); // Stop scrolling on tap - addGestureRecognizer(new TapGestureRecognizer([this](brls::TapGestureStatus status) { + addGestureRecognizer(new TapGestureRecognizer([this](brls::TapGestureStatus status, Sound* soundToPlay) { if (status.state == GestureState::UNSURE) this->scrollY.stop(); - return true; - }, - false)); + })); } void ScrollingFrame::draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index 0f5a2ca5..81887221 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -73,31 +73,28 @@ SidebarItem::SidebarItem() return true; }, false, SOUND_CLICK_SIDEBAR); - - this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureStatus status) { + + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureStatus status, Sound* soundToPlay) { if (this->active) - return true; + return; this->playClickAnimation(status.state != GestureState::UNSURE); switch (status.state) { case GestureState::UNSURE: - Application::getAudioPlayer()->play(SOUND_FOCUS_SIDEBAR); + *soundToPlay = SOUND_FOCUS_SIDEBAR; break; case GestureState::FAILED: case GestureState::INTERRUPTED: - Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); + *soundToPlay = SOUND_TOUCH_UNFOCUS; break; case GestureState::END: - Application::getAudioPlayer()->play(SOUND_CLICK_SIDEBAR); + *soundToPlay = SOUND_CLICK_SIDEBAR; Application::giveFocus(this); break; } - - return false; - }, - false)); + })); } void SidebarItem::setActive(bool active) From f63d92c7f43e617704ba8d3387f64349d09ce80e Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Thu, 25 Mar 2021 01:51:01 +0300 Subject: [PATCH 47/59] Changed function to Event in tap recognizer --- library/include/borealis/core/gesture.hpp | 2 +- .../borealis/core/touch/tap_gesture.hpp | 27 ++++++++++--------- library/lib/core/touch/tap_gesture.cpp | 26 ++++++++---------- library/lib/views/sidebar.cpp | 2 +- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/library/include/borealis/core/gesture.hpp b/library/include/borealis/core/gesture.hpp index ebf80297..01521db2 100644 --- a/library/include/borealis/core/gesture.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -16,8 +16,8 @@ #pragma once -#include #include +#include #include namespace brls diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index 3929185f..7e0ac78f 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -24,17 +24,17 @@ namespace brls struct TapGestureConfig { bool highlightOnSelect = true; - Sound unsureSound = SOUND_FOCUS_CHANGE; - Sound failedSound = SOUND_TOUCH_UNFOCUS; - Sound endSound = SOUND_CLICK; + Sound unsureSound = SOUND_FOCUS_CHANGE; + Sound failedSound = SOUND_TOUCH_UNFOCUS; + Sound endSound = SOUND_CLICK; - TapGestureConfig() {} - TapGestureConfig(bool highlightOnSelect, Sound unsureSound, Sound failedSound,Sound endSound) + TapGestureConfig() { } + TapGestureConfig(bool highlightOnSelect, Sound unsureSound, Sound failedSound, Sound endSound) { this->highlightOnSelect = highlightOnSelect; - this->unsureSound = unsureSound; - this->failedSound = failedSound; - this->endSound = endSound; + this->unsureSound = unsureSound; + this->failedSound = failedSound; + this->endSound = endSound; } }; @@ -43,7 +43,7 @@ struct TapGestureStatus GestureState state; // Gesture state Point position; // Current position }; -typedef std::function TapGestureRespond; +typedef Event TapGestureEvent; // Tap recognizer // UNSURE: while touch moves inside of View bounds @@ -61,15 +61,18 @@ class TapGestureRecognizer : public GestureRecognizer TapGestureRecognizer(View* view, std::function respond, TapGestureConfig config = TapGestureConfig()); // Complex ctor with fully controllable response. - TapGestureRecognizer(TapGestureRespond respond); - + TapGestureRecognizer(TapGestureEvent::Callback respond); + GestureState recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) override; // Get current state of recognizer TapGestureStatus getCurrentStatus(); + // Get tap gesture event + TapGestureEvent getPanGestureEvent() const { return tapEvent; } + private: - TapGestureRespond respond; + TapGestureEvent tapEvent; Point position; GestureState lastState; }; diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index 05917b58..c9ed6dc1 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -21,7 +21,7 @@ namespace brls TapGestureRecognizer::TapGestureRecognizer(View* view, TapGestureConfig config) { - this->respond = [view, config](TapGestureStatus status, Sound* soundToPlay) { + this->tapEvent.subscribe([view, config](TapGestureStatus status, Sound* soundToPlay) { Application::giveFocus(view); for (auto& action : view->getActions()) { @@ -49,12 +49,12 @@ TapGestureRecognizer::TapGestureRecognizer(View* view, TapGestureConfig config) } } } - }; + }); } TapGestureRecognizer::TapGestureRecognizer(View* view, std::function respond, TapGestureConfig config) { - this->respond = [view, respond, config](TapGestureStatus status, Sound* soundToPlay) { + this->tapEvent.subscribe([view, respond, config](TapGestureStatus status, Sound* soundToPlay) { Application::giveFocus(view); if (config.highlightOnSelect) view->playClickAnimation(status.state != GestureState::UNSURE); @@ -73,12 +73,12 @@ TapGestureRecognizer::TapGestureRecognizer(View* view, std::function res respond(); break; } - }; + }); } -TapGestureRecognizer::TapGestureRecognizer(TapGestureRespond respond) - : respond(respond) +TapGestureRecognizer::TapGestureRecognizer(TapGestureEvent::Callback respond) { + tapEvent.subscribe(respond); } GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) @@ -92,8 +92,8 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, { if (this->state == GestureState::INTERRUPTED || this->state == GestureState::FAILED) { - if (respond && this->state != lastState) - this->respond(getCurrentStatus(), soundToPlay); + if (this->state != lastState) + this->tapEvent.fire(getCurrentStatus(), soundToPlay); lastState = this->state; return this->state; @@ -105,9 +105,7 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, case TouchPhase::START: this->state = GestureState::UNSURE; this->position = touch.position; - - if (respond) - this->respond(getCurrentStatus(), soundToPlay); + this->tapEvent.fire(getCurrentStatus(), soundToPlay); break; case TouchPhase::STAY: // Check if touch is out view's bounds @@ -115,14 +113,12 @@ GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, if (touch.position.x < view->getX() || touch.position.x > view->getX() + view->getWidth() || touch.position.y < view->getY() || touch.position.y > view->getY() + view->getHeight()) { this->state = GestureState::FAILED; - if (respond) - this->respond(getCurrentStatus(), soundToPlay); + this->tapEvent.fire(getCurrentStatus(), soundToPlay); } break; case TouchPhase::END: this->state = GestureState::END; - if (respond) - this->respond(getCurrentStatus(), soundToPlay); + this->tapEvent.fire(getCurrentStatus(), soundToPlay); break; case TouchPhase::NONE: this->state = GestureState::FAILED; diff --git a/library/lib/views/sidebar.cpp b/library/lib/views/sidebar.cpp index 81887221..8d22bc5a 100644 --- a/library/lib/views/sidebar.cpp +++ b/library/lib/views/sidebar.cpp @@ -73,7 +73,7 @@ SidebarItem::SidebarItem() return true; }, false, SOUND_CLICK_SIDEBAR); - + this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureStatus status, Sound* soundToPlay) { if (this->active) return; From d5035fe50de8d6863c1618b2bd9e907d950c8682 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Thu, 25 Mar 2021 02:06:20 +0300 Subject: [PATCH 48/59] Gesture documentation update --- library/include/borealis/core/gesture.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/include/borealis/core/gesture.hpp b/library/include/borealis/core/gesture.hpp index 01521db2..fff96506 100644 --- a/library/include/borealis/core/gesture.hpp +++ b/library/include/borealis/core/gesture.hpp @@ -56,6 +56,13 @@ enum class GestureState * * If gesture does not apply to recognizer's pattern, change its state to FAILED. * It could also be used as a placerholder when recognizer is not in use. +* +* Use touch argument to get current state of touch. +* +* View argument contains the view to which this recognizer is attached. +* +* Use soundToPlay pointer to set sound which will be played in current frame. +* Leave it empty or use SOUND_NONE to not play any sound. */ class GestureRecognizer { From 6946d86f8d3e0c4c4bd2f1d2679b7dbda7e9cb7a Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Thu, 25 Mar 2021 22:55:28 +0300 Subject: [PATCH 49/59] Merge fix --- library/lib/views/recycler.cpp | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 9dc8b77a..47729d37 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -30,37 +30,7 @@ RecyclerCell::RecyclerCell() return true; }); - this->addGestureRecognizer(new TapGestureRecognizer([this](TapGestureStatus status) { - Application::giveFocus(this); - for (auto& action : getActions()) - { - if (action.button != static_cast(BUTTON_A)) - continue; - - if (action.available) - { - this->playClickAnimation(status.state != GestureState::UNSURE); - - switch (status.state) - { - case GestureState::UNSURE: - Application::getAudioPlayer()->play(SOUND_FOCUS_CHANGE); - break; - case GestureState::FAILED: - case GestureState::INTERRUPTED: - Application::getAudioPlayer()->play(SOUND_TOUCH_UNFOCUS); - break; - case GestureState::END: - if (action.actionListener(this)) - Application::getAudioPlayer()->play(action.sound); - break; - } - return false; - } - } - return true; - }, - false)); + this->addGestureRecognizer(new TapGestureRecognizer(this)); } RecyclerCell* RecyclerCell::create() From cdd150e00b8659bb5d3793a8474a1772bb8aa6c1 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sun, 28 Mar 2021 20:04:06 +0300 Subject: [PATCH 50/59] Mouse scrolling --- library/include/borealis.hpp | 1 + library/include/borealis/core/geometry.hpp | 3 + library/include/borealis/core/input.hpp | 2 + .../borealis/core/touch/pan_gesture.hpp | 1 + .../borealis/core/touch/scroll_gesture.hpp | 41 +++++++++++++ .../borealis/platforms/glfw/glfw_input.hpp | 2 + library/lib/core/application.cpp | 12 ++++ library/lib/core/geometry.cpp | 17 ++++++ library/lib/core/input.cpp | 8 ++- library/lib/core/touch/scroll_gesture.cpp | 57 +++++++++++++++++++ library/lib/platforms/glfw/glfw_input.cpp | 30 +++++++--- library/lib/views/scrolling_frame.cpp | 11 +++- library/meson.build | 3 +- 13 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 library/include/borealis/core/touch/scroll_gesture.hpp create mode 100644 library/lib/core/touch/scroll_gesture.cpp diff --git a/library/include/borealis.hpp b/library/include/borealis.hpp index 9d8cda11..7be47ca3 100644 --- a/library/include/borealis.hpp +++ b/library/include/borealis.hpp @@ -56,4 +56,5 @@ // Gestures #include +#include #include diff --git a/library/include/borealis/core/geometry.hpp b/library/include/borealis/core/geometry.hpp index 393411ae..82655b60 100644 --- a/library/include/borealis/core/geometry.hpp +++ b/library/include/borealis/core/geometry.hpp @@ -38,6 +38,9 @@ struct Point Point operator/(const float& a) const; Point operator*(const float& a) const; bool operator==(const Point& other) const; + bool operator!=(const Point& other) const; + void operator+=(const Point& a); + void operator-=(const Point& a); }; // A structure that contains width and height values. diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index 1244fb24..5f4a28e1 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -89,6 +89,7 @@ struct RawTouchState { bool pressed; Point position; + Point scroll; }; // Contains touch data filled with it's current phase @@ -96,6 +97,7 @@ struct TouchState { TouchPhase phase; Point position; + Point scroll; }; // Interface responsible for reporting input state to the application - button presses, diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index 8de1d636..d3115116 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -39,6 +39,7 @@ struct PanGestureStatus Point position; // Current position Point startPosition; // Start X position Point delta; // Difference between current and previous positions by X + bool deltaOnly = false; // If true, current state will contain delta values ONLY // Acceleration info, NOT NULL ONLY from // gesture callback and when current state is END diff --git a/library/include/borealis/core/touch/scroll_gesture.hpp b/library/include/borealis/core/touch/scroll_gesture.hpp new file mode 100644 index 00000000..f6f02aa1 --- /dev/null +++ b/library/include/borealis/core/touch/scroll_gesture.hpp @@ -0,0 +1,41 @@ +/* + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include + +namespace brls +{ + +/* + * Scroll recognizer + * + * Child of Pan recognizer. + * The only difference is that Scroll recognizer will translate scrolling wheel to pan gestures. + * If NO_TOUCH_SCROLLING=true defined, will ignore scrolling by touch. + * + * If mouse translation used, the only available state is MOVE. + * Also PanGestureStatus will be returned with delta values ONLY. + */ +class ScrollGestureRecognizer : public PanGestureRecognizer +{ + public: + ScrollGestureRecognizer(PanGestureEvent::Callback respond, PanAxis axis); + GestureState recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) override; +}; + +}; diff --git a/library/include/borealis/platforms/glfw/glfw_input.hpp b/library/include/borealis/platforms/glfw/glfw_input.hpp index 9a52c607..edd3ba74 100644 --- a/library/include/borealis/platforms/glfw/glfw_input.hpp +++ b/library/include/borealis/platforms/glfw/glfw_input.hpp @@ -35,6 +35,8 @@ class GLFWInputManager : public InputManager void updateTouchState(RawTouchState* state) override; private: + Point scrollOffset; + static void scrollCallback(GLFWwindow* window, double xoffset, double yoffset); GLFWwindow* window; }; diff --git a/library/lib/core/application.cpp b/library/lib/core/application.cpp index 435b2587..8e1ca26b 100644 --- a/library/lib/core/application.cpp +++ b/library/lib/core/application.cpp @@ -191,6 +191,18 @@ bool Application::mainLoop() break; } + if (!firstResponder && (touchState.scroll.x != 0 || touchState.scroll.y != 0)) + { + Logger::debug("Touched at X: " + std::to_string(touchState.position.x) + ", Y: " + std::to_string(touchState.position.y)); + Application::setInputType(InputType::TOUCH); + + // Search for first responder, which will be the root of recognition tree + if (Application::activitiesStack.size() > 0) + firstResponder = Application::activitiesStack[Application::activitiesStack.size() - 1] + ->getContentView() + ->hitTest(touchState.position); + } + if (firstResponder) { Sound sound = firstResponder->gestureRecognizerRequest(touchState, firstResponder); diff --git a/library/lib/core/geometry.cpp b/library/lib/core/geometry.cpp index 21d0bb20..d6faceb3 100644 --- a/library/lib/core/geometry.cpp +++ b/library/lib/core/geometry.cpp @@ -56,6 +56,23 @@ bool Point::operator==(const Point& other) const return x == other.x && y == other.y; } +bool Point::operator!=(const Point& other) const +{ + return x != other.x || y != other.y; +} + +void Point::operator+=(const Point& a) +{ + this->x += a.x; + this->y += a.y; +} + +void Point::operator-=(const Point& a) +{ + this->x -= a.x; + this->y -= a.y; +} + // Size Size::Size() : Size(0.0f, 0.0f) diff --git a/library/lib/core/input.cpp b/library/lib/core/input.cpp index 190a81d4..8897244a 100644 --- a/library/lib/core/input.cpp +++ b/library/lib/core/input.cpp @@ -37,7 +37,13 @@ TouchState InputManager::computeTouchState(RawTouchState currentTouch, TouchStat lastFrameState.phase = TouchPhase::END; } + if (currentTouch.scroll.x != 0 || currentTouch.scroll.y != 0) + { + lastFrameState.position = currentTouch.position; + } + lastFrameState.scroll = currentTouch.scroll; + return lastFrameState; } -} // namespace brls \ No newline at end of file +} // namespace brls diff --git a/library/lib/core/touch/scroll_gesture.cpp b/library/lib/core/touch/scroll_gesture.cpp new file mode 100644 index 00000000..6501d439 --- /dev/null +++ b/library/lib/core/touch/scroll_gesture.cpp @@ -0,0 +1,57 @@ +/* + Copyright 2021 XITRIX + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include + +#ifndef NO_TOUCH_SCROLLING +#define NO_TOUCH_SCROLLING false +#endif + +namespace brls +{ + +ScrollGestureRecognizer::ScrollGestureRecognizer(PanGestureEvent::Callback respond, PanAxis axis) + : PanGestureRecognizer(respond, axis) +{ +} + +GestureState ScrollGestureRecognizer::recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) +{ + if (!enabled) + return GestureState::FAILED; + + GestureState result; + if (NO_TOUCH_SCROLLING) + result = GestureState::FAILED; + else + result = PanGestureRecognizer::recognitionLoop(touch, view, soundToPlay); + + if (result == GestureState::FAILED && (touch.scroll.x != 0 || touch.scroll.y != 0)) + { + PanGestureStatus status { + .state = GestureState::STAY, + .position = Point(), + .startPosition = Point(), + .delta = touch.scroll, + .deltaOnly = true, + }; + this->getPanGestureEvent().fire(status); + } + + return result; +} + +}; diff --git a/library/lib/platforms/glfw/glfw_input.cpp b/library/lib/platforms/glfw/glfw_input.cpp index 024e3421..694f9f3a 100644 --- a/library/lib/platforms/glfw/glfw_input.cpp +++ b/library/lib/platforms/glfw/glfw_input.cpp @@ -80,10 +80,18 @@ static void glfwJoystickCallback(int jid, int event) } } +void GLFWInputManager::scrollCallback(GLFWwindow* window, double xoffset, double yoffset) +{ + GLFWInputManager* self = (GLFWInputManager*)Application::getPlatform()->getInputManager(); + self->scrollOffset.x += xoffset * 10; + self->scrollOffset.y += yoffset * 10; +} + GLFWInputManager::GLFWInputManager(GLFWwindow* window) : window(window) { glfwSetJoystickCallback(glfwJoystickCallback); + glfwSetScrollCallback(window, scrollCallback); if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1)) { @@ -112,20 +120,26 @@ void GLFWInputManager::updateControllerState(ControllerState* state) } } +bool sameSign(int a, int b) +{ + if (a == 0 || b == 0) + return true; + return (a >= 0) ^ (b < 0); +} + void GLFWInputManager::updateTouchState(RawTouchState* state) { // Get touchscreen state double x, y; glfwGetCursorPos(this->window, &x, &y); - state->pressed = false; - int clickState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); - if (clickState == GLFW_PRESS) - { - state->pressed = true; - state->position.x = x / Application::windowScale; - state->position.y = y / Application::windowScale; - } + state->pressed = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS; + state->position.x = x / Application::windowScale; + state->position.y = y / Application::windowScale; + state->scroll = scrollOffset; + + scrollOffset.x = 0; + scrollOffset.y = 0; } }; diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index e813313e..a83e67f1 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -16,7 +16,7 @@ */ #include -#include +#include #include #include #include @@ -35,9 +35,16 @@ ScrollingFrame::ScrollingFrame() this->setMaximumAllowedXMLElements(1); - addGestureRecognizer(new PanGestureRecognizer([this](PanGestureStatus state) { + addGestureRecognizer(new ScrollGestureRecognizer([this](PanGestureStatus state) { float contentHeight = this->getContentHeight(); + if (state.deltaOnly) + { + float newScroll = (this->scrollY * contentHeight - (state.delta.y)) / contentHeight; + startScrolling(false, newScroll); + return; + } + static float startY; if (state.state == GestureState::START) startY = this->scrollY * contentHeight; diff --git a/library/meson.build b/library/meson.build index d1c1b6d5..4f5e8234 100644 --- a/library/meson.build +++ b/library/meson.build @@ -24,6 +24,7 @@ borealis_files = files( 'lib/core/gesture.cpp', 'lib/core/touch/tap_gesture.cpp', 'lib/core/touch/pan_gesture.cpp', + 'lib/core/touch/scroll_gesture.cpp', 'lib/platforms/glfw/glfw_platform.cpp', 'lib/platforms/glfw/glfw_video.cpp', @@ -76,4 +77,4 @@ borealis_include = include_directories( ) borealis_dependencies = [ dep_glfw3, dep_glm, ] -borealis_cpp_args = [ '-DYG_ENABLE_EVENTS', '-D__GLFW__', ] +borealis_cpp_args = [ '-DNO_TOUCH_SCROLLING=true', '-DYG_ENABLE_EVENTS', '-D__GLFW__', ] From c8597c472dc4eef54d5b4642fcf1848f38481124 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sun, 28 Mar 2021 22:01:41 +0300 Subject: [PATCH 51/59] Fix blink on mouse scroll --- library/lib/core/touch/tap_gesture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/lib/core/touch/tap_gesture.cpp b/library/lib/core/touch/tap_gesture.cpp index c9ed6dc1..76a973f0 100644 --- a/library/lib/core/touch/tap_gesture.cpp +++ b/library/lib/core/touch/tap_gesture.cpp @@ -83,7 +83,7 @@ TapGestureRecognizer::TapGestureRecognizer(TapGestureEvent::Callback respond) GestureState TapGestureRecognizer::recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) { - if (!enabled) + if (!enabled || touch.phase == TouchPhase::NONE) return GestureState::FAILED; // If not first touch frame and state is From cd02506b30e8f3742b60184b0e9f74d781c392a9 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Mon, 29 Mar 2021 20:53:28 +0300 Subject: [PATCH 52/59] Changed behavior of scrolling in ScrollingFrame --- .../borealis/views/scrolling_frame.hpp | 14 +++-- library/lib/views/recycler.cpp | 2 +- library/lib/views/scrolling_frame.cpp | 58 +++++++++---------- 3 files changed, 40 insertions(+), 34 deletions(-) diff --git a/library/include/borealis/views/scrolling_frame.hpp b/library/include/borealis/views/scrolling_frame.hpp index 5564c4c4..58e9d508 100644 --- a/library/include/borealis/views/scrolling_frame.hpp +++ b/library/include/borealis/views/scrolling_frame.hpp @@ -68,12 +68,18 @@ class ScrollingFrame : public Box */ void setScrollingBehavior(ScrollingBehavior behavior); - float getScroll() const + /** + * The point at which the origin of the content view is offset from the origin of the scroll view. + */ + float getContentOffsetY() const { - return scrollY; + return contentOffsetY; } - void setScroll(float value); + /** + * Sets the offset from the content view’s origin that corresponds to the receiver’s origin. + */ + void setContentOffsetY(float value, bool animated); static View* create(); @@ -86,7 +92,7 @@ class ScrollingFrame : public Box float middleY = 0; // y + height/2 float bottomY = 0; // y + height - Animatable scrollY = 0.0f; // from 0.0f to 1.0f, in % of content view height + Animatable contentOffsetY = 0.0f; void prebakeScrolling(); bool updateScrolling(bool animated); diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 47729d37..c29f6cd5 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -142,7 +142,7 @@ void RecyclerFrame::reloadData() renderedFrame = Rect(); renderedFrame.size.width = getWidth(); - setScroll(0); + setContentOffsetY(0, false); if (dataSource) { diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index a5ddcd4a..aabff3df 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -36,20 +36,21 @@ ScrollingFrame::ScrollingFrame() this->setMaximumAllowedXMLElements(1); addGestureRecognizer(new ScrollGestureRecognizer([this](PanGestureStatus state) { - float contentHeight = this->getContentHeight(); - + if (state.state == GestureState::FAILED) + return; + if (state.deltaOnly) { - float newScroll = (this->scrollY * contentHeight - (state.delta.y)) / contentHeight; + float newScroll = this->contentOffsetY - state.delta.y; startScrolling(false, newScroll); return; } static float startY; if (state.state == GestureState::START) - startY = this->scrollY * contentHeight; + startY = this->contentOffsetY; - float newScroll = (startY - (state.position.y - state.startPosition.y)) / contentHeight; + float newScroll = startY - (state.position.y - state.startPosition.y); // Start animation if (state.state != GestureState::END) @@ -57,11 +58,11 @@ ScrollingFrame::ScrollingFrame() else { float time = state.acceleration.time.y * 1000.0f; - float newPos = this->scrollY * contentHeight + state.acceleration.distance.y; + float newPos = this->contentOffsetY + state.acceleration.distance.y; - newScroll = newPos / contentHeight; + newScroll = newPos; - if (newScroll == this->scrollY || time < 100) + if (newScroll == this->contentOffsetY || time < 100) return; animateScrolling(newScroll, time); @@ -72,7 +73,7 @@ ScrollingFrame::ScrollingFrame() // Stop scrolling on tap addGestureRecognizer(new TapGestureRecognizer([this](brls::TapGestureStatus status, Sound* soundToPlay) { if (status.state == GestureState::UNSURE) - this->scrollY.stop(); + this->contentOffsetY.stop(); })); } @@ -173,7 +174,7 @@ void ScrollingFrame::prebakeScrolling() void ScrollingFrame::startScrolling(bool animated, float newScroll) { - if (newScroll == this->scrollY) + if (newScroll == this->contentOffsetY) return; if (animated) @@ -183,8 +184,8 @@ void ScrollingFrame::startScrolling(bool animated, float newScroll) } else { - this->scrollY.stop(); - this->scrollY = newScroll; + this->contentOffsetY.stop(); + this->contentOffsetY = newScroll; this->scrollAnimationTick(); this->invalidate(); } @@ -192,17 +193,17 @@ void ScrollingFrame::startScrolling(bool animated, float newScroll) void ScrollingFrame::animateScrolling(float newScroll, float time) { - this->scrollY.stop(); + this->contentOffsetY.stop(); - this->scrollY.reset(); + this->contentOffsetY.reset(); - this->scrollY.addStep(newScroll, time, EasingFunction::quadraticOut); + this->contentOffsetY.addStep(newScroll, time, EasingFunction::quadraticOut); - this->scrollY.setTickCallback([this] { + this->contentOffsetY.setTickCallback([this] { this->scrollAnimationTick(); }); - this->scrollY.start(); + this->contentOffsetY.start(); this->invalidate(); } @@ -220,10 +221,9 @@ float ScrollingFrame::getContentHeight() return this->contentView->getHeight(); } -void ScrollingFrame::setScroll(float value) +void ScrollingFrame::setContentOffsetY(float value, bool animated) { - scrollY = value; - scrollAnimationTick(); + startScrolling(animated, value); } void ScrollingFrame::scrollAnimationTick() @@ -231,18 +231,18 @@ void ScrollingFrame::scrollAnimationTick() if (this->contentView) { float contentHeight = this->getContentHeight(); - float bottomLimit = (contentHeight - this->getScrollingAreaHeight()) / contentHeight; + float bottomLimit = contentHeight - this->getScrollingAreaHeight(); - if (this->scrollY < 0) - this->scrollY = 0; + if (this->contentOffsetY < 0) + this->contentOffsetY = 0; - if (this->scrollY > bottomLimit) - this->scrollY = bottomLimit; + if (this->contentOffsetY > bottomLimit) + this->contentOffsetY = bottomLimit; if (contentHeight <= getHeight()) - this->scrollY = 0; + this->contentOffsetY = 0; - this->contentView->setTranslationY(-(this->scrollY * contentHeight)); + this->contentView->setTranslationY(-this->contentOffsetY); } } @@ -271,7 +271,7 @@ bool ScrollingFrame::updateScrolling(bool animated) View* focusedView = Application::getCurrentFocus(); int currentSelectionMiddleOnScreen = focusedView->getY() + focusedView->getHeight() / 2; - float newScroll = -(this->scrollY * contentHeight) - (currentSelectionMiddleOnScreen - this->middleY); + float newScroll = -this->contentOffsetY - (currentSelectionMiddleOnScreen - this->middleY); // Bottom boundary if (this->getScrollingAreaTopBoundary() + newScroll + contentHeight < this->bottomY) @@ -293,7 +293,7 @@ bool ScrollingFrame::updateScrolling(bool animated) Rect ScrollingFrame::getVisibleFrame() { Rect frame = getFrame(); - frame.origin.y += this->scrollY * this->getContentHeight(); + frame.origin.y += this->contentOffsetY; return frame; } From 4f3c590853c368ecba06e40d976b8466dc5673a1 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Mon, 29 Mar 2021 20:59:52 +0300 Subject: [PATCH 53/59] Added sections --- demo/recycling_list_tab.cpp | 21 +++++++---- demo/recycling_list_tab.hpp | 7 ++-- library/include/borealis/views/recycler.hpp | 40 ++++++++++++++++---- library/lib/views/recycler.cpp | 41 +++++++++++++-------- library/lib/views/scrolling_frame.cpp | 3 -- 5 files changed, 76 insertions(+), 36 deletions(-) diff --git a/demo/recycling_list_tab.cpp b/demo/recycling_list_tab.cpp index f7b3fa5c..4ec99864 100644 --- a/demo/recycling_list_tab.cpp +++ b/demo/recycling_list_tab.cpp @@ -28,23 +28,30 @@ RecyclerCell* RecyclerCell::create() // DATA SOURCE -int DataSource::numberOfRows() +int DataSource::numberOfSections(brls::RecyclerFrame* recycler) { - return 30; + return 2; } -brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, int row) +int DataSource::numberOfRows(brls::RecyclerFrame* recycler, int section) +{ + if (section == 0) + return 30; + return 10; +} + +brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, brls::IndexPath index) { RecyclerCell* item = (RecyclerCell*)recycler->dequeueReusableCell("Cell"); - item->label->setText("Item #" + std::to_string(row)); - if (row == 7) + item->label->setText("Item Section: " + std::to_string(index.section) + ", Row: " + std::to_string(index.row)); + if (index.row == 7) item->label->setText("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."); return item; } -void DataSource::didSelectRowAt(int row) +void DataSource::didSelectRowAt(brls::RecyclerFrame* recycler, brls::IndexPath index) { - brls::Logger::info("Cell #" + std::to_string(row) + " selected."); + brls::Logger::info("Item Index(" + std::to_string(index.section) + ":" + std::to_string(index.row) + ") selected."); } // RECYCLER VIEW diff --git a/demo/recycling_list_tab.hpp b/demo/recycling_list_tab.hpp index b97c0134..d0f63429 100644 --- a/demo/recycling_list_tab.hpp +++ b/demo/recycling_list_tab.hpp @@ -34,9 +34,10 @@ class DataSource : public brls::RecyclerDataSource { public: - int numberOfRows() override; - brls::RecyclerCell* cellForRow(brls::RecyclerFrame* recycler, int row) override; - void didSelectRowAt(int row) override; + int numberOfSections(brls::RecyclerFrame* recycler) override; + int numberOfRows(brls::RecyclerFrame* recycler, int section) override; + brls::RecyclerCell* cellForRow(brls::RecyclerFrame* recycler, brls::IndexPath index) override; + void didSelectRowAt(brls::RecyclerFrame* recycler, brls::IndexPath index) override; }; class RecyclingListTab : public brls::Box diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index 2025f91c..9e4917a4 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -27,6 +27,24 @@ namespace brls { +struct IndexPath +{ + int section; + int row; + int item; + + IndexPath() + : IndexPath(0, 0, 0) + { } + + IndexPath(int section, int row, int item) + { + this->section = section; + this->row = row; + this->item = item; + } +}; + class RecyclerCell : public Box { public: @@ -35,12 +53,12 @@ class RecyclerCell : public Box /* * Cell's position inside recycler frame */ - int getIndexPath() const { return indexPath; } + IndexPath getIndexPath() const { return indexPath; } /* * DO NOT USE! FOR INTERNAL USAGE ONLY! */ - void setIndexPath(int value) { indexPath = value; } + void setIndexPath(IndexPath value) { indexPath = value; } /* * A string used to identify a cell that is reusable. @@ -55,7 +73,7 @@ class RecyclerCell : public Box static RecyclerCell* create(); private: - int indexPath; + IndexPath indexPath; }; class RecyclerFrame; @@ -63,25 +81,30 @@ class RecyclerDataSource { public: /* - * Tells the data source to return the number of rows in a table view. + * Asks the data source to return the number of sections in the recycler frame. + */ + virtual int numberOfSections(RecyclerFrame* recycler) { return 1; } + + /* + * Tells the data source to return the number of rows in a recycler frame. */ - virtual int numberOfRows() { return 0; } + virtual int numberOfRows(RecyclerFrame* recycler, int section) { return 0; } /* * Asks the data source for a cell to insert in a particular location of the recycler frame. */ - virtual RecyclerCell* cellForRow(RecyclerFrame* recycler, int row) { return nullptr; } + virtual RecyclerCell* cellForRow(RecyclerFrame* recycler, IndexPath index) { return nullptr; } /* * Asks the data source for the height to use for a row in a specified location. * Return -1 to use autoscaling. */ - virtual float heightForRow(int row) { return -1; } + virtual float heightForRow(RecyclerFrame* recycler, IndexPath index) { return -1; } /* * Tells the data source a row is selected. */ - virtual void didSelectRowAt(int row) { } + virtual void didSelectRowAt(RecyclerFrame* recycler, IndexPath index) { } }; class RecyclerContentBox : public Box @@ -153,6 +176,7 @@ class RecyclerFrame : public ScrollingFrame Box* contentBox; Rect renderedFrame; std::vector cacheFramesData; + std::vector cacheIndexPathData; std::map*> queueMap; std::map> allocationMap; diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index c29f6cd5..2a533785 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -26,7 +26,7 @@ RecyclerCell::RecyclerCell() this->registerClickAction([this](View* view) { RecyclerFrame* recycler = dynamic_cast(getParent()->getParent()); if (recycler) - recycler->getDataSource()->didSelectRowAt(indexPath); + recycler->getDataSource()->didSelectRowAt(recycler, indexPath); return true; }); @@ -148,11 +148,15 @@ void RecyclerFrame::reloadData() { cacheCellFrames(); Rect frame = getFrame(); - for (int i = 0; i < dataSource->numberOfRows(); i++) + int counter = 0; + for (int section = 0; section < dataSource->numberOfSections(this); section++) { - addCellAt(i, true); - if (renderedFrame.getMaxY() > frame.getMaxY()) - break; + for (int row = 0; row < dataSource->numberOfRows(this, section); row++) + { + addCellAt(counter++, true); + if (renderedFrame.getMaxY() > frame.getMaxY()) + break; + } } } } @@ -198,19 +202,25 @@ void RecyclerFrame::queueReusableCell(RecyclerCell* cell) void RecyclerFrame::cacheCellFrames() { cacheFramesData.clear(); + cacheIndexPathData.clear(); Rect frame = getFrame(); Point currentOrigin; if (dataSource) { - for (int i = 0; i < dataSource->numberOfRows(); i++) + for (int section = 0; section < dataSource->numberOfSections(this); section++) { - float height = dataSource->heightForRow(i); - if (height == -1) - height = estimatedRowHeight; - - cacheFramesData.push_back(Size(frame.getWidth(), height)); - currentOrigin.y += height; + for (int row = 0; row < dataSource->numberOfRows(this, section); row++) + { + cacheIndexPathData.push_back(IndexPath(section, row, row)); + + float height = dataSource->heightForRow(this, IndexPath(section, row, row)); + if (height == -1) + height = estimatedRowHeight; + + cacheFramesData.push_back(Size(frame.getWidth(), height)); + currentOrigin.y += height; + } } contentBox->setHeight(currentOrigin.y + paddingTop + paddingBottom); } @@ -293,14 +303,15 @@ void RecyclerFrame::cellsRecyclingLoop() void RecyclerFrame::addCellAt(int index, int downSide) { - RecyclerCell* cell = dataSource->cellForRow(this, index); + IndexPath indexPath = cacheIndexPathData[index]; + RecyclerCell* cell = dataSource->cellForRow(this, indexPath); cell->setWidth(renderedFrame.getWidth() - paddingLeft - paddingRight); Point cellOrigin = Point(renderedFrame.getMinX() + paddingLeft, (downSide ? renderedFrame.getMaxY() : renderedFrame.getMinY() - cell->getHeight()) + paddingTop); cell->setDetachedPosition(cellOrigin.x, cellOrigin.y); - cell->setIndexPath(index); + cell->setIndexPath(indexPath); this->contentBox->getChildren().insert(this->contentBox->getChildren().end(), cell); - + // Allocate and set parent userdata size_t* userdata = (size_t*)malloc(sizeof(size_t)); *userdata = index; diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index aabff3df..06f230a8 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -36,9 +36,6 @@ ScrollingFrame::ScrollingFrame() this->setMaximumAllowedXMLElements(1); addGestureRecognizer(new ScrollGestureRecognizer([this](PanGestureStatus state) { - if (state.state == GestureState::FAILED) - return; - if (state.deltaOnly) { float newScroll = this->contentOffsetY - state.delta.y; From e580b9a49e498ee8a0ad8458559128c82c44b686 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Mon, 29 Mar 2021 21:00:22 +0300 Subject: [PATCH 54/59] Format --fix --- library/include/borealis/core/font.hpp | 2 +- library/include/borealis/core/input.hpp | 2 +- library/include/borealis/views/rectangle.hpp | 2 +- library/include/borealis/views/recycler.hpp | 13 +++++++------ library/lib/views/recycler.cpp | 8 ++++---- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/library/include/borealis/core/font.hpp b/library/include/borealis/core/font.hpp index 4e70c67e..edafba70 100644 --- a/library/include/borealis/core/font.hpp +++ b/library/include/borealis/core/font.hpp @@ -35,7 +35,7 @@ typedef std::unordered_map FontStash; class FontLoader { public: - virtual ~FontLoader() {} + virtual ~FontLoader() { } /** * Called once on init to load every font in the font stash. * diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index f0cc8525..5f4a28e1 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -105,7 +105,7 @@ struct TouchState class InputManager { public: - virtual ~InputManager() {} + virtual ~InputManager() { } /** * Called once every frame to fill the given ControllerState struct with the controller state. diff --git a/library/include/borealis/views/rectangle.hpp b/library/include/borealis/views/rectangle.hpp index 3990a75b..b462353b 100644 --- a/library/include/borealis/views/rectangle.hpp +++ b/library/include/borealis/views/rectangle.hpp @@ -29,7 +29,7 @@ class Rectangle : public View public: Rectangle(NVGcolor color); Rectangle(); - ~Rectangle() {} + ~Rectangle() { } void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index 9e4917a4..c43d8bae 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -32,16 +32,17 @@ struct IndexPath int section; int row; int item; - + IndexPath() : IndexPath(0, 0, 0) - { } - + { + } + IndexPath(int section, int row, int item) { this->section = section; - this->row = row; - this->item = item; + this->row = row; + this->item = item; } }; @@ -84,7 +85,7 @@ class RecyclerDataSource * Asks the data source to return the number of sections in the recycler frame. */ virtual int numberOfSections(RecyclerFrame* recycler) { return 1; } - + /* * Tells the data source to return the number of rows in a recycler frame. */ diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 2a533785..dcc9a842 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -147,7 +147,7 @@ void RecyclerFrame::reloadData() if (dataSource) { cacheCellFrames(); - Rect frame = getFrame(); + Rect frame = getFrame(); int counter = 0; for (int section = 0; section < dataSource->numberOfSections(this); section++) { @@ -213,7 +213,7 @@ void RecyclerFrame::cacheCellFrames() for (int row = 0; row < dataSource->numberOfRows(this, section); row++) { cacheIndexPathData.push_back(IndexPath(section, row, row)); - + float height = dataSource->heightForRow(this, IndexPath(section, row, row)); if (height == -1) height = estimatedRowHeight; @@ -304,14 +304,14 @@ void RecyclerFrame::cellsRecyclingLoop() void RecyclerFrame::addCellAt(int index, int downSide) { IndexPath indexPath = cacheIndexPathData[index]; - RecyclerCell* cell = dataSource->cellForRow(this, indexPath); + RecyclerCell* cell = dataSource->cellForRow(this, indexPath); cell->setWidth(renderedFrame.getWidth() - paddingLeft - paddingRight); Point cellOrigin = Point(renderedFrame.getMinX() + paddingLeft, (downSide ? renderedFrame.getMaxY() : renderedFrame.getMinY() - cell->getHeight()) + paddingTop); cell->setDetachedPosition(cellOrigin.x, cellOrigin.y); cell->setIndexPath(indexPath); this->contentBox->getChildren().insert(this->contentBox->getChildren().end(), cell); - + // Allocate and set parent userdata size_t* userdata = (size_t*)malloc(sizeof(size_t)); *userdata = index; From 9eea7662305b609100a40f9f471e6f9acf15528c Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Mon, 29 Mar 2021 21:02:38 +0300 Subject: [PATCH 55/59] Fix scroll failed state chech --- library/lib/views/scrolling_frame.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index a83e67f1..64bc43c5 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -36,6 +36,9 @@ ScrollingFrame::ScrollingFrame() this->setMaximumAllowedXMLElements(1); addGestureRecognizer(new ScrollGestureRecognizer([this](PanGestureStatus state) { + if (state.state == GestureState::FAILED) + return; + float contentHeight = this->getContentHeight(); if (state.deltaOnly) From 56f375791acbf705d753129ab1625aac3771e5dc Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Thu, 1 Apr 2021 17:44:51 +0300 Subject: [PATCH 56/59] Fix controller navigation in scroll frame --- library/lib/views/scrolling_frame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 3e2d5f9b..7aa4cdaf 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -281,8 +281,8 @@ bool ScrollingFrame::updateScrolling(bool animated) if (newScroll > 0.0f) newScroll = 0.0f; - // Apply 0.0f -> 1.0f scale - newScroll = abs(newScroll) / contentHeight; + // Apply scale + newScroll = abs(newScroll); //Start animation this->startScrolling(animated, newScroll); From cab3ac6aab9cae93d21f0bffbc63dbf9e3ce7259 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sun, 4 Apr 2021 13:05:15 +0300 Subject: [PATCH 57/59] Review fixes --- library/include/borealis/core/audio.hpp | 2 +- library/include/borealis/core/input.hpp | 4 ++-- library/include/borealis/core/touch/pan_gesture.hpp | 2 +- .../include/borealis/core/touch/scroll_gesture.hpp | 4 +++- library/include/borealis/core/touch/tap_gesture.hpp | 2 +- library/include/borealis/core/view.hpp | 4 +--- .../borealis/platforms/switch/switch_input.hpp | 1 - library/lib/core/touch/pan_gesture.cpp | 12 +++++++++++- 8 files changed, 20 insertions(+), 11 deletions(-) diff --git a/library/include/borealis/core/audio.hpp b/library/include/borealis/core/audio.hpp index fb9a10e8..6864755a 100644 --- a/library/include/borealis/core/audio.hpp +++ b/library/include/borealis/core/audio.hpp @@ -31,7 +31,7 @@ enum Sound SOUND_HONK, // honk SOUND_CLICK_SIDEBAR, // played when a sidebar item is clicked SOUND_TOUCH_UNFOCUS, // played when touch focus has been interrupted - SOUND_TOUCH, // played when touch not requires it's own click sound + SOUND_TOUCH, // played when touch doesn't require it's own click sound _SOUND_MAX, // not an actual sound, just used to count of many sounds there are }; diff --git a/library/include/borealis/core/input.hpp b/library/include/borealis/core/input.hpp index f0cc8525..bb895d61 100644 --- a/library/include/borealis/core/input.hpp +++ b/library/include/borealis/core/input.hpp @@ -84,7 +84,7 @@ enum class TouchPhase NONE, }; -// Contains raw touch data +// Contains raw touch data, filled in by platform driver struct RawTouchState { bool pressed; @@ -92,7 +92,7 @@ struct RawTouchState Point scroll; }; -// Contains touch data filled with it's current phase +// Contains touch data automatically filled with current phase by the library struct TouchState { TouchPhase phase; diff --git a/library/include/borealis/core/touch/pan_gesture.hpp b/library/include/borealis/core/touch/pan_gesture.hpp index d3115116..e34e51a1 100644 --- a/library/include/borealis/core/touch/pan_gesture.hpp +++ b/library/include/borealis/core/touch/pan_gesture.hpp @@ -87,4 +87,4 @@ class PanGestureRecognizer : public GestureRecognizer GestureState lastState; }; -}; +} // namespace brls diff --git a/library/include/borealis/core/touch/scroll_gesture.hpp b/library/include/borealis/core/touch/scroll_gesture.hpp index f6f02aa1..4ad010d0 100644 --- a/library/include/borealis/core/touch/scroll_gesture.hpp +++ b/library/include/borealis/core/touch/scroll_gesture.hpp @@ -30,6 +30,8 @@ namespace brls * * If mouse translation used, the only available state is MOVE. * Also PanGestureStatus will be returned with delta values ONLY. + * + * TODO: Reimplement scroll events when mouse input will be separated from touch */ class ScrollGestureRecognizer : public PanGestureRecognizer { @@ -38,4 +40,4 @@ class ScrollGestureRecognizer : public PanGestureRecognizer GestureState recognitionLoop(TouchState touch, View* view, Sound* soundToPlay) override; }; -}; +} // namespace brls diff --git a/library/include/borealis/core/touch/tap_gesture.hpp b/library/include/borealis/core/touch/tap_gesture.hpp index 7e0ac78f..2269e1be 100644 --- a/library/include/borealis/core/touch/tap_gesture.hpp +++ b/library/include/borealis/core/touch/tap_gesture.hpp @@ -77,4 +77,4 @@ class TapGestureRecognizer : public GestureRecognizer GestureState lastState; }; -}; +} // namespace brls diff --git a/library/include/borealis/core/view.hpp b/library/include/borealis/core/view.hpp index 3516e78f..89c10266 100644 --- a/library/include/borealis/core/view.hpp +++ b/library/include/borealis/core/view.hpp @@ -1065,9 +1065,7 @@ class View /** * Called each frame when touch is registered. * - * @return false if there is no recognizer - * that needs to play it's own click sound, - * so play default is allowed + * @returns sound to play invoked by touch recognizers. */ Sound gestureRecognizerRequest(TouchState touch, View* firstResponder); diff --git a/library/include/borealis/platforms/switch/switch_input.hpp b/library/include/borealis/platforms/switch/switch_input.hpp index d3a78bf2..80b51f16 100644 --- a/library/include/borealis/platforms/switch/switch_input.hpp +++ b/library/include/borealis/platforms/switch/switch_input.hpp @@ -36,7 +36,6 @@ class SwitchInputManager : public InputManager private: PadState padState; - TouchState oldTouch; }; } // namespace brls diff --git a/library/lib/core/touch/pan_gesture.cpp b/library/lib/core/touch/pan_gesture.cpp index 254eb3f8..81122c92 100644 --- a/library/lib/core/touch/pan_gesture.cpp +++ b/library/lib/core/touch/pan_gesture.cpp @@ -16,9 +16,19 @@ #include -#define FPS 60.0f // TODO: get real FPS +// TODO: get real FPS +// Uses to calculate time to play acceleration animation +#define FPS 60.0f + +// Delta from touch starting point to current, when +// touch will be recognized as pan movement #define MAX_DELTA_MOVEMENT 10 + +// Touch history limit which uses to calculate current pan speed #define HISTORY_LIMIT 2 + +// Negative acceleration to calculate +// time to play acceleration animation #define PAN_SCROLL_ACCELERATION -5000 namespace brls From 0e50819e3510aad8025d64b63784a8b4fb3c2a7e Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sun, 4 Apr 2021 19:10:18 +0300 Subject: [PATCH 58/59] Added section headers --- demo/recycling_list_tab.cpp | 18 ++-- demo/recycling_list_tab.hpp | 8 +- library/include/borealis/views/recycler.hpp | 37 +++++++- library/lib/views/recycler.cpp | 95 ++++++++++++++++++--- 4 files changed, 137 insertions(+), 21 deletions(-) diff --git a/demo/recycling_list_tab.cpp b/demo/recycling_list_tab.cpp index 4ec99864..b062da1a 100644 --- a/demo/recycling_list_tab.cpp +++ b/demo/recycling_list_tab.cpp @@ -30,21 +30,26 @@ RecyclerCell* RecyclerCell::create() int DataSource::numberOfSections(brls::RecyclerFrame* recycler) { - return 2; + return 3; } int DataSource::numberOfRows(brls::RecyclerFrame* recycler, int section) { - if (section == 0) - return 30; return 10; } + +std::string DataSource::titleForHeader(brls::RecyclerFrame* recycler, int section) +{ + if (section == 0) + return ""; + return "Section #" + std::to_string(section+1); +} -brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, brls::IndexPath index) +brls::RecyclerCell* DataSource::cellForRow(brls::RecyclerFrame* recycler, brls::IndexPath indexPath) { RecyclerCell* item = (RecyclerCell*)recycler->dequeueReusableCell("Cell"); - item->label->setText("Item Section: " + std::to_string(index.section) + ", Row: " + std::to_string(index.row)); - if (index.row == 7) + item->label->setText("Item Section: " + std::to_string(indexPath.section) + ", Row: " + std::to_string(indexPath.row)); + if (indexPath.row == 7) item->label->setText("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."); return item; } @@ -61,6 +66,7 @@ RecyclingListTab::RecyclingListTab() // Inflate the tab from the XML file this->inflateFromXMLRes("xml/tabs/recycling_list.xml"); + recycler->registerCell("Header", []() { return RecyclerHeader::create(); }); recycler->registerCell("Cell", []() { return RecyclerCell::create(); }); recycler->setDataSource(new DataSource()); } diff --git a/demo/recycling_list_tab.hpp b/demo/recycling_list_tab.hpp index d0f63429..4d34f495 100644 --- a/demo/recycling_list_tab.hpp +++ b/demo/recycling_list_tab.hpp @@ -18,6 +18,11 @@ #include +class RecyclerHeader + : public brls::RecyclerHeader +{ +}; + class RecyclerCell : public brls::RecyclerCell { @@ -37,7 +42,8 @@ class DataSource int numberOfSections(brls::RecyclerFrame* recycler) override; int numberOfRows(brls::RecyclerFrame* recycler, int section) override; brls::RecyclerCell* cellForRow(brls::RecyclerFrame* recycler, brls::IndexPath index) override; - void didSelectRowAt(brls::RecyclerFrame* recycler, brls::IndexPath index) override; + void didSelectRowAt(brls::RecyclerFrame* recycler, brls::IndexPath indexPath) override; + std::string titleForHeader(brls::RecyclerFrame* recycler, int section) override; }; class RecyclingListTab : public brls::Box diff --git a/library/include/borealis/views/recycler.hpp b/library/include/borealis/views/recycler.hpp index c43d8bae..34bb21e5 100644 --- a/library/include/borealis/views/recycler.hpp +++ b/library/include/borealis/views/recycler.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,21 @@ class RecyclerCell : public Box IndexPath indexPath; }; +class RecyclerHeader + : public RecyclerCell +{ + public: + RecyclerHeader(); + + void setTitle(std::string text); + void setSubtitle(std::string text); + + static RecyclerHeader* create(); + + private: + Header* header; +}; + class RecyclerFrame; class RecyclerDataSource { @@ -95,12 +111,28 @@ class RecyclerDataSource * Asks the data source for a cell to insert in a particular location of the recycler frame. */ virtual RecyclerCell* cellForRow(RecyclerFrame* recycler, IndexPath index) { return nullptr; } + + /* + * Asks the data source for a cell to display in the header of the specified section of the recycler frame. + */ + virtual RecyclerCell* cellForHeader(RecyclerFrame* recycler, int section); /* * Asks the data source for the height to use for a row in a specified location. * Return -1 to use autoscaling. */ virtual float heightForRow(RecyclerFrame* recycler, IndexPath index) { return -1; } + + /* + * Asks the data source for the height to use for the header of a particular section. + * Return -1 to use autoscaling. + */ + virtual float heightForHeader(RecyclerFrame* recycler, int section); + + /* + * Asks the data source for the title of the header of the specified section of the recycler frame. + */ + virtual std::string titleForHeader(RecyclerFrame* recycler, int section) { return ""; } /* * Tells the data source a row is selected. @@ -111,8 +143,10 @@ class RecyclerDataSource class RecyclerContentBox : public Box { public: - RecyclerContentBox(); + RecyclerContentBox(RecyclerFrame* recycler); View* getNextFocus(FocusDirection direction, View* currentView) override; + private: + RecyclerFrame* recycler; }; // Custom Box for propper recycling navigation @@ -122,6 +156,7 @@ class RecyclerFrame : public ScrollingFrame RecyclerFrame(); ~RecyclerFrame(); + View* getNextCellFocus(FocusDirection direction, View* currentView); void draw(NVGcontext* vg, float x, float y, float width, float height, Style style, FrameContext* ctx) override; void onLayout() override; void setPadding(float padding) override; diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index dcc9a842..8fe498ff 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -23,6 +23,7 @@ namespace brls RecyclerCell::RecyclerCell() { + this->setLineBottom(1); this->registerClickAction([this](View* view) { RecyclerFrame* recycler = dynamic_cast(getParent()->getParent()); if (recycler) @@ -38,17 +39,63 @@ RecyclerCell* RecyclerCell::create() return new RecyclerCell(); } -RecyclerContentBox::RecyclerContentBox() - : Box(Axis::COLUMN) +RecyclerHeader::RecyclerHeader() +{ + this->header = new Header(); + this->addView(header); + header->setGrow(1); + + this->setActionAvailable(ControllerButton::BUTTON_A, false); +} + +void RecyclerHeader::setTitle(std::string title) +{ + this->header->setTitle(title); +} + +void RecyclerHeader::setSubtitle(std::string subtitle) +{ + this->header->setSubtitle(subtitle); +} + +RecyclerHeader* RecyclerHeader::create() +{ + return new RecyclerHeader(); +} + +RecyclerCell* RecyclerDataSource::cellForHeader(RecyclerFrame* recycler, int section) +{ + RecyclerHeader* header = (RecyclerHeader*) recycler->dequeueReusableCell("brls::Header"); + std::string title = this->titleForHeader(recycler, section); + header->setTitle(title); + header->setVisibility(title.empty() ? Visibility::GONE : Visibility::VISIBLE); + header->setHeight(title.empty() ? 0 : View::AUTO); + return header; +} + +float RecyclerDataSource::heightForHeader(RecyclerFrame* recycler, int section) +{ + if (section == 0) + return 0; + return 44; +} + +RecyclerContentBox::RecyclerContentBox(RecyclerFrame* recycler) + : Box(Axis::COLUMN), recycler(recycler) { } View* RecyclerContentBox::getNextFocus(FocusDirection direction, View* currentView) +{ + return this->recycler->getNextCellFocus(direction, currentView); +} + +View* RecyclerFrame::getNextCellFocus(FocusDirection direction, View* currentView) { void* parentUserData = currentView->getParentUserData(); // Return nullptr immediately if focus direction mismatches the box axis (clang-format refuses to split it in multiple lines...) - if ((this->getAxis() == Axis::ROW && direction != FocusDirection::LEFT && direction != FocusDirection::RIGHT) || (this->getAxis() == Axis::COLUMN && direction != FocusDirection::UP && direction != FocusDirection::DOWN)) + if ((this->contentBox->getAxis() == Axis::ROW && direction != FocusDirection::LEFT && direction != FocusDirection::RIGHT) || (this->contentBox->getAxis() == Axis::COLUMN && direction != FocusDirection::UP && direction != FocusDirection::DOWN)) { return nullptr; } @@ -56,7 +103,7 @@ View* RecyclerContentBox::getNextFocus(FocusDirection direction, View* currentVi // Traverse the children size_t offset = 1; // which way we are going in the children list - if ((this->getAxis() == Axis::ROW && direction == FocusDirection::LEFT) || (this->getAxis() == Axis::COLUMN && direction == FocusDirection::UP)) + if ((this->contentBox->getAxis() == Axis::ROW && direction == FocusDirection::LEFT) || (this->contentBox->getAxis() == Axis::COLUMN && direction == FocusDirection::UP)) { offset = -1; } @@ -64,10 +111,17 @@ View* RecyclerContentBox::getNextFocus(FocusDirection direction, View* currentVi size_t currentFocusIndex = *((size_t*)parentUserData) + offset; View* currentFocus = nullptr; - for (auto it : getChildren()) + while (!currentFocus && currentFocusIndex >= 0 && currentFocusIndex < this->cacheIndexPathData.size()) { - if (*((size_t*)it->getParentUserData()) == currentFocusIndex) - currentFocus = it->getDefaultFocus(); + for (auto it : this->contentBox->getChildren()) + { + if (*((size_t*)it->getParentUserData()) == currentFocusIndex) + { + currentFocus = it->getDefaultFocus(); + break; + } + } + currentFocusIndex += offset; } return currentFocus; @@ -75,6 +129,8 @@ View* RecyclerContentBox::getNextFocus(FocusDirection direction, View* currentVi RecyclerFrame::RecyclerFrame() { + registerCell("brls::Header", []() { return RecyclerHeader::create(); }); + // Padding this->registerFloatXMLAttribute("paddingTop", [this](float value) { this->setPaddingTop(value); @@ -99,7 +155,7 @@ RecyclerFrame::RecyclerFrame() this->setScrollingBehavior(ScrollingBehavior::CENTERED); // Create content box - this->contentBox = new RecyclerContentBox(); + this->contentBox = new RecyclerContentBox(this); this->setContentView(this->contentBox); } @@ -151,7 +207,7 @@ void RecyclerFrame::reloadData() int counter = 0; for (int section = 0; section < dataSource->numberOfSections(this); section++) { - for (int row = 0; row < dataSource->numberOfRows(this, section); row++) + for (int row = -1; row < dataSource->numberOfRows(this, section); row++) { addCellAt(counter++, true); if (renderedFrame.getMaxY() > frame.getMaxY()) @@ -210,11 +266,14 @@ void RecyclerFrame::cacheCellFrames() { for (int section = 0; section < dataSource->numberOfSections(this); section++) { - for (int row = 0; row < dataSource->numberOfRows(this, section); row++) + for (int row = -1; row < dataSource->numberOfRows(this, section); row++) { cacheIndexPathData.push_back(IndexPath(section, row, row)); - float height = dataSource->heightForRow(this, IndexPath(section, row, row)); + float height = row == -1 ? + dataSource->heightForHeader(this, section) : + dataSource->heightForRow(this, IndexPath(section, row, row)); + if (height == -1) height = estimatedRowHeight; @@ -304,9 +363,19 @@ void RecyclerFrame::cellsRecyclingLoop() void RecyclerFrame::addCellAt(int index, int downSide) { IndexPath indexPath = cacheIndexPathData[index]; - RecyclerCell* cell = dataSource->cellForRow(this, indexPath); + + RecyclerCell* cell; + if (indexPath.row == -1) + cell = dataSource->cellForHeader(this, indexPath.section); + else + cell = dataSource->cellForRow(this, indexPath); + cell->setWidth(renderedFrame.getWidth() - paddingLeft - paddingRight); - Point cellOrigin = Point(renderedFrame.getMinX() + paddingLeft, (downSide ? renderedFrame.getMaxY() : renderedFrame.getMinY() - cell->getHeight()) + paddingTop); + Point cellOrigin = Point(renderedFrame.getMinX() + paddingLeft, + (downSide ? + renderedFrame.getMaxY() : + renderedFrame.getMinY() - cell->getHeight()) + paddingTop); + cell->setDetachedPosition(cellOrigin.x, cellOrigin.y); cell->setIndexPath(indexPath); From 94a85e71039eacb196214857b45e2a285c7e78f9 Mon Sep 17 00:00:00 2001 From: Daniil Vinogradov Date: Sun, 9 May 2021 14:32:12 +0300 Subject: [PATCH 59/59] fixed view's freeing --- library/include/borealis/core/box.hpp | 2 +- library/include/borealis/views/scrolling_frame.hpp | 2 +- library/lib/core/box.cpp | 6 ++++-- library/lib/views/recycler.cpp | 6 +++--- library/lib/views/scrolling_frame.cpp | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/library/include/borealis/core/box.hpp b/library/include/borealis/core/box.hpp index 16bbbe6b..da99d796 100644 --- a/library/include/borealis/core/box.hpp +++ b/library/include/borealis/core/box.hpp @@ -93,7 +93,7 @@ class Box : public View /** * Removes the given view from the Box. It will be freed. */ - virtual void removeView(View* view); + virtual void removeView(View* view, bool free = true); /** * Sets the padding of the view, aka the internal space to give diff --git a/library/include/borealis/views/scrolling_frame.hpp b/library/include/borealis/views/scrolling_frame.hpp index 58e9d508..2aa1b42a 100644 --- a/library/include/borealis/views/scrolling_frame.hpp +++ b/library/include/borealis/views/scrolling_frame.hpp @@ -46,7 +46,7 @@ class ScrollingFrame : public Box void onChildFocusLost(View* directChild, View* focusedView) override; void willAppear(bool resetState) override; void addView(View* view) override; - void removeView(View* view) override; + void removeView(View* view, bool free = true) override; void onLayout() override; void setPadding(float top, float right, float bottom, float left) override; void setPaddingTop(float top) override; diff --git a/library/lib/core/box.cpp b/library/lib/core/box.cpp index 84cff69b..d0448478 100644 --- a/library/lib/core/box.cpp +++ b/library/lib/core/box.cpp @@ -184,7 +184,7 @@ void Box::addView(View* view, size_t position) view->willAppear(); } -void Box::removeView(View* view) +void Box::removeView(View* view, bool free) { if (!view) return; @@ -214,7 +214,9 @@ void Box::removeView(View* view) this->children.erase(this->children.begin() + index); view->willDisappear(true); - // delete view; + + if (free) + delete view; this->invalidate(); } diff --git a/library/lib/views/recycler.cpp b/library/lib/views/recycler.cpp index 8fe498ff..c432e390 100644 --- a/library/lib/views/recycler.cpp +++ b/library/lib/views/recycler.cpp @@ -189,7 +189,7 @@ void RecyclerFrame::reloadData() for (auto const& child : children) { queueReusableCell((RecyclerCell*)child); - this->contentBox->removeView(child); + this->contentBox->removeView(child, false); } visibleMin = UINT_MAX; @@ -319,7 +319,7 @@ void RecyclerFrame::cellsRecyclingLoop() renderedFrame.size.height -= cellHeight; queueReusableCell(minCell); - this->contentBox->removeView(minCell); + this->contentBox->removeView(minCell, false); Logger::debug("Cell #" + std::to_string(visibleMin) + " - destroyed"); @@ -340,7 +340,7 @@ void RecyclerFrame::cellsRecyclingLoop() renderedFrame.size.height -= cellHeight; queueReusableCell(maxCell); - this->contentBox->removeView(maxCell); + this->contentBox->removeView(maxCell, false); Logger::debug("Cell #" + std::to_string(visibleMax) + " - destroyed"); diff --git a/library/lib/views/scrolling_frame.cpp b/library/lib/views/scrolling_frame.cpp index 7aa4cdaf..55deea12 100644 --- a/library/lib/views/scrolling_frame.cpp +++ b/library/lib/views/scrolling_frame.cpp @@ -101,7 +101,7 @@ void ScrollingFrame::addView(View* view) this->setContentView(view); } -void ScrollingFrame::removeView(View* view) +void ScrollingFrame::removeView(View* view, bool free) { this->setContentView(nullptr); }