UI: make display notification/query use sciter

This commit is contained in:
Stardust 2026-05-25 12:35:17 +09:30
parent d41b232322
commit 6e819f283f
6 changed files with 358 additions and 7 deletions

2
external/sciterui vendored

@ -1 +1 @@
Subproject commit 7b5a7e6deb92648ac05548a815f96ca17baea7b2
Subproject commit 10c9708a7554d0e3fbe7d5dc995f1a58ae4cb953

View File

@ -0,0 +1,147 @@
:root {
--color-notification-background: #181818;
}
html {
margin: 0;
padding: 0 7px 14px 7px;
border: 0;
overflow: visible;
width: max-content;
height: max-content;
background: transparent;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
background: transparent;
}
.notification-panel {
border-radius: 8px;
border: none;
background-color: var(--color-notification-background);
box-shadow:
0 2px 8px rgba(0, 0, 0, 1),
0 12px 12px rgba(0, 0, 0, 0.55);
overflow: hidden;
color: #eff0f1;
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 0.9rem;
}
.notification-panel > header {
width: *;
flow: horizontal;
background-color: var(--color-titlebar-background);
height: window-caption-height;
margin: 0;
color: var(--color-titlebar-text);
}
html[data-active="false"] .notification-panel > header {
background-color: var(--color-titlebar-background-inactive);
}
.notification-panel > header > window-caption {
display: block;
line-height: window-caption-height;
width: *;
height: *;
padding: 0 0.75em 0 1.1em;
font-family: var(--font-ui);
font-size: 1rem;
font-weight: 400;
}
.notification-panel > header > window-caption span {
vertical-align: middle;
}
.notification-panel > header > window-caption img {
vertical-align: middle;
margin-right: 0.5em;
width: 1.5em;
height: 1.5em;
}
.notification-panel > header > window-buttons {
display: block;
flow: horizontal;
width: max-content;
height: *;
vertical-align: middle;
}
.notification-panel > header > window-buttons > window-button {
behavior: clickable;
display: block;
height: *;
width: window-button-width;
foreground-size: 2em 1.5em;
foreground-repeat: no-repeat;
foreground-position: 50% 50%;
stroke-width: 0;
fill: var(--color-titlebar-icon);
transition: background-color linear 100ms;
padding: 0.25em;
}
html[data-active="false"] .notification-panel > header > window-buttons > window-button {
fill: var(--color-titlebar-icon-inactive);
}
.notification-panel > header > window-buttons > window-button:hover {
background: var(--color-titlebar-icon-hover);
}
.notification-panel > header > window-buttons > window-button[role="window-close"] {
foreground-image: url(icon:window-close);
}
.notification-panel > header > window-buttons > window-button[role="window-close"]:hover {
background: rgb(232, 17, 35);
fill: #fff;
}
.notification-body {
min-width: 22em;
max-width: 32em;
margin: 0;
padding: 1.35em 1.2em 0.75em 1.2em;
}
#NotificationMessage {
white-space: pre-wrap;
line-height: 1.4;
}
.footer {
padding: 0.25em 1.2em 1.1em 1.2em;
text-align: right;
}
.notification-panel button {
color: #eff0f1;
background: #232728;
border: 1px solid #55575a;
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 0.85rem;
}
.notification-panel button:hover {
border: 1px solid #3cafe9;
}
.notification-panel button:active {
background: #3cafe9;
}
.footer button {
margin-left: 0.5em;
min-width: 5.5rem;
height: 2rem;
padding: 0.4em 1.4em;
}

View File

@ -0,0 +1,25 @@
<html window-frame="transparent" window-icon="window-icon.png">
<head>
<link href="base.css" rel="stylesheet" type="text/css" />
<link href="notification_dialog.css" rel="stylesheet" type="text/css" />
<title>Notification</title>
</head>
<body>
<div class="notification-panel">
<header>
<window-caption role="window-caption"><img src="window-icon-32.png" /><span id="NotificationTitle">Notification</span></window-caption>
<window-buttons>
<window-button role="window-close"></window-button>
</window-buttons>
</header>
<div class="notification-body">
<div id="NotificationMessage"></div>
</div>
<div class="footer">
<button id="NotificationYes">Yes</button>
<button id="NotificationNo" role="window-close">No</button>
<button id="NotificationOk" role="window-ok">OK</button>
</div>
</div>
</body>
</html>

