add lua navigation

This commit is contained in:
2026-01-16 00:36:41 +01:00
parent ebf80052f0
commit 08327e5575
10 changed files with 170 additions and 59 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@
/cmake-build* /cmake-build*
/out /out
/dump /dump
/material-design-icons

View File

@@ -35,19 +35,19 @@
<div class="home-content"> <div class="home-content">
<div class="app-grid"> <div class="app-grid">
<!-- Row 1 --> <!-- Row 1 -->
<div class="app-icon"> <div class="app-icon" onclick="navigateTo('dialer')">
<div class="app-icon-image" style="background-color: #4CAF50;">P</div> <div class="app-icon-image" style="background-color: #4CAF50;">P</div>
<span class="app-icon-label">Phone</span> <span class="app-icon-label">Phone</span>
</div> </div>
<div class="app-icon"> <div class="app-icon" onclick="navigateTo('messages')">
<div class="app-icon-image" style="background-color: #2196F3;">M</div> <div class="app-icon-image" style="background-color: #2196F3;">M</div>
<span class="app-icon-label">Messages</span> <span class="app-icon-label">Messages</span>
</div> </div>
<div class="app-icon"> <div class="app-icon" onclick="navigateTo('contacts')">
<div class="app-icon-image" style="background-color: #FF9800;">C</div> <div class="app-icon-image" style="background-color: #FF9800;">C</div>
<span class="app-icon-label">Contacts</span> <span class="app-icon-label">Contacts</span>
</div> </div>
<div class="app-icon"> <div class="app-icon" onclick="navigateTo('browser')">
<div class="app-icon-image" style="background-color: #F44336;">B</div> <div class="app-icon-image" style="background-color: #F44336;">B</div>
<span class="app-icon-label">Browser</span> <span class="app-icon-label">Browser</span>
</div> </div>
@@ -61,7 +61,7 @@
<div class="app-icon-image" style="background-color: #00BCD4;">C</div> <div class="app-icon-image" style="background-color: #00BCD4;">C</div>
<span class="app-icon-label">Camera</span> <span class="app-icon-label">Camera</span>
</div> </div>
<div class="app-icon"> <div class="app-icon" onclick="navigateTo('settings')">
<div class="app-icon-image" style="background-color: #607D8B;">S</div> <div class="app-icon-image" style="background-color: #607D8B;">S</div>
<span class="app-icon-label">Settings</span> <span class="app-icon-label">Settings</span>
</div> </div>
@@ -110,10 +110,10 @@
<!-- Dock --> <!-- Dock -->
<div class="dock"> <div class="dock">
<div class="dock-item" style="background-color: #4CAF50;">P</div> <div class="dock-item" style="background-color: #4CAF50;" onclick="navigateTo('dialer')">P</div>
<div class="dock-item" style="background-color: #2196F3;">M</div> <div class="dock-item" style="background-color: #2196F3;" onclick="navigateTo('messages')">M</div>
<div class="dock-item" style="background-color: #FF9800;">C</div> <div class="dock-item" style="background-color: #FF9800;" onclick="navigateTo('contacts')">C</div>
<div class="dock-item" style="background-color: #F44336;">B</div> <div class="dock-item" style="background-color: #F44336;" onclick="navigateTo('browser')">B</div>
</div> </div>
</body> </body>
</rml> </rml>

View File

