From 271f79ea6306c70ed1b93086e5f4b8a9f5a3e2be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 8 Mar 2020 14:49:29 +0100 Subject: [PATCH] UI: Break out the hierarchy functions into ui/root.cpp/h --- CMakeLists.txt | 2 + UI/EmuScreen.cpp | 1 + UWP/NativeUWP/NativeUWP.vcxproj | 2 + UWP/NativeUWP/NativeUWP.vcxproj.filters | 6 + ext/native/Android.mk | 1 + ext/native/native.vcxproj | 2 + ext/native/native.vcxproj.filters | 6 + ext/native/ui/CMakeLists.txt | 5 +- ext/native/ui/root.cpp | 202 ++++++++++++++++++++++++ ext/native/ui/root.h | 17 ++ ext/native/ui/ui_screen.cpp | 1 + ext/native/ui/viewgroup.cpp | 193 ---------------------- ext/native/ui/viewgroup.h | 7 - libretro/Makefile.common | 1 + 14 files changed, 244 insertions(+), 202 deletions(-) create mode 100644 ext/native/ui/root.cpp create mode 100644 ext/native/ui/root.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ed6f186c6..4f91a430b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1040,6 +1040,8 @@ add_library(native STATIC ext/native/thread/threadutil.h ext/native/thread/threadpool.cpp ext/native/thread/threadpool.h + ext/native/ui/root.cpp + ext/native/ui/root.h ext/native/ui/screen.cpp ext/native/ui/screen.h ext/native/ui/ui.cpp diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 95664c0d6..2708cd32f 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -30,6 +30,7 @@ #include "input/input_state.h" #include "math/curves.h" +#include "ui/root.h" #include "ui/ui.h" #include "ui/ui_context.h" #include "ui/ui_tween.h" diff --git a/UWP/NativeUWP/NativeUWP.vcxproj b/UWP/NativeUWP/NativeUWP.vcxproj index be3e378ef..f6adacf85 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj +++ b/UWP/NativeUWP/NativeUWP.vcxproj @@ -455,6 +455,7 @@ + @@ -1608,6 +1609,7 @@ + diff --git a/UWP/NativeUWP/NativeUWP.vcxproj.filters b/UWP/NativeUWP/NativeUWP.vcxproj.filters index 65587a1c3..5e8aaecb8 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj.filters +++ b/UWP/NativeUWP/NativeUWP.vcxproj.filters @@ -145,6 +145,9 @@ input + + ui + ui @@ -583,6 +586,9 @@ input + + ui + ui diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 09b69d441..d10c21491 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -89,6 +89,7 @@ LOCAL_SRC_FILES :=\ thin3d/VulkanRenderManager.cpp \ thin3d/VulkanQueueRunner.cpp \ thin3d/DataFormatGL.cpp \ + ui/root.cpp \ ui/view.cpp \ ui/viewgroup.cpp \ ui/ui.cpp \ diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index f28e6ca22..06c297979 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -438,6 +438,7 @@ + @@ -1147,6 +1148,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index d466841ef..82e64872a 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -347,6 +347,9 @@ thin3d + + ui + @@ -826,6 +829,9 @@ thin3d + + ui + diff --git a/ext/native/ui/CMakeLists.txt b/ext/native/ui/CMakeLists.txt index 40530c53b..016db5350 100644 --- a/ext/native/ui/CMakeLists.txt +++ b/ext/native/ui/CMakeLists.txt @@ -1,9 +1,10 @@ set(SRCS ui.cpp ui_context.cpp - view.cpp - viewgroup.cpp + view.cpp + viewgroup.cpp screen.cpp + root.cpp ) set(SRCS ${SRCS}) diff --git a/ext/native/ui/root.cpp b/ext/native/ui/root.cpp new file mode 100644 index 000000000..dd2b610a9 --- /dev/null +++ b/ext/native/ui/root.cpp @@ -0,0 +1,202 @@ +#include + +#include "base/timeutil.h" +#include "ui/root.h" +#include "ui/viewgroup.h" + +namespace UI { + +static std::mutex focusLock; +static std::vector focusMoves; +extern bool focusForced; + +void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root) { + if (!root) { + ELOG("Tried to layout a view hierarchy from a zero pointer root"); + return; + } + const Bounds &rootBounds = dc.GetBounds(); + + MeasureSpec horiz(EXACTLY, rootBounds.w); + MeasureSpec vert(EXACTLY, rootBounds.h); + + // Two phases - measure contents, layout. + root->Measure(dc, horiz, vert); + // Root has a specified size. Set it, then let root layout all its children. + root->SetBounds(rootBounds); + root->Layout(); +} + +void MoveFocus(ViewGroup *root, FocusDirection direction) { + if (!GetFocusedView()) { + // Nothing was focused when we got in here. Focus the first non-group in the hierarchy. + root->SetFocus(); + return; + } + + NeighborResult neigh(0, 0); + neigh = root->FindNeighbor(GetFocusedView(), direction, neigh); + + if (neigh.view) { + neigh.view->SetFocus(); + root->SubviewFocused(neigh.view); + } +} + +// TODO: Figure out where this should really live. +// Simple simulation of key repeat on platforms and for gamepads where we don't +// automatically get it. + +static int frameCount; + +// Ignore deviceId when checking for matches. Turns out that Ouya for example sends +// completely broken input where the original keypresses have deviceId = 10 and the repeats +// have deviceId = 0. +struct HeldKey { + int key; + int deviceId; + double triggerTime; + + // Ignores startTime + bool operator <(const HeldKey &other) const { + if (key < other.key) return true; + return false; + } + bool operator ==(const HeldKey &other) const { return key == other.key; } +}; + +static std::set heldKeys; + +const double repeatDelay = 15 * (1.0 / 60.0f); // 15 frames like before. +const double repeatInterval = 5 * (1.0 / 60.0f); // 5 frames like before. + +bool KeyEvent(const KeyInput &key, ViewGroup *root) { + bool retval = false; + // Ignore repeats for focus moves. + if ((key.flags & (KEY_DOWN | KEY_IS_REPEAT)) == KEY_DOWN) { + if (IsDPadKey(key)) { + // Let's only repeat DPAD initially. + HeldKey hk; + hk.key = key.keyCode; + hk.deviceId = key.deviceId; + hk.triggerTime = time_now_d() + repeatDelay; + + // Check if the key is already held. If it is, ignore it. This is to avoid + // multiple key repeat mechanisms colliding. + if (heldKeys.find(hk) != heldKeys.end()) { + return false; + } + + heldKeys.insert(hk); + std::lock_guard lock(focusLock); + focusMoves.push_back(key.keyCode); + retval = true; + } + } + if (key.flags & KEY_UP) { + // We ignore the device ID here (in the comparator for HeldKey), due to the Ouya quirk mentioned above. + if (!heldKeys.empty()) { + HeldKey hk; + hk.key = key.keyCode; + hk.deviceId = key.deviceId; + hk.triggerTime = 0.0; // irrelevant + if (heldKeys.find(hk) != heldKeys.end()) { + heldKeys.erase(hk); + retval = true; + } + } + } + + retval = root->Key(key); + + // Ignore volume keys and stuff here. Not elegant but need to propagate bools through the view hierarchy as well... + switch (key.keyCode) { + case NKCODE_VOLUME_DOWN: + case NKCODE_VOLUME_UP: + case NKCODE_VOLUME_MUTE: + retval = false; + break; + } + + return retval; +} + +static void ProcessHeldKeys(ViewGroup *root) { + double now = time_now_d(); + +restart: + + for (std::set::iterator iter = heldKeys.begin(); iter != heldKeys.end(); ++iter) { + if (iter->triggerTime < now) { + KeyInput key; + key.keyCode = iter->key; + key.deviceId = iter->deviceId; + key.flags = KEY_DOWN; + KeyEvent(key, root); + + std::lock_guard lock(focusLock); + focusMoves.push_back(key.keyCode); + + // Cannot modify the current item when looping over a set, so let's do this instead. + HeldKey hk = *iter; + heldKeys.erase(hk); + hk.triggerTime = now + repeatInterval; + heldKeys.insert(hk); + goto restart; + } + } +} + +bool TouchEvent(const TouchInput &touch, ViewGroup *root) { + focusForced = false; + root->Touch(touch); + if ((touch.flags & TOUCH_DOWN) && !focusForced) { + EnableFocusMovement(false); + } + return true; +} + +bool AxisEvent(const AxisInput &axis, ViewGroup *root) { + root->Axis(axis); + return true; +} + +void UpdateViewHierarchy(ViewGroup *root) { + ProcessHeldKeys(root); + frameCount++; + + if (!root) { + ELOG("Tried to update a view hierarchy from a zero pointer root"); + return; + } + + if (focusMoves.size()) { + std::lock_guard lock(focusLock); + EnableFocusMovement(true); + if (!GetFocusedView()) { + View *defaultView = root->GetDefaultFocusView(); + // Can't focus what you can't see. + if (defaultView && defaultView->GetVisibility() == V_VISIBLE) { + root->GetDefaultFocusView()->SetFocus(); + } else { + root->SetFocus(); + } + root->SubviewFocused(GetFocusedView()); + } else { + for (size_t i = 0; i < focusMoves.size(); i++) { + switch (focusMoves[i]) { + case NKCODE_DPAD_LEFT: MoveFocus(root, FOCUS_LEFT); break; + case NKCODE_DPAD_RIGHT: MoveFocus(root, FOCUS_RIGHT); break; + case NKCODE_DPAD_UP: MoveFocus(root, FOCUS_UP); break; + case NKCODE_DPAD_DOWN: MoveFocus(root, FOCUS_DOWN); break; + } + } + } + focusMoves.clear(); + } + + root->Update(); + DispatchEvents(); +} + +} diff --git a/ext/native/ui/root.h b/ext/native/ui/root.h new file mode 100644 index 000000000..69730aa8e --- /dev/null +++ b/ext/native/ui/root.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ui/ui_context.h" +#include "input/input_state.h" + +namespace UI { + +class ViewGroup; + +void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root); +void UpdateViewHierarchy(ViewGroup *root); +// Hooks arrow keys for navigation +bool KeyEvent(const KeyInput &key, ViewGroup *root); +bool TouchEvent(const TouchInput &touch, ViewGroup *root); +bool AxisEvent(const AxisInput &axis, ViewGroup *root); + +} diff --git a/ext/native/ui/ui_screen.cpp b/ext/native/ui/ui_screen.cpp index 9ed9aa2b3..c69ec761a 100644 --- a/ext/native/ui/ui_screen.cpp +++ b/ext/native/ui/ui_screen.cpp @@ -7,6 +7,7 @@ #include "ui/ui_screen.h" #include "ui/ui_context.h" #include "ui/screen.h" +#include "ui/root.h" #include "i18n/i18n.h" #include "gfx_es2/draw_buffer.h" diff --git a/ext/native/ui/viewgroup.cpp b/ext/native/ui/viewgroup.cpp index 244f852fd..d0961078e 100644 --- a/ext/native/ui/viewgroup.cpp +++ b/ext/native/ui/viewgroup.cpp @@ -19,10 +19,6 @@ namespace UI { const float ITEM_HEIGHT = 64.f; -static std::mutex focusLock; -static std::vector focusMoves; -extern bool focusForced; - void ApplyGravity(const Bounds outer, const Margins &margins, float w, float h, int gravity, Bounds &inner) { inner.w = w; inner.h = h; @@ -375,22 +371,6 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei } } -void MoveFocus(ViewGroup *root, FocusDirection direction) { - if (!GetFocusedView()) { - // Nothing was focused when we got in here. Focus the first non-group in the hierarchy. - root->SetFocus(); - return; - } - - NeighborResult neigh(0, 0); - neigh = root->FindNeighbor(GetFocusedView(), direction, neigh); - - if (neigh.view) { - neigh.view->SetFocus(); - root->SubviewFocused(neigh.view); - } -} - // TODO: This code needs some cleanup/restructuring... void LinearLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) { MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_); @@ -1421,177 +1401,4 @@ bool StringVectorListAdaptor::AddEventCallback(View *view, std::functionMeasure(dc, horiz, vert); - // Root has a specified size. Set it, then let root layout all its children. - root->SetBounds(rootBounds); - root->Layout(); -} - -// TODO: Figure out where this should really live. -// Simple simulation of key repeat on platforms and for gamepads where we don't -// automatically get it. - -static int frameCount; - -// Ignore deviceId when checking for matches. Turns out that Ouya for example sends -// completely broken input where the original keypresses have deviceId = 10 and the repeats -// have deviceId = 0. -struct HeldKey { - int key; - int deviceId; - double triggerTime; - - // Ignores startTime - bool operator <(const HeldKey &other) const { - if (key < other.key) return true; - return false; - } - bool operator ==(const HeldKey &other) const { return key == other.key; } -}; - -static std::set heldKeys; - -const double repeatDelay = 15 * (1.0 / 60.0f); // 15 frames like before. -const double repeatInterval = 5 * (1.0 / 60.0f); // 5 frames like before. - -bool KeyEvent(const KeyInput &key, ViewGroup *root) { - bool retval = false; - // Ignore repeats for focus moves. - if ((key.flags & (KEY_DOWN | KEY_IS_REPEAT)) == KEY_DOWN) { - if (IsDPadKey(key)) { - // Let's only repeat DPAD initially. - HeldKey hk; - hk.key = key.keyCode; - hk.deviceId = key.deviceId; - hk.triggerTime = time_now_d() + repeatDelay; - - // Check if the key is already held. If it is, ignore it. This is to avoid - // multiple key repeat mechanisms colliding. - if (heldKeys.find(hk) != heldKeys.end()) { - return false; - } - - heldKeys.insert(hk); - std::lock_guard lock(focusLock); - focusMoves.push_back(key.keyCode); - retval = true; - } - } - if (key.flags & KEY_UP) { - // We ignore the device ID here (in the comparator for HeldKey), due to the Ouya quirk mentioned above. - if (!heldKeys.empty()) { - HeldKey hk; - hk.key = key.keyCode; - hk.deviceId = key.deviceId; - hk.triggerTime = 0.0; // irrelevant - if (heldKeys.find(hk) != heldKeys.end()) { - heldKeys.erase(hk); - retval = true; - } - } - } - - retval = root->Key(key); - - // Ignore volume keys and stuff here. Not elegant but need to propagate bools through the view hierarchy as well... - switch (key.keyCode) { - case NKCODE_VOLUME_DOWN: - case NKCODE_VOLUME_UP: - case NKCODE_VOLUME_MUTE: - retval = false; - break; - } - - return retval; -} - -static void ProcessHeldKeys(ViewGroup *root) { - double now = time_now_d(); - -restart: - - for (std::set::iterator iter = heldKeys.begin(); iter != heldKeys.end(); ++iter) { - if (iter->triggerTime < now) { - KeyInput key; - key.keyCode = iter->key; - key.deviceId = iter->deviceId; - key.flags = KEY_DOWN; - KeyEvent(key, root); - - std::lock_guard lock(focusLock); - focusMoves.push_back(key.keyCode); - - // Cannot modify the current item when looping over a set, so let's do this instead. - HeldKey hk = *iter; - heldKeys.erase(hk); - hk.triggerTime = now + repeatInterval; - heldKeys.insert(hk); - goto restart; - } - } -} - -bool TouchEvent(const TouchInput &touch, ViewGroup *root) { - focusForced = false; - root->Touch(touch); - if ((touch.flags & TOUCH_DOWN) && !focusForced) { - EnableFocusMovement(false); - } - return true; -} - -bool AxisEvent(const AxisInput &axis, ViewGroup *root) { - root->Axis(axis); - return true; -} - -void UpdateViewHierarchy(ViewGroup *root) { - ProcessHeldKeys(root); - frameCount++; - - if (!root) { - ELOG("Tried to update a view hierarchy from a zero pointer root"); - return; - } - - if (focusMoves.size()) { - std::lock_guard lock(focusLock); - EnableFocusMovement(true); - if (!GetFocusedView()) { - View *defaultView = root->GetDefaultFocusView(); - // Can't focus what you can't see. - if (defaultView && defaultView->GetVisibility() == V_VISIBLE) { - root->GetDefaultFocusView()->SetFocus(); - } else { - root->SetFocus(); - } - root->SubviewFocused(GetFocusedView()); - } else { - for (size_t i = 0; i < focusMoves.size(); i++) { - switch (focusMoves[i]) { - case NKCODE_DPAD_LEFT: MoveFocus(root, FOCUS_LEFT); break; - case NKCODE_DPAD_RIGHT: MoveFocus(root, FOCUS_RIGHT); break; - case NKCODE_DPAD_UP: MoveFocus(root, FOCUS_UP); break; - case NKCODE_DPAD_DOWN: MoveFocus(root, FOCUS_DOWN); break; - } - } - } - focusMoves.clear(); - } - - root->Update(); - DispatchEvents(); -} - } // namespace UI diff --git a/ext/native/ui/viewgroup.h b/ext/native/ui/viewgroup.h index fb693c33a..28d788e5f 100644 --- a/ext/native/ui/viewgroup.h +++ b/ext/native/ui/viewgroup.h @@ -397,11 +397,4 @@ private: std::set hidden_; }; -void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root); -void UpdateViewHierarchy(ViewGroup *root); -// Hooks arrow keys for navigation -bool KeyEvent(const KeyInput &key, ViewGroup *root); -bool TouchEvent(const TouchInput &touch, ViewGroup *root); -bool AxisEvent(const AxisInput &axis, ViewGroup *root); - } // namespace UI diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 89f7880c1..b176364e8 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -305,6 +305,7 @@ SOURCES_CXX += $(NATIVEDIR)/math/dataconv.cpp \ $(NATIVEDIR)/thin3d/DataFormatGL.cpp \ $(NATIVEDIR)/thread/threadutil.cpp \ $(NATIVEDIR)/thread/threadpool.cpp \ + $(NATIVEDIR)/ui/root.cpp \ $(NATIVEDIR)/ui/screen.cpp \ $(NATIVEDIR)/ui/ui.cpp \ $(NATIVEDIR)/ui/ui_context.cpp \