add simulator mode to desktop designer for testing apps

- Add --simulator flag to launch home screen showing discovered apps
- Create app discovery system to scan test-apps/ directory
- Build simulator home screen with dark phone-like UI
- Add Lua API: simulator.launchApp, simulator.goHome, simulator.getApps
- ESC key returns to home when inside an app
- Apps displayed with icons in grid layout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-19 11:16:01 +01:00
parent 02db0d849c
commit f41eda6f62
7 changed files with 795 additions and 3 deletions

View File

@@ -0,0 +1,154 @@
/* Simulator Home Screen Styles */
body {
font-family: LatoLatin;
background-color: #1a1a2e;
color: #ffffff;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
/* Status Bar */
.status-bar {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 24dp;
padding: 0 12dp;
background-color: #0f0f1a;
font-size: 12dp;
color: #cccccc;
}
.status-time {
font-weight: bold;
}
.status-icons {
display: flex;
flex-direction: row;
gap: 8dp;
}
.status-wifi, .status-battery {
font-size: 10dp;
color: #4ade80;
}
/* Home Content */
.home-content {
flex: 1;
padding: 16dp;
overflow: auto;
}
.home-header {
text-align: center;
margin-bottom: 24dp;
}
.home-header h1 {
font-size: 24dp;
font-weight: bold;
margin: 0 0 4dp 0;
color: #ffffff;
}
.home-header .subtitle {
font-size: 14dp;
color: #888888;
margin: 0;
}
/* App Grid */
.app-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 16dp;
justify-content: center;
}
.no-apps {
text-align: center;
color: #666666;
padding: 32dp;
}
.no-apps p {
margin: 8dp 0;
}
.no-apps .hint {
font-size: 12dp;
color: #555555;
}
/* App Icon */
.app-icon {
display: flex;
flex-direction: column;
align-items: center;
width: 80dp;
cursor: pointer;
}
.app-icon:hover .app-icon-image {
transform: scale(1.1);
background-color: #3d3d5c;
}
.app-icon-image {
width: 56dp;
height: 56dp;
border-radius: 12dp;
background-color: #2d2d44;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
transition: transform 0.1s, background-color 0.1s;
}
.app-icon-image img {
width: 48dp;
height: 48dp;
}
.app-icon-placeholder {
font-size: 24dp;
color: #888888;
}
.app-icon-label {
font-size: 11dp;
color: #cccccc;
margin-top: 6dp;
text-align: center;
max-width: 80dp;
overflow: hidden;
white-space: nowrap;
}
/* Navigation Bar */
.nav-bar {
display: flex;
flex-direction: row;
justify-content: center;
gap: 24dp;
height: 40dp;
background-color: #0f0f1a;
align-items: center;
border-top-width: 1dp;
border-top-color: #2d2d44;
}
.nav-hint {
font-size: 11dp;
color: #666666;
}

View File

@@ -0,0 +1,36 @@
<rml>
<head>
<title>Mosis Simulator</title>
<link type="text/rcss" href="home.rcss"/>
<script src="simulator.lua"/>
</head>
<body>
<div class="status-bar">
<span class="status-time" id="status-time">12:00</span>
<span class="status-icons">
<span class="status-wifi">&#x25CF;</span>
<span class="status-battery">&#x25A0;</span>
</span>
</div>
<div class="home-content">
<div class="home-header">
<h1>Test Apps</h1>
<p class="subtitle">Tap an app to launch</p>
</div>
<div class="app-grid" id="app-grid">
<!-- Apps will be populated dynamically by Lua -->
<div class="no-apps" id="no-apps">
<p>No apps found</p>
<p class="hint">Place apps in test-apps/ folder</p>
</div>
</div>
</div>
<div class="nav-bar">
<div class="nav-hint">ESC = Back</div>
<div class="nav-hint">F5 = Reload</div>
</div>
</body>
</rml>

View File

@@ -0,0 +1,158 @@
-- Simulator Home Screen Logic
local apps = {}
-- Helper to get document (may not be available immediately)
local function getDoc()
if document then
return document
end
if rmlui and rmlui.contexts and rmlui.contexts.main then
local ctx = rmlui.contexts.main
if ctx.documents then
for i, doc in ipairs(ctx.documents) do
if doc then
return doc
end
end
end
end
return nil
end
-- Populate the app grid with discovered apps
function populateAppGrid()
local doc = getDoc()
if not doc then
print("[Simulator] Document not available for populateAppGrid")
return
end
local grid = doc:GetElementById("app-grid")
if not grid then
print("[Simulator] app-grid element not found")
return
end
-- Clear existing content
grid.inner_rml = ""
if #apps == 0 then
grid.inner_rml = [[
<div class="no-apps">
<p>No apps found</p>
<p class="hint">Place apps in test-apps/ folder</p>
</div>
]]
return
end
-- Build app icons
local html = ""
for i, app in ipairs(apps) do
local icon_html
if app.icon and app.icon ~= "" then
icon_html = string.format('<img src="%s"/>', app.icon)
else
icon_html = '<span class="app-icon-placeholder">&#x25A0;</span>'
end
html = html .. string.format([[
<div class="app-icon" onclick="launchApp('%s')">
<div class="app-icon-image">%s</div>
<span class="app-icon-label">%s</span>
</div>
]], app.id, icon_html, app.name)
end
grid.inner_rml = html
print("[Simulator] Populated " .. #apps .. " apps")
end
-- Get apps from C++ and populate the grid
function refreshApps()
if simulator and simulator.getApps then
apps = simulator.getApps()
print("[Simulator] Got " .. #apps .. " apps from C++")
populateAppGrid()
else
print("[Simulator] simulator.getApps not available")
end
end
-- Called from C++ after apps are discovered (backup method)
function setApps(app_list)
apps = app_list
populateAppGrid()
end
-- Launch an app by ID
function launchApp(app_id)
print("[Simulator] Launching app: " .. app_id)
-- Find the app
for _, app in ipairs(apps) do
if app.id == app_id then
-- Call C++ function to launch the app
if simulator and simulator.launchApp then
simulator.launchApp(app.entry, app.path, app.id)
else
print("[Simulator] Error: simulator.launchApp not available")
end
return
end
end
print("[Simulator] App not found: " .. app_id)
end
-- Update the time display
function updateTime()
local doc = getDoc()
if not doc then return end
local timeEl = doc:GetElementById("status-time")
if timeEl then
-- Use os.date if available, otherwise show static time
local time_str = "12:00"
if os and os.date then
time_str = os.date("%H:%M")
end
timeEl.inner_rml = time_str
end
end
-- Initialize
print("[Simulator] Home screen loaded")
-- Try immediate initialization first
local doc = getDoc()
if doc then
print("[Simulator] Document available immediately")
refreshApps()
updateTime()
elseif setInterval then
print("[Simulator] Document not ready, setting up timer")
-- Use a one-time timer to refresh apps after document is ready
local initTimerId = nil
local attempts = 0
initTimerId = setInterval(function()
attempts = attempts + 1
local d = getDoc()
if d then
print("[Simulator] Document ready after " .. attempts .. " attempts")
clearInterval(initTimerId)
refreshApps()
updateTime()
elseif attempts > 50 then
-- Give up after 5 seconds
print("[Simulator] Gave up waiting for document")
clearInterval(initTimerId)
end
end, 100)
-- Update time every minute
setInterval(updateTime, 60000)
else
print("[Simulator] No setInterval and no document - cannot init")
end