@@ -2,6 +2,7 @@
<head> <head>
<link type="text/rcss" href="../ui/theme.rcss"/> <link type="text/rcss" href="../ui/theme.rcss"/>
<link type="text/rcss" href="../ui/components.rcss"/> <link type="text/rcss" href="../ui/components.rcss"/>
<script src="../scripts/navigation.lua"></script>
<title>Browser</title> <title>Browser</title>
<style> <style>
.browser-screen { .browser-screen {
@@ -172,7 +173,7 @@
<!-- Browser Toolbar --> <!-- Browser Toolbar -->
<div class="browser-toolbar"> <div class="browser-toolbar">
<div class="browser-nav-btn" onclick="navigateTo('home')">←</div> <div class="browser-nav-btn" onclick="goBack()">←</div>
<div class="browser-nav-btn disabled">→</div> <div class="browser-nav-btn disabled">→</div>
<div class="browser-url-bar"> <div class="browser-url-bar">
<span class="browser-secure-icon">L</span> <span class="browser-secure-icon">L</span>
@@ -211,7 +212,7 @@
<!-- Bottom Bar --> <!-- Bottom Bar -->
<div class="browser-bottom-bar"> <div class="browser-bottom-bar">
<div class="browser-tab-btn" onclick="navigateTo('home')"> <div class="browser-tab-btn" onclick="goHome()">
<span class="browser-tab-icon">H</span> <span class="browser-tab-icon">H</span>
<span class="browser-tab-label">Home</span> <span class="browser-tab-label">Home</span>
</div> </div>

View File

@@ -2,6 +2,7 @@
<head> <head>
<link type="text/rcss" href="../ui/theme.rcss"/> <link type="text/rcss" href="../ui/theme.rcss"/>
<link type="text/rcss" href="../ui/components.rcss"/> <link type="text/rcss" href="../ui/components.rcss"/>
<script src="../scripts/navigation.lua"></script>
<title>Contacts</title> <title>Contacts</title>
<style> <style>
.contacts-screen { .contacts-screen {
@@ -70,7 +71,7 @@
<body class="contacts-screen"> <body class="contacts-screen">
<!-- App Bar --> <!-- App Bar -->
<div class="app-bar"> <div class="app-bar">
<div class="btn-icon" onclick="navigateTo('home')">←</div> <div class="btn-icon" onclick="goBack()">←</div>
<span class="app-bar-title">Contacts</span> <span class="app-bar-title">Contacts</span>
<div class="btn-icon">+</div> <div class="btn-icon">+</div>
</div> </div>

View File

@@ -2,6 +2,7 @@
<head> <head>
<link type="text/rcss" href="../ui/theme.rcss"/> <link type="text/rcss" href="../ui/theme.rcss"/>
<link type="text/rcss" href="../ui/components.rcss"/> <link type="text/rcss" href="../ui/components.rcss"/>
<script src="../scripts/navigation.lua"></script>
<script src="../scripts/phone.lua"></script> <script src="../scripts/phone.lua"></script>
<title>Phone</title> <title>Phone</title>
<style> <style>
@@ -106,7 +107,7 @@
<body class="dialer-screen"> <body class="dialer-screen">
<!-- App Bar --> <!-- App Bar -->
<div class="app-bar"> <div class="app-bar">
<div class="btn-icon" onclick="navigateTo('home')">←</div> <div class="btn-icon" onclick="goBack()">←</div>
<span class="app-bar-title">Phone</span> <span class="app-bar-title">Phone</span>
</div> </div>

View File

@@ -2,6 +2,7 @@
<head> <head>
<link type="text/rcss" href="../ui/theme.rcss"/> <link type="text/rcss" href="../ui/theme.rcss"/>
<link type="text/rcss" href="../ui/components.rcss"/> <link type="text/rcss" href="../ui/components.rcss"/>
<script src="../scripts/navigation.lua"></script>
<title>Lock Screen</title> <title>Lock Screen</title>
<style> <style>
.lock-screen { .lock-screen {
@@ -124,7 +125,7 @@
} }
</style> </style>
</head> </head>
<body class="lock-screen" onclick="navigateTo('home')"> <body class="lock-screen" onclick="goHome()">
<!-- Status Bar --> <!-- Status Bar -->
<div class="status-bar"> <div class="status-bar">
<span class="status-bar-time">12:30</span> <span class="status-bar-time">12:30</span>

View File

@@ -2,6 +2,7 @@
<head> <head>
<link type="text/rcss" href="../ui/theme.rcss"/> <link type="text/rcss" href="../ui/theme.rcss"/>
<link type="text/rcss" href="../ui/components.rcss"/> <link type="text/rcss" href="../ui/components.rcss"/>
<script src="../scripts/navigation.lua"></script>
<title>Messages</title> <title>Messages</title>
<style> <style>
.messages-screen { .messages-screen {
@@ -130,7 +131,7 @@
<body class="messages-screen"> <body class="messages-screen">
<!-- App Bar --> <!-- App Bar -->
<div class="app-bar"> <div class="app-bar">
<div class="btn-icon" onclick="navigateTo('home')">←</div> <div class="btn-icon" onclick="goBack()">←</div>
<span class="app-bar-title">Messages</span> <span class="app-bar-title">Messages</span>
<div class="btn-icon">Q</div> <div class="btn-icon">Q</div>
</div> </div>

View File

@@ -2,6 +2,7 @@
<head> <head>
<link type="text/rcss" href="../ui/theme.rcss"/> <link type="text/rcss" href="../ui/theme.rcss"/>
<link type="text/rcss" href="../ui/components.rcss"/> <link type="text/rcss" href="../ui/components.rcss"/>
<script src="../scripts/navigation.lua"></script>
<title>Settings</title> <title>Settings</title>
<style> <style>
.settings-screen { .settings-screen {
@@ -148,7 +149,7 @@
<body class="settings-screen"> <body class="settings-screen">
<!-- App Bar --> <!-- App Bar -->
<div class="app-bar"> <div class="app-bar">
<div class="btn-icon" onclick="navigateTo('home')">←</div> <div class="btn-icon" onclick="goBack()">←</div>
<span class="app-bar-title">Settings</span> <span class="app-bar-title">Settings</span>
<div class="btn-icon">Q</div> <div class="btn-icon">Q</div>
</div> </div>

View File

@@ -1,9 +1,9 @@
-- Navigation System for Virtual Smartphone -- Navigation System for Virtual Smartphone
-- Handles screen transitions and state management -- Handles screen transitions and state management
-- Screen registry -- Screen registry - maps screen names to RML file paths
local screens = { local screens = {
home = "screens/home.rml", home = "demo.rml",
lock = "screens/lock.rml", lock = "screens/lock.rml",
dialer = "screens/dialer.rml", dialer = "screens/dialer.rml",
contacts = "screens/contacts.rml", contacts = "screens/contacts.rml",
@@ -12,33 +12,59 @@ local screens = {
browser = "screens/browser.rml" browser = "screens/browser.rml"
} }
-- Current screen state -- Navigation history stack
local current_screen = "home" local history = {}
local screen_history = {}
-- Navigate to a screen -- Current screen name
local current_screen = "home"
-- Navigate to a screen by name
function navigateTo(screen_name) function navigateTo(screen_name)
if screens[screen_name] then local path = screens[screen_name]
-- Add current screen to history if path then
table.insert(screen_history, current_screen) -- Push current screen to history before navigating
table.insert(history, current_screen)
current_screen = screen_name current_screen = screen_name
-- Load the new document -- Load the new screen using C++ function
-- Note: In RmlUi, we'd typically use document loading local success = loadScreen(path)
print("Navigating to: " .. screen_name) if success then
print("Navigated to: " .. screen_name)
else
-- Restore previous state on failure
current_screen = table.remove(history)
print("Failed to navigate to: " .. screen_name)
end
return success
else else
print("Unknown screen: " .. screen_name) print("Unknown screen: " .. screen_name)
return false
end end
end end
-- Go back to previous screen -- Go back to previous screen
function goBack() function goBack()
if #screen_history > 0 then if #history > 0 then
current_screen = table.remove(screen_history) local previous = table.remove(history)
print("Going back to: " .. current_screen) local path = screens[previous]
if path then
current_screen = previous
loadScreen(path)
print("Back to: " .. previous)
return true
end
else else
print("No history to go back to") print("No history to go back to")
end end
return false
end
-- Go to home screen (clear history)
function goHome()
history = {}
current_screen = "home"
loadScreen(screens.home)
print("Navigated to home")
end end
-- Get current screen name -- Get current screen name
@@ -46,9 +72,19 @@ function getCurrentScreen()
return current_screen return current_screen
end end
-- Check if we can go back
function canGoBack()
return #history > 0
end
-- Clear navigation history -- Clear navigation history
function clearHistory() function clearHistory()
screen_history = {} history = {}
end
-- Get history depth
function getHistoryDepth()
return #history
end end
print("Navigation system initialized") print("Navigation system initialized")

122
main.cpp
View File

@@ -9,12 +9,26 @@
#include <RmlUi/Core.h> #include <RmlUi/Core.h>
#include <RmlUi/Debugger.h> #include <RmlUi/Debugger.h>
#include <RmlUi/Lua.h> #include <RmlUi/Lua.h>
#include <RmlUi/Lua/Interpreter.h>
#include <RmlUi_Backend.h> #include <RmlUi_Backend.h>
#include <RmlUi_Include_Windows.h> #include <RmlUi_Include_Windows.h>
#include <RmlUi_Include_GL3.h> #include <RmlUi_Include_GL3.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <png.h> #include <png.h>
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
// Global app state for Lua access
struct AppState {
Rml::Context* context = nullptr;
Rml::ElementDocument* document = nullptr;
std::filesystem::path assets_path;
} g_app;
void load_fonts(const std::filesystem::path& dir) void load_fonts(const std::filesystem::path& dir)
{ {
for (const auto& file : std::filesystem::directory_iterator(dir)) for (const auto& file : std::filesystem::directory_iterator(dir))
@@ -26,6 +40,57 @@ void load_fonts(const std::filesystem::path& dir)
} }
} }
// Lua function: loadScreen(path) - loads a new RML document
// Path is relative to assets folder
int lua_loadScreen(lua_State* L)
{
const char* path = luaL_checkstring(L, 1);
std::filesystem::path full_path = g_app.assets_path / path;
if (!std::filesystem::exists(full_path))
{
std::println("Screen not found: {}", full_path.string());
lua_pushboolean(L, false);
return 1;
}
// Unload current document
if (g_app.document)
{
g_app.context->UnloadDocument(g_app.document);
g_app.document = nullptr;
}
// Load new document
g_app.document = g_app.context->LoadDocument(full_path.string());
if (g_app.document)
{
g_app.document->Show();
std::println("Loaded screen: {}", path);
lua_pushboolean(L, true);
}
else
{
std::println("Failed to load screen: {}", path);
lua_pushboolean(L, false);
}
return 1;
}
// Register custom Lua functions
void registerLuaFunctions()
{
lua_State* L = Rml::Lua::Interpreter::GetLuaState();
// Register global function
lua_pushcfunction(L, lua_loadScreen);
lua_setglobal(L, "loadScreen");
std::println("Registered Lua functions");
}
void DumpElementTree(Rml::Element* element, std::ofstream& out, int depth = 0) void DumpElementTree(Rml::Element* element, std::ofstream& out, int depth = 0)
{ {
if (!element) return; if (!element) return;
@@ -173,22 +238,25 @@ int main(const int argc, const char* argv[])
Rml::Initialise(); Rml::Initialise();
Rml::Lua::Initialise(); Rml::Lua::Initialise();
Rml::Context* context = Rml::CreateContext("main", Rml::Vector2i(window_width, window_height)); g_app.context = Rml::CreateContext("main", Rml::Vector2i(window_width, window_height));
if (!context) if (!g_app.context)
{ {
Rml::Shutdown(); Rml::Shutdown();
Backend::Shutdown(); Backend::Shutdown();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Register custom Lua functions
registerLuaFunctions();
// Find the assets folder by checking for fonts // Find the assets folder by checking for fonts
std::filesystem::path assets_path = std::filesystem::absolute(file.parent_path()); g_app.assets_path = std::filesystem::absolute(file.parent_path());
// Walk up the directory tree to find a folder containing .ttf fonts // Walk up the directory tree to find a folder containing .ttf fonts
while (!assets_path.empty() && assets_path.has_parent_path()) while (!g_app.assets_path.empty() && g_app.assets_path.has_parent_path())
{ {
bool has_fonts = false; bool has_fonts = false;
for (const auto& entry : std::filesystem::directory_iterator(assets_path)) for (const auto& entry : std::filesystem::directory_iterator(g_app.assets_path))
{ {
if (entry.path().extension() == ".ttf") if (entry.path().extension() == ".ttf")
{ {
@@ -197,15 +265,15 @@ int main(const int argc, const char* argv[])
} }
} }
if (has_fonts) break; if (has_fonts) break;
assets_path = assets_path.parent_path(); g_app.assets_path = g_app.assets_path.parent_path();
} }
load_fonts(assets_path); load_fonts(g_app.assets_path);
Rml::ElementDocument* document = context->LoadDocument(file.string()); g_app.document = g_app.context->LoadDocument(file.string());
if (document) if (g_app.document)
{ {
document->Show(); g_app.document->Show();
} }
// Dump mode: render and capture screenshot // Dump mode: render and capture screenshot
@@ -222,10 +290,10 @@ int main(const int argc, const char* argv[])
} }
// First render to default framebuffer to initialize everything // First render to default framebuffer to initialize everything
Backend::ProcessEvents(context); Backend::ProcessEvents(g_app.context);
context->Update(); g_app.context->Update();
Backend::BeginFrame(); Backend::BeginFrame();
context->Render(); g_app.context->Render();
Backend::PresentFrame(); Backend::PresentFrame();
// Create dump folder if it doesn't exist // Create dump folder if it doesn't exist
@@ -238,7 +306,7 @@ int main(const int argc, const char* argv[])
{ {
dump_file << "Window: " << window_width << "x" << window_height << "\n"; dump_file << "Window: " << window_width << "x" << window_height << "\n";
dump_file << "\n=== Element Layout ===\n"; dump_file << "\n=== Element Layout ===\n";
DumpElementTree(document, dump_file); DumpElementTree(g_app.document, dump_file);
dump_file.close(); dump_file.close();
} }
@@ -252,8 +320,8 @@ int main(const int argc, const char* argv[])
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Render to FBO // Render to FBO
context->Update(); g_app.context->Update();
context->Render(); g_app.context->Render();
// Read pixels // Read pixels
auto pixels = fbo.readPixels(); auto pixels = fbo.readPixels();
@@ -269,7 +337,7 @@ int main(const int argc, const char* argv[])
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
const HANDLE hNotif = FindFirstChangeNotification(assets_path.c_str(), const HANDLE hNotif = FindFirstChangeNotification(g_app.assets_path.c_str(),
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE); TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE);
bool running = true; bool running = true;
@@ -278,22 +346,22 @@ int main(const int argc, const char* argv[])
if (const DWORD wait_result = WaitForSingleObject(hNotif, 100); if (const DWORD wait_result = WaitForSingleObject(hNotif, 100);
wait_result == WAIT_OBJECT_0) wait_result == WAIT_OBJECT_0)
{ {
if (document) if (g_app.document)
context->UnloadDocument(document); g_app.context->UnloadDocument(g_app.document);
context->Update(); g_app.context->Update();
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
document = context->LoadDocument(file.string()); g_app.document = g_app.context->LoadDocument(file.string());
if (document) if (g_app.document)
{ {
document->ReloadStyleSheet(); g_app.document->ReloadStyleSheet();
document->Show(); g_app.document->Show();
} }
FindNextChangeNotification(hNotif); FindNextChangeNotification(hNotif);
} }
running = Backend::ProcessEvents(context); running = Backend::ProcessEvents(g_app.context);
context->Update(); g_app.context->Update();
Backend::BeginFrame(); Backend::BeginFrame();
context->Render(); g_app.context->Render();
Backend::PresentFrame(); Backend::PresentFrame();
} }