2023-01-14 16:33:00 -08:00
|
|
|
#include <algorithm>
|
2023-12-05 23:14:41 +01:00
|
|
|
|
|
|
|
|
#include "Common/Log.h"
|
2020-10-04 10:10:55 +02:00
|
|
|
#include "Common/System/Display.h"
|
2023-07-20 09:56:51 +02:00
|
|
|
#include "Common/System/System.h"
|
2023-12-05 23:14:41 +01:00
|
|
|
#include "Common/System/Request.h"
|
2020-10-01 09:36:43 +02:00
|
|
|
#include "Common/Input/InputState.h"
|
|
|
|
|
#include "Common/Input/KeyCodes.h"
|
2020-10-04 00:25:21 +02:00
|
|
|
#include "Common/Math/curves.h"
|
2020-10-04 20:48:47 +02:00
|
|
|
#include "Common/UI/UIScreen.h"
|
|
|
|
|
#include "Common/UI/Context.h"
|
|
|
|
|
#include "Common/UI/Screen.h"
|
|
|
|
|
#include "Common/UI/Root.h"
|
2020-10-01 13:05:04 +02:00
|
|
|
#include "Common/Data/Text/I18n.h"
|
2020-10-04 23:24:14 +02:00
|
|
|
#include "Common/Render/DrawBuffer.h"
|
2020-08-15 16:13:24 +02:00
|
|
|
|
2015-10-31 13:43:33 +01:00
|
|
|
static const bool ClickDebug = false;
|
|
|
|
|
|
2013-06-08 22:41:17 +02:00
|
|
|
UIScreen::UIScreen()
|
2020-03-23 07:58:24 -07:00
|
|
|
: Screen() {
|
2022-12-08 16:04:20 +01:00
|
|
|
lastVertical_ = UseVerticalLayout();
|
2013-06-08 22:41:17 +02:00
|
|
|
}
|
|
|
|
|
|
2013-11-26 13:56:56 +01:00
|
|
|
UIScreen::~UIScreen() {
|
|
|
|
|
delete root_;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-08 16:04:20 +01:00
|
|
|
bool UIScreen::UseVerticalLayout() const {
|
2023-02-25 13:09:44 +01:00
|
|
|
return g_display.dp_yres > g_display.dp_xres * 1.1f;
|
2022-12-08 16:04:20 +01:00
|
|
|
}
|
|
|
|
|
|
2013-07-21 13:30:47 +02:00
|
|
|
void UIScreen::DoRecreateViews() {
|
2024-10-25 21:15:48 +02:00
|
|
|
if (!recreateViews_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-02-21 16:45:31 -08:00
|
|
|
|
2024-10-25 21:15:48 +02:00
|
|
|
std::lock_guard<std::recursive_mutex> guard(screenManager()->inputLock_);
|
2016-01-22 22:40:16 -08:00
|
|
|
|
2024-10-25 21:15:48 +02:00
|
|
|
UI::PersistMap persisted;
|
|
|
|
|
bool persisting = root_ != nullptr;
|
|
|
|
|
if (persisting) {
|
|
|
|
|
root_->PersistData(UI::PERSIST_SAVE, "root", persisted);
|
|
|
|
|
}
|
2016-01-22 22:40:16 -08:00
|
|
|
|
2024-10-25 21:15:48 +02:00
|
|
|
delete root_;
|
|
|
|
|
root_ = nullptr;
|
|
|
|
|
CreateViews();
|
|
|
|
|
UI::View *defaultView = root_ ? root_->GetDefaultFocusView() : nullptr;
|
|
|
|
|
if (defaultView && defaultView->GetVisibility() == UI::V_VISIBLE) {
|
|
|
|
|
defaultView->SetFocus();
|
|
|
|
|
}
|
|
|
|
|
recreateViews_ = false;
|
2016-01-23 02:23:04 -08:00
|
|
|
|
2024-10-25 21:15:48 +02:00
|
|
|
if (persisting && root_ != nullptr) {
|
|
|
|
|
root_->PersistData(UI::PERSIST_RESTORE, "root", persisted);
|
|
|
|
|
|
|
|
|
|
// Update layout and refocus so things scroll into view.
|
|
|
|
|
// This is for resizing down, when focused on something now offscreen.
|
|
|
|
|
UI::LayoutViewHierarchy(*screenManager()->getUIContext(), root_, ignoreInsets_);
|
|
|
|
|
UI::View *focused = UI::GetFocusedView();
|
|
|
|
|
if (focused) {
|
|
|
|
|
root_->SubviewFocused(focused);
|
2016-01-22 22:40:16 -08:00
|
|
|
}
|
2013-06-03 19:57:40 +02:00
|
|
|
}
|
2013-07-21 13:30:47 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-01 14:23:47 +02:00
|
|
|
void UIScreen::touch(const TouchInput &touch) {
|
2023-05-27 00:31:10 +02:00
|
|
|
if (!ignoreInput_ && root_) {
|
2023-05-26 11:49:50 +02:00
|
|
|
UI::TouchEvent(touch, root_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UIScreen::axis(const AxisInput &axis) {
|
2023-05-27 00:31:10 +02:00
|
|
|
if (!ignoreInput_ && root_) {
|
2023-05-26 11:49:50 +02:00
|
|
|
UI::AxisEvent(axis, root_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UIScreen::key(const KeyInput &key) {
|
2023-05-27 00:31:10 +02:00
|
|
|
if (!ignoreInput_ && root_) {
|
2023-07-14 12:01:38 +02:00
|
|
|
return UI::KeyEvent(key, root_);
|
2023-05-26 11:49:50 +02:00
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-04 10:58:11 +02:00
|
|
|
bool UIScreen::UnsyncTouch(const TouchInput &touch) {
|
2023-05-27 00:31:10 +02:00
|
|
|
if (ClickDebug && root_ && (touch.flags & TOUCH_DOWN)) {
|
2024-07-14 14:42:59 +02:00
|
|
|
INFO_LOG(Log::System, "Touch down!");
|
2023-05-27 00:31:10 +02:00
|
|
|
std::vector<UI::View *> views;
|
|
|
|
|
root_->Query(touch.x, touch.y, views);
|
|
|
|
|
for (auto view : views) {
|
2024-07-14 14:42:59 +02:00
|
|
|
INFO_LOG(Log::System, "%s", view->DescribeLog().c_str());
|
2023-05-01 14:23:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QueuedEvent ev{};
|
|
|
|
|
ev.type = QueuedEventType::TOUCH;
|
|
|
|
|
ev.touch = touch;
|
2023-09-11 17:37:31 +02:00
|
|
|
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
2023-05-01 14:23:47 +02:00
|
|
|
eventQueue_.push_back(ev);
|
2023-09-04 10:58:11 +02:00
|
|
|
return false;
|
2023-05-01 14:23:47 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-27 17:34:34 +02:00
|
|
|
void UIScreen::UnsyncAxis(const AxisInput *axes, size_t count) {
|
2023-05-01 14:23:47 +02:00
|
|
|
QueuedEvent ev{};
|
|
|
|
|
ev.type = QueuedEventType::AXIS;
|
2023-09-11 17:37:31 +02:00
|
|
|
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
2023-09-27 17:34:34 +02:00
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
|
ev.axis = axes[i];
|
|
|
|
|
eventQueue_.push_back(ev);
|
|
|
|
|
}
|
2023-05-01 14:23:47 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-26 11:49:50 +02:00
|
|
|
bool UIScreen::UnsyncKey(const KeyInput &key) {
|
|
|
|
|
bool retval = false;
|
|
|
|
|
if (root_) {
|
2023-05-01 14:23:47 +02:00
|
|
|
// TODO: Make key events async too. The return value is troublesome, though.
|
2023-05-26 11:49:50 +02:00
|
|
|
switch (UI::UnsyncKeyEvent(key, root_)) {
|
|
|
|
|
case UI::KeyEventResult::ACCEPT:
|
|
|
|
|
retval = true;
|
|
|
|
|
break;
|
|
|
|
|
case UI::KeyEventResult::PASS_THROUGH:
|
|
|
|
|
retval = false;
|
|
|
|
|
break;
|
|
|
|
|
case UI::KeyEventResult::IGNORE_KEY:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-05-01 14:23:47 +02:00
|
|
|
}
|
2023-05-26 11:49:50 +02:00
|
|
|
|
|
|
|
|
QueuedEvent ev{};
|
|
|
|
|
ev.type = QueuedEventType::KEY;
|
|
|
|
|
ev.key = key;
|
2023-09-11 17:37:31 +02:00
|
|
|
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
2023-05-26 11:49:50 +02:00
|
|
|
eventQueue_.push_back(ev);
|
2023-06-13 00:47:48 +02:00
|
|
|
return retval;
|
2023-05-01 14:23:47 +02:00
|
|
|
}
|
|
|
|
|
|
2017-03-14 22:01:18 -07:00
|
|
|
void UIScreen::update() {
|
2022-12-08 16:04:20 +01:00
|
|
|
bool vertical = UseVerticalLayout();
|
|
|
|
|
if (vertical != lastVertical_) {
|
|
|
|
|
RecreateViews();
|
|
|
|
|
lastVertical_ = vertical;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-21 13:30:47 +02:00
|
|
|
DoRecreateViews();
|
2013-06-03 19:57:40 +02:00
|
|
|
|
2013-07-20 12:04:24 +02:00
|
|
|
if (root_) {
|
2017-03-14 22:01:18 -07:00
|
|
|
UpdateViewHierarchy(root_);
|
2023-05-27 00:31:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
QueuedEvent ev{};
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> guard(eventQueueLock_);
|
|
|
|
|
if (!eventQueue_.empty()) {
|
|
|
|
|
ev = eventQueue_.front();
|
|
|
|
|
eventQueue_.pop_front();
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ignoreInput_) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
switch (ev.type) {
|
|
|
|
|
case QueuedEventType::KEY:
|
|
|
|
|
key(ev.key);
|
|
|
|
|
break;
|
|
|
|
|
case QueuedEventType::TOUCH:
|
|
|
|
|
if (ClickDebug && (ev.touch.flags & TOUCH_DOWN)) {
|
2024-07-14 14:42:59 +02:00
|
|
|
INFO_LOG(Log::System, "Touch down!");
|
2023-05-27 00:31:10 +02:00
|
|
|
std::vector<UI::View *> views;
|
|
|
|
|
root_->Query(ev.touch.x, ev.touch.y, views);
|
|
|
|
|
for (auto view : views) {
|
2024-07-14 14:42:59 +02:00
|
|
|
INFO_LOG(Log::System, "%s", view->DescribeLog().c_str());
|
2023-05-01 14:23:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-27 00:31:10 +02:00
|
|
|
touch(ev.touch);
|
|
|
|
|
break;
|
|
|
|
|
case QueuedEventType::AXIS:
|
|
|
|
|
axis(ev.axis);
|
|
|
|
|
break;
|
2023-05-01 14:23:47 +02:00
|
|
|
}
|
2013-07-20 12:04:24 +02:00
|
|
|
}
|
2013-06-03 19:57:40 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-27 23:10:33 +02:00
|
|
|
void UIScreen::deviceLost() {
|
|
|
|
|
if (root_)
|
|
|
|
|
root_->DeviceLost();
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-05 11:18:26 +01:00
|
|
|
void UIScreen::deviceRestored(Draw::DrawContext *draw) {
|
2018-03-27 23:10:33 +02:00
|
|
|
if (root_)
|
2024-11-05 11:18:26 +01:00
|
|
|
root_->DeviceRestored(draw);
|
2018-03-27 23:10:33 +02:00
|
|
|
}
|
|
|
|
|
|
2023-12-10 21:57:05 +01:00
|
|
|
void UIScreen::SetupViewport() {
|
|
|
|
|
using namespace Draw;
|
|
|
|
|
Draw::DrawContext *draw = screenManager()->getDrawContext();
|
|
|
|
|
_dbg_assert_(draw != nullptr);
|
|
|
|
|
// Bind and clear the back buffer
|
|
|
|
|
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI");
|
|
|
|
|
screenManager()->getUIContext()->BeginFrame();
|
|
|
|
|
|
|
|
|
|
Draw::Viewport viewport;
|
|
|
|
|
viewport.TopLeftX = 0;
|
|
|
|
|
viewport.TopLeftY = 0;
|
|
|
|
|
viewport.Width = g_display.pixel_xres;
|
|
|
|
|
viewport.Height = g_display.pixel_yres;
|
|
|
|
|
viewport.MaxDepth = 1.0;
|
|
|
|
|
viewport.MinDepth = 0.0;
|
|
|
|
|
draw->SetViewport(viewport);
|
|
|
|
|
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-11 12:41:44 +01:00
|
|
|
ScreenRenderFlags UIScreen::render(ScreenRenderMode mode) {
|
2023-12-10 14:09:55 +01:00
|
|
|
if (mode & ScreenRenderMode::FIRST) {
|
2023-12-10 21:57:05 +01:00
|
|
|
SetupViewport();
|
2023-12-10 14:09:55 +01:00
|
|
|
}
|
|
|
|
|
|
2013-07-21 13:30:47 +02:00
|
|
|
DoRecreateViews();
|
|
|
|
|
|
2023-12-11 12:41:44 +01:00
|
|
|
UIContext &uiContext = *screenManager()->getUIContext();
|
2013-06-08 22:41:17 +02:00
|
|
|
if (root_) {
|
2023-12-10 21:57:05 +01:00
|
|
|
UI::LayoutViewHierarchy(uiContext, root_, ignoreInsets_);
|
2013-06-08 22:41:17 +02:00
|
|
|
}
|
2023-12-11 12:41:44 +01:00
|
|
|
|
|
|
|
|
uiContext.PushTransform({translation_, scale_, alpha_});
|
|
|
|
|
|
|
|
|
|
uiContext.Begin();
|
|
|
|
|
DrawBackground(uiContext);
|
|
|
|
|
if (root_) {
|
|
|
|
|
root_->Draw(uiContext);
|
|
|
|
|
}
|
|
|
|
|
uiContext.Flush();
|
|
|
|
|
DrawForeground(uiContext);
|
|
|
|
|
uiContext.Flush();
|
|
|
|
|
|
|
|
|
|
uiContext.PopTransform();
|
|
|
|
|
|
|
|
|
|
return ScreenRenderFlags::NONE;
|
2013-06-03 19:57:40 +02:00
|
|
|
}
|
|
|
|
|
|
2017-03-19 14:28:24 -07:00
|
|
|
TouchInput UIScreen::transformTouch(const TouchInput &touch) {
|
|
|
|
|
TouchInput updated = touch;
|
|
|
|
|
|
|
|
|
|
float x = touch.x - translation_.x;
|
|
|
|
|
float y = touch.y - translation_.y;
|
|
|
|
|
// Scale around the center as the origin.
|
2023-02-25 13:09:44 +01:00
|
|
|
updated.x = (x - g_display.dp_xres * 0.5f) / scale_.x + g_display.dp_xres * 0.5f;
|
|
|
|
|
updated.y = (y - g_display.dp_yres * 0.5f) / scale_.y + g_display.dp_yres * 0.5f;
|
2017-03-19 14:28:24 -07:00
|
|
|
|
|
|
|
|
return updated;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 17:43:03 -07:00
|
|
|
void UIScreen::TriggerFinish(DialogResult result) {
|
2023-05-06 15:09:12 +02:00
|
|
|
// From here on, this dialog cannot receive input.
|
|
|
|
|
ignoreInput_ = true;
|
2017-03-19 17:43:03 -07:00
|
|
|
screenManager()->finishDialog(this, result);
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-15 13:04:10 +02:00
|
|
|
bool UIDialogScreen::key(const KeyInput &key) {
|
2015-02-03 00:10:38 +01:00
|
|
|
bool retval = UIScreen::key(key);
|
2015-08-28 17:05:11 +02:00
|
|
|
if (!retval && (key.flags & KEY_DOWN) && UI::IsEscapeKey(key)) {
|
2013-10-28 16:05:21 +01:00
|
|
|
if (finished_) {
|
2024-07-14 14:42:59 +02:00
|
|
|
ERROR_LOG(Log::System, "Screen already finished");
|
2013-10-28 16:05:21 +01:00
|
|
|
} else {
|
|
|
|
|
finished_ = true;
|
2017-03-19 17:43:03 -07:00
|
|
|
TriggerFinish(DR_BACK);
|
2020-08-03 11:58:55 +02:00
|
|
|
UI::PlayUISound(UI::UISound::BACK);
|
2013-10-28 16:05:21 +01:00
|
|
|
}
|
2014-06-15 13:04:10 +02:00
|
|
|
return true;
|
2013-08-13 00:06:23 +02:00
|
|
|
}
|
2015-02-03 00:10:38 +01:00
|
|
|
return retval;
|
2013-08-13 00:06:23 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-30 11:21:22 +02:00
|
|
|
void UIDialogScreen::sendMessage(UIMessage message, const char *value) {
|
2017-12-02 11:07:27 -08:00
|
|
|
Screen *screen = screenManager()->dialogParent(this);
|
|
|
|
|
if (screen) {
|
2023-09-30 11:21:22 +02:00
|
|
|
screen->sendMessage(message, value);
|
2017-12-02 11:07:27 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-03 19:57:40 +02:00
|
|
|
UI::EventReturn UIScreen::OnBack(UI::EventParams &e) {
|
2017-03-19 17:43:03 -07:00
|
|
|
TriggerFinish(DR_BACK);
|
2013-06-03 19:57:40 +02:00
|
|
|
return UI::EVENT_DONE;
|
|
|
|
|
}
|
2013-07-16 00:25:08 +02:00
|
|
|
|
2013-12-05 14:15:18 +01:00
|
|
|
UI::EventReturn UIScreen::OnOK(UI::EventParams &e) {
|
2017-03-19 17:43:03 -07:00
|
|
|
TriggerFinish(DR_OK);
|
2013-12-05 14:15:18 +01:00
|
|
|
return UI::EVENT_DONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UI::EventReturn UIScreen::OnCancel(UI::EventParams &e) {
|
2017-03-19 17:43:03 -07:00
|
|
|
TriggerFinish(DR_CANCEL);
|
2013-12-05 14:15:18 +01:00
|
|
|
return UI::EVENT_DONE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-19 13:44:49 +01:00
|
|
|
PopupScreen::PopupScreen(std::string_view title, std::string_view button1, std::string_view button2)
|
2023-01-30 18:31:49 +01:00
|
|
|
: title_(title) {
|
2023-04-06 00:34:50 +02:00
|
|
|
auto di = GetI18NCategory(I18NCat::DIALOG);
|
2013-11-29 11:33:51 +01:00
|
|
|
if (!button1.empty())
|
2024-01-19 13:44:49 +01:00
|
|
|
button1_ = di->T(button1);
|
2013-11-29 11:33:51 +01:00
|
|
|
if (!button2.empty())
|
2024-01-19 13:44:49 +01:00
|
|
|
button2_ = di->T(button2);
|
2023-01-30 18:31:49 +01:00
|
|
|
alpha_ = 0.0f; // inherited
|
2013-08-31 05:33:23 +08:00
|
|
|
}
|
2013-08-20 13:03:42 +02:00
|
|
|
|
2022-12-31 21:41:32 +01:00
|
|
|
void PopupScreen::touch(const TouchInput &touch) {
|
2021-04-17 22:33:44 +02:00
|
|
|
if (!box_ || (touch.flags & TOUCH_DOWN) == 0) {
|
2023-03-24 00:05:32 +01:00
|
|
|
// Handle down-presses here.
|
2022-12-31 21:41:32 +01:00
|
|
|
UIDialogScreen::touch(touch);
|
2023-03-24 00:05:32 +01:00
|
|
|
return;
|
2013-08-20 13:03:42 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-01 15:35:57 +01:00
|
|
|
// Extra bounds to avoid closing the dialog while trying to aim for something
|
2023-03-24 00:05:32 +01:00
|
|
|
// near the edge. Now that we only close on actual down-events, we can shrink
|
|
|
|
|
// this border a bit.
|
|
|
|
|
if (!box_->GetBounds().Expand(30.0f, 30.0f).Contains(touch.x, touch.y)) {
|
2023-02-01 15:35:57 +01:00
|
|
|
TriggerFinish(DR_CANCEL);
|
2021-04-17 12:07:40 +02:00
|
|
|
}
|
2013-08-20 13:03:42 +02:00
|
|
|
|
2022-12-31 21:41:32 +01:00
|
|
|
UIDialogScreen::touch(touch);
|
2013-08-20 13:03:42 +02:00
|
|
|
}
|
2013-07-16 00:25:08 +02:00
|
|
|
|
2014-07-18 12:22:33 +02:00
|
|
|
bool PopupScreen::key(const KeyInput &key) {
|
|
|
|
|
if (key.flags & KEY_DOWN) {
|
2022-06-21 08:32:57 +02:00
|
|
|
if ((key.keyCode == NKCODE_ENTER || key.keyCode == NKCODE_NUMPAD_ENTER) && defaultButton_) {
|
2017-03-21 18:34:52 -07:00
|
|
|
UI::EventParams e{};
|
2014-07-18 12:22:33 +02:00
|
|
|
defaultButton_->OnClick.Trigger(e);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return UIDialogScreen::key(key);
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 15:41:48 -07:00
|
|
|
void PopupScreen::update() {
|
|
|
|
|
UIDialogScreen::update();
|
|
|
|
|
|
2019-08-18 12:00:21 -07:00
|
|
|
if (defaultButton_)
|
|
|
|
|
defaultButton_->SetEnabled(CanComplete(DR_OK));
|
2019-08-17 18:24:57 -07:00
|
|
|
|
2017-03-21 18:27:57 -07:00
|
|
|
float animatePos = 1.0f;
|
|
|
|
|
|
2017-03-19 17:45:39 -07:00
|
|
|
++frames_;
|
2017-03-26 08:58:04 -07:00
|
|
|
if (finishFrame_ >= 0) {
|
2017-03-19 17:45:39 -07:00
|
|
|
float leadOut = bezierEaseInOut((frames_ - finishFrame_) * (1.0f / (float)FRAMES_LEAD_OUT));
|
2017-03-21 18:27:57 -07:00
|
|
|
animatePos = 1.0f - leadOut;
|
2017-03-19 17:45:39 -07:00
|
|
|
|
|
|
|
|
if (frames_ >= finishFrame_ + FRAMES_LEAD_OUT) {
|
|
|
|
|
// Actual finish happens here.
|
|
|
|
|
screenManager()->finishDialog(this, finishResult_);
|
|
|
|
|
}
|
2017-03-26 08:58:04 -07:00
|
|
|
} else if (frames_ < FRAMES_LEAD_IN) {
|
|
|
|
|
float leadIn = bezierEaseInOut(frames_ * (1.0f / (float)FRAMES_LEAD_IN));
|
|
|
|
|
animatePos = leadIn;
|
2017-03-21 18:27:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (animatePos < 1.0f) {
|
|
|
|
|
alpha_ = animatePos;
|
|
|
|
|
scale_.x = 0.9f + animatePos * 0.1f;
|
|
|
|
|
scale_.y = 0.9f + animatePos * 0.1f;
|
|
|
|
|
|
|
|
|
|
if (hasPopupOrigin_) {
|
2023-02-25 13:09:44 +01:00
|
|
|
float xoff = popupOrigin_.x - g_display.dp_xres / 2;
|
|
|
|
|
float yoff = popupOrigin_.y - g_display.dp_yres / 2;
|
2017-03-21 18:27:57 -07:00
|
|
|
|
|
|
|
|
// Pull toward the origin a bit.
|
|
|
|
|
translation_.x = xoff * (1.0f - animatePos) * 0.2f;
|
|
|
|
|
translation_.y = yoff * (1.0f - animatePos) * 0.2f;
|
|
|
|
|
} else {
|
2023-02-25 13:09:44 +01:00
|
|
|
translation_.y = -g_display.dp_yres * (1.0f - animatePos) * 0.2f;
|
2017-03-21 18:27:57 -07:00
|
|
|
}
|
2017-03-19 15:41:48 -07:00
|
|
|
} else {
|
|
|
|
|
alpha_ = 1.0f;
|
|
|
|
|
scale_.x = 1.0f;
|
|
|
|
|
scale_.y = 1.0f;
|
2017-03-21 18:27:57 -07:00
|
|
|
translation_.x = 0.0f;
|
2017-03-19 15:41:48 -07:00
|
|
|
translation_.y = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 18:27:57 -07:00
|
|
|
void PopupScreen::SetPopupOrigin(const UI::View *view) {
|
|
|
|
|
hasPopupOrigin_ = true;
|
|
|
|
|
popupOrigin_ = view->GetBounds().Center();
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 17:45:39 -07:00
|
|
|
void PopupScreen::TriggerFinish(DialogResult result) {
|
2019-08-17 18:24:57 -07:00
|
|
|
if (CanComplete(result)) {
|
2023-05-06 15:09:12 +02:00
|
|
|
ignoreInput_ = true;
|
2019-08-17 18:24:57 -07:00
|
|
|
finishFrame_ = frames_;
|
|
|
|
|
finishResult_ = result;
|
2017-03-19 17:45:39 -07:00
|
|
|
|
2019-08-17 18:24:57 -07:00
|
|
|
OnCompleted(result);
|
|
|
|
|
}
|
2023-08-24 06:33:36 +04:00
|
|
|
// Inform UI that popup close to hide OSK (if visible)
|
2024-05-20 18:25:16 +02:00
|
|
|
System_NotifyUIEvent(UIEventNotification::POPUP_CLOSED);
|
2017-03-19 17:45:39 -07:00
|
|
|
}
|
|
|
|
|
|
2013-07-16 00:25:08 +02:00
|
|
|
void PopupScreen::CreateViews() {
|
|
|
|
|
using namespace UI;
|
2014-02-10 12:35:36 +01:00
|
|
|
UIContext &dc = *screenManager()->getUIContext();
|
|
|
|
|
|
2015-12-22 19:52:23 -08:00
|
|
|
AnchorLayout *anchor = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
|
|
|
|
|
anchor->Overflow(false);
|
|
|
|
|
root_ = anchor;
|
2013-08-10 23:03:12 +02:00
|
|
|
|
2014-02-10 12:35:36 +01:00
|
|
|
float yres = screenManager()->getUIContext()->GetBounds().h;
|
|
|
|
|
|
2024-05-20 18:33:35 +02:00
|
|
|
AnchorLayoutParams *anchorParams;
|
|
|
|
|
if (!alignTop_) {
|
|
|
|
|
// Standard centering etc.
|
|
|
|
|
anchorParams = new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT,
|
|
|
|
|
dc.GetBounds().centerX(), dc.GetBounds().centerY() + offsetY_, NONE, NONE, true);
|
|
|
|
|
} else {
|
|
|
|
|
// Top-aligned, for dialogs where we need to pop a keyboard below.
|
|
|
|
|
anchorParams = new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT,
|
|
|
|
|
NONE, 0, NONE, NONE, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
box_ = new LinearLayout(ORIENT_VERTICAL, anchorParams);
|
2013-07-17 01:03:29 +02:00
|
|
|
|
2013-08-20 13:03:42 +02:00
|
|
|
root_->Add(box_);
|
2017-03-26 08:57:04 -07:00
|
|
|
box_->SetBG(dc.theme->popupStyle.background);
|
2022-02-11 18:23:56 +01:00
|
|
|
box_->SetHasDropShadow(hasDropShadow_);
|
2017-03-19 15:41:48 -07:00
|
|
|
// Since we scale a bit, make the dropshadow bleed past the edges.
|
2023-02-25 13:09:44 +01:00
|
|
|
box_->SetDropShadowExpand(std::max(g_display.dp_xres, g_display.dp_yres));
|
2023-01-10 16:00:17 +01:00
|
|
|
box_->SetSpacing(0.0f);
|
2013-07-16 00:25:08 +02:00
|
|
|
|
2022-12-07 01:09:44 +01:00
|
|
|
if (HasTitleBar()) {
|
2023-12-20 12:33:56 +03:00
|
|
|
View* title = new PopupHeader(title_);
|
2022-12-07 01:09:44 +01:00
|
|
|
box_->Add(title);
|
|
|
|
|
}
|
2013-07-16 00:25:08 +02:00
|
|
|
|
2013-08-20 13:03:42 +02:00
|
|
|
CreatePopupContents(box_);
|
2014-03-03 12:55:29 +01:00
|
|
|
root_->SetDefaultFocusView(box_);
|
2013-11-29 11:33:51 +01:00
|
|
|
if (ShowButtons() && !button1_.empty()) {
|
2013-08-15 22:13:57 +02:00
|
|
|
// And the two buttons at the bottom.
|
2013-08-16 16:47:25 +02:00
|
|
|
LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(200, WRAP_CONTENT));
|
|
|
|
|
buttonRow->SetSpacing(0);
|
|
|
|
|
Margins buttonMargins(5, 5);
|
2013-08-16 12:51:57 +02:00
|
|
|
|
|
|
|
|
// Adjust button order to the platform default.
|
2023-12-05 14:17:14 +01:00
|
|
|
if (System_GetPropertyBool(SYSPROP_OK_BUTTON_LEFT)) {
|
|
|
|
|
defaultButton_ = buttonRow->Add(new Button(button1_, new LinearLayoutParams(1.0f, buttonMargins)));
|
|
|
|
|
defaultButton_->OnClick.Handle<UIScreen>(this, &UIScreen::OnOK);
|
|
|
|
|
if (!button2_.empty())
|
|
|
|
|
buttonRow->Add(new Button(button2_, new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnCancel);
|
|
|
|
|
} else {
|
|
|
|
|
if (!button2_.empty())
|
|
|
|
|
buttonRow->Add(new Button(button2_, new LinearLayoutParams(1.0f)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnCancel);
|
|
|
|
|
defaultButton_ = buttonRow->Add(new Button(button1_, new LinearLayoutParams(1.0f)));
|
|
|
|
|
defaultButton_->OnClick.Handle<UIScreen>(this, &UIScreen::OnOK);
|
|
|
|
|
}
|
2013-08-16 12:51:57 +02:00
|
|
|
|
2013-08-20 13:03:42 +02:00
|
|
|
box_->Add(buttonRow);
|
2013-08-15 22:13:57 +02:00
|
|
|
}
|
2013-07-16 00:25:08 +02:00
|
|
|
}
|