View File

@ -1,23 +1,191 @@
#include "notification.h"
#include "settings/ui_settings.h"
#include <Windows.h>
#include <sciter_element.h>
#include <sciter_handler.h>
#include <sciter_ui.h>
#include <common/std_string.h>
#include <Windows.h>
namespace
{
enum class NotificationDialogMode
{
Alert,
Query,
};
class NotificationWindow :
public IClickSink,
public IWindowDestroySink
{
public:
NotificationWindow(ISciterUI & sciterUI) :
m_sciterUI(sciterUI),
m_window(nullptr),
m_mode(NotificationDialogMode::Alert),
m_response(NotificationResponse::No)
{
}
NotificationResponse Show(void * parentWindow, NotificationDialogMode mode, const char * title, const char * message)
{
m_mode = mode;
m_response = mode == NotificationDialogMode::Query ? NotificationResponse::No : NotificationResponse::Yes;
m_window = nullptr;
if (!m_sciterUI.WindowCreate(parentWindow, "notification_dialog.html", 0, 0, 0, 0, SUIW_CHILD, m_window))
{
return m_response;
}
SciterElement root(m_window->GetRootElement());
if (root.IsValid())
{
SciterElement titleEl(root.GetElementByID("NotificationTitle"));
if (titleEl.IsValid() && title != nullptr)
{
titleEl.SetText(title);
}
SciterElement messageEl(root.GetElementByID("NotificationMessage"));
if (messageEl.IsValid() && message != nullptr)
{
messageEl.SetText(message);
}
ConfigureButtons();
m_sciterUI.AttachHandler(root.FindFirst("button[role=\"window-ok\"]"), IID_ICLICKSINK, (IClickSink *)this);
m_sciterUI.AttachHandler(root.FindFirst("button[role=\"window-close\"]"), IID_ICLICKSINK, (IClickSink *)this);
m_sciterUI.AttachHandler(root.GetElementByID("NotificationYes"), IID_ICLICKSINK, (IClickSink *)this);
m_sciterUI.AttachHandler(root.GetElementByID("NotificationNo"), IID_ICLICKSINK, (IClickSink *)this);
m_sciterUI.AttachHandler(root.GetElementByID("NotificationOk"), IID_ICLICKSINK, (IClickSink *)this);
}
m_window->OnDestroySinkAdd(this);
m_window->FixMinSize();
m_window->CenterWindow();
m_window->RunModal();
return m_response;
}
private:
NotificationWindow(const NotificationWindow &) = delete;
NotificationWindow & operator=(const NotificationWindow &) = delete;
void ConfigureButtons()
{
SciterElement root(m_window->GetRootElement());
if (!root.IsValid())
{
return;
}
const bool query = m_mode == NotificationDialogMode::Query;
SciterElement yesBtn(root.GetElementByID("NotificationYes"));
SciterElement noBtn(root.GetElementByID("NotificationNo"));
SciterElement okBtn(root.GetElementByID("NotificationOk"));
if (yesBtn.IsValid())
{
yesBtn.SetStyleAttribute("display", query ? "inline-block" : "none");
}
if (noBtn.IsValid())
{
noBtn.SetStyleAttribute("display", query ? "inline-block" : "none");
}
if (okBtn.IsValid())
{
okBtn.SetStyleAttribute("display", query ? "none" : "inline-block");
}
}
void Close(NotificationResponse response)
{
if (m_window == nullptr || m_window->IsClosed())
{
return;
}
m_response = response;
m_window->Destroy();
}
bool OnClick(SCITER_ELEMENT element, SCITER_ELEMENT /*source*/, uint32_t /*reason*/) override
{
SciterElement clickElem(element);
const std::string role = clickElem.GetAttribute("role");
const std::string id = clickElem.GetAttribute("id");
if (id == "NotificationYes" || role == "window-ok")
{
Close(NotificationResponse::Yes);
return true;
}
if (id == "NotificationNo" || role == "window-close")
{
Close(NotificationResponse::No);
return true;
}
if (id == "NotificationOk")
{
Close(NotificationResponse::Yes);
return true;
}
return false;
}
void OnWindowDestroy(HWINDOW /*hWnd*/) override
{
m_window = nullptr;
}
ISciterUI & m_sciterUI;
ISciterWindow * m_window;
NotificationDialogMode m_mode;
NotificationResponse m_response;
};
} // namespace
std::unique_ptr<Notification> Notification::s_instance;
Notification::Notification()
Notification::Notification() :
m_sciterUI(nullptr),
m_parentWindow(nullptr)
{
}
void Notification::SetSciterContext(ISciterUI * sciterUI, void * parentWindow)
{
m_sciterUI = sciterUI;
m_parentWindow = parentWindow;
}
void Notification::ClearSciterContext()
{
m_sciterUI = nullptr;
m_parentWindow = nullptr;
}
void Notification::DisplayError(const char * message, const char * title) const
{
MessageBoxW(nullptr, stdstr(message).ToUTF16().c_str(), stdstr(title).ToUTF16().c_str(), MB_OK | MB_ICONERROR | MB_SETFOREGROUND);
if (m_sciterUI == nullptr || m_parentWindow == nullptr)
{
return;
}
NotificationWindow dialog(*m_sciterUI);
dialog.Show(m_parentWindow, NotificationDialogMode::Alert, title, message);
}
NotificationResponse Notification::Query(const char * message, const char * title) const
{
const int result = MessageBoxW(nullptr, stdstr(message).ToUTF16().c_str(), stdstr(title).ToUTF16().c_str(), MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND);
return result == IDYES ? NotificationResponse::Yes : NotificationResponse::No;
if (m_sciterUI == nullptr || m_parentWindow == nullptr)
{
return NotificationResponse::No;
}
NotificationWindow dialog(*m_sciterUI);
return dialog.Show(m_parentWindow, NotificationDialogMode::Query, title, message);
}
void Notification::BreakPoint(const char * fileName, uint32_t lineNumber)
@ -46,4 +214,4 @@ Notification & Notification::GetInstance()
void Notification::CleanUp()
{
s_instance.reset();
}
}

View File

@ -2,6 +2,8 @@
#include <memory>
#include <nxemu-core/notification.h>
__interface ISciterUI;
class Notification :
public INotification
{
@ -14,6 +16,9 @@ public:
void BreakPoint(const char * fileName, uint32_t lineNumber) override;
void AppInitDone(void) override;
void SetSciterContext(ISciterUI * sciterUI, void * parentWindow);
void ClearSciterContext();
static Notification & GetInstance();
static void CleanUp();
@ -22,4 +27,7 @@ private:
Notification & operator=(const Notification &) = delete;
static std::unique_ptr<Notification> s_instance;
ISciterUI * m_sciterUI;
void * m_parentWindow;
};

View File

@ -1,4 +1,5 @@
#include "sciter_main_window.h"
#include "user_interface/notification.h"
#include "settings/input_config.h"
#include "settings/system_config.h"
#include "settings/ui_settings.h"
@ -298,6 +299,7 @@ const std::string * SciterMainWindow::MenuIconSvg(GuiAction action)
SciterMainWindow::~SciterMainWindow()
{
Notification::GetInstance().ClearSciterContext();
m_WebBrowser.DetachWindow();
SettingsStore & settings = SettingsStore::GetInstance();
@ -491,6 +493,7 @@ bool SciterMainWindow::Show()
{
PopulateVulkanRecords(m_vkDeviceRecords, RenderSurface());
}
Notification::GetInstance().SetSciterContext(&m_sciterUI, (void *)m_window->GetHandle());
m_window->Show();
return true;
}