screen-toolkit: add compact/full panel modes, mode-aware toggle, and … (#237)
* screen-toolkit: add compact/full panel modes, mode-aware toggle, and full documentation * screen-toolkit: restrict compact tile hover/click to icon and label * screen-toolkit: full-mode hover border on tile, mirror compact colors * screen-toolkit: add plugin thumbnail screenshot --------- Co-authored-by: Ahmed5Emad <ahmed5emad@users.noreply.github.com>
This commit is contained in:
+171
-44
@@ -1,13 +1,22 @@
|
||||
--!nonstrict
|
||||
-- Screen Toolkit — main [[panel]].
|
||||
--
|
||||
-- Thin client for the toolkit: a header plus the tool grid grouped into
|
||||
-- Capture / Annotate / Record sections. It only dispatches actions to the
|
||||
-- [[service]] through the "command" state channel. Capture tools close the
|
||||
-- panel first so the capture never includes it.
|
||||
-- Thin client for the toolkit. Two layouts are provided, selected by the
|
||||
-- "panel-mode" plugin setting:
|
||||
-- * compact — a dense grid grouped into tinted sections (default)
|
||||
-- * full — the original spacious layout with section titles
|
||||
-- The panel entry to open (and thus its host-owned size) is chosen by the
|
||||
-- bar [[widget]] / control-center [[shortcut]] from the same setting.
|
||||
-- It only dispatches actions to the [[service]] through the "command"
|
||||
-- state channel. Capture tools close the panel first so the capture never
|
||||
-- includes it.
|
||||
|
||||
local panelOpen = false
|
||||
local recording = noctalia.state.get("recording") == true
|
||||
local hoveredTool = nil
|
||||
local hoveredClose = false
|
||||
local hoveredStop = false
|
||||
local render
|
||||
|
||||
-- ── Helpers ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -20,6 +29,10 @@ local function send(action, payload)
|
||||
noctalia.state.set("command", { action = action, payload = payload })
|
||||
end
|
||||
|
||||
local function getMode()
|
||||
return noctalia.getConfig("panel-mode") == "full" and "full" or "compact"
|
||||
end
|
||||
|
||||
-- Capture tools must not see the panel in the frame, so close it first.
|
||||
local CAPTURE_ACTIONS = {
|
||||
colorPicker = true,
|
||||
@@ -44,28 +57,7 @@ local function runTool(action)
|
||||
send(action)
|
||||
end
|
||||
|
||||
-- ── Header ─────────────────────────────────────────────────────────────────
|
||||
|
||||
local function header()
|
||||
local actions = {
|
||||
ui.button({ glyph = "close", onClick = function() panel.close() end }),
|
||||
}
|
||||
if recording then
|
||||
table.insert(actions, 1, ui.button({
|
||||
glyph = "square",
|
||||
variant = "destructive",
|
||||
controlSize = "sm",
|
||||
tooltip = tr("panel.stop"),
|
||||
onClick = function() send("recordStop") end,
|
||||
}))
|
||||
end
|
||||
return ui.row({ align = "center", gap = 8 }, {
|
||||
ui.label({ text = tr("panel.title"), flexGrow = 1, fontSize = 16, fontWeight = "bold", color = "on_surface" }),
|
||||
ui.row({ gap = 4 }, actions),
|
||||
})
|
||||
end
|
||||
|
||||
-- ── Tool grid ──────────────────────────────────────────────────────────────
|
||||
-- ── Tool data ──────────────────────────────────────────────────────────────
|
||||
|
||||
local TOOLS = {
|
||||
{ action = "colorPicker", label_key = "tools.colorpicker", glyph = "palette" },
|
||||
@@ -89,30 +81,155 @@ local RECORD_TOOLS = {
|
||||
{ action = "recordFullscreenMp4", label_key = "tools.record_fullscreen_mp4", glyph = "video" },
|
||||
}
|
||||
|
||||
local function toolTile(t)
|
||||
-- ── Compact mode ───────────────────────────────────────────────────────────
|
||||
|
||||
local function compactHeader()
|
||||
local actions = {
|
||||
ui.column({
|
||||
key = "close-button",
|
||||
width = 24,
|
||||
height = 24,
|
||||
radius = 9,
|
||||
fill = hoveredClose and "primary/0.12" or nil,
|
||||
border = hoveredClose and "primary" or "outline",
|
||||
borderWidth = 1,
|
||||
align = "center",
|
||||
justify = "center",
|
||||
onClick = function() panel.close() end,
|
||||
onHover = function(state)
|
||||
hoveredClose = state == "true"
|
||||
render()
|
||||
end,
|
||||
}, {
|
||||
ui.glyph({ name = "close", size = 14, color = hoveredClose and "on_surface" or "on_surface_variant" }),
|
||||
}),
|
||||
}
|
||||
if recording then
|
||||
table.insert(actions, 1, ui.column({
|
||||
key = "stop-button",
|
||||
width = 24,
|
||||
height = 24,
|
||||
radius = 9,
|
||||
fill = "error",
|
||||
border = hoveredStop and "on_surface" or nil,
|
||||
borderWidth = hoveredStop and 2 or 0,
|
||||
align = "center",
|
||||
justify = "center",
|
||||
tooltip = tr("panel.stop"),
|
||||
onClick = function() send("recordStop") end,
|
||||
onHover = function(state)
|
||||
hoveredStop = state == "true"
|
||||
render()
|
||||
end,
|
||||
}, {
|
||||
ui.glyph({ name = "square", size = 12, color = "on_surface" }),
|
||||
}))
|
||||
end
|
||||
return ui.row({ align = "center", gap = 6 }, {
|
||||
ui.glyph({ name = "crosshair", size = 16, color = "primary" }),
|
||||
ui.label({ text = tr("panel.title"), flexGrow = 1, fontSize = 16, fontWeight = "bold", color = "on_surface" }),
|
||||
ui.row({ gap = 4 }, actions),
|
||||
})
|
||||
end
|
||||
|
||||
local function compactTile(t)
|
||||
local color = hoveredTool == t.action and "primary" or "on_surface"
|
||||
return ui.column({
|
||||
key = "tile-" .. t.action,
|
||||
flexGrow = 1,
|
||||
gap = 2,
|
||||
align = "center",
|
||||
}, {
|
||||
ui.column({
|
||||
gap = 2,
|
||||
align = "center",
|
||||
onClick = function() runTool(t.action) end,
|
||||
onHover = function(state)
|
||||
hoveredTool = (state == "true") and t.action or nil
|
||||
render()
|
||||
end,
|
||||
}, {
|
||||
ui.column({ width = 30, height = 30, radius = 9, fill = "on_primary", border = color == "primary" and "primary" or "on_primary", borderWidth = 1, align = "center", justify = "center" }, {
|
||||
ui.glyph({ name = t.glyph, size = 16, color = color }),
|
||||
}),
|
||||
ui.label({ text = tr(t.label_key), maxWidth = 100, maxLines = 1, textAlign = "center", fontSize = 11, color = color }),
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
local function compactToolsRow(tools)
|
||||
local row = {}
|
||||
for _, t in ipairs(tools) do
|
||||
row[#row + 1] = compactTile(t)
|
||||
end
|
||||
return ui.row({ flexGrow = 1, gap = 2 }, row)
|
||||
end
|
||||
|
||||
local function compactSection(tools)
|
||||
return ui.column({ gap = 4, paddingV = 8, paddingH = 6, radius = 12, fill = "surface_variant" }, {
|
||||
compactToolsRow(tools),
|
||||
})
|
||||
end
|
||||
|
||||
local function compactGrid()
|
||||
return ui.column({ gap = 6 }, {
|
||||
compactSection(TOOLS),
|
||||
compactSection(ANNOTATE_TOOLS),
|
||||
compactSection(RECORD_TOOLS),
|
||||
})
|
||||
end
|
||||
|
||||
-- ── Full mode ──────────────────────────────────────────────────────────────
|
||||
|
||||
local function fullHeader()
|
||||
local actions = {
|
||||
ui.button({ glyph = "close", onClick = function() panel.close() end }),
|
||||
}
|
||||
if recording then
|
||||
table.insert(actions, 1, ui.button({
|
||||
glyph = "square",
|
||||
variant = "destructive",
|
||||
controlSize = "sm",
|
||||
tooltip = tr("panel.stop"),
|
||||
onClick = function() send("recordStop") end,
|
||||
}))
|
||||
end
|
||||
return ui.row({ align = "center", gap = 10 }, {
|
||||
ui.glyph({ name = "crosshair", size = 24, color = "primary" }),
|
||||
ui.label({ text = tr("panel.title"), flexGrow = 1, fontSize = 22, fontWeight = "bold", color = "on_surface" }),
|
||||
ui.row({ gap = 4 }, actions),
|
||||
})
|
||||
end
|
||||
|
||||
local function fullTile(t)
|
||||
local color = hoveredTool == t.action and "primary" or "on_surface"
|
||||
return ui.column({
|
||||
key = "tile-" .. t.action,
|
||||
flexGrow = 1,
|
||||
gap = 6,
|
||||
align = "center",
|
||||
paddingV = 6,
|
||||
radius = 10,
|
||||
fill = "surface_variant/0.28",
|
||||
radius = 12,
|
||||
fill = "surface_variant",
|
||||
borderWidth = 1,
|
||||
border = "outline",
|
||||
border = color == "primary" and "primary" or "outline",
|
||||
onClick = function() runTool(t.action) end,
|
||||
onHover = function(state)
|
||||
hoveredTool = (state == "true") and t.action or nil
|
||||
render()
|
||||
end,
|
||||
}, {
|
||||
ui.column({ width = 30, height = 30, radius = 9, fill = "primary/0.12", align = "center", justify = "center" }, {
|
||||
ui.glyph({ name = t.glyph, size = 16, color = "primary" }),
|
||||
ui.column({ width = 30, height = 30, radius = 9, fill = "on_primary", border = color == "primary" and "primary" or "on_primary", borderWidth = 1, align = "center", justify = "center" }, {
|
||||
ui.glyph({ name = t.glyph, size = 16, color = color }),
|
||||
}),
|
||||
ui.label({ text = tr(t.label_key), maxWidth = 100, maxLines = 1, textAlign = "center", fontSize = 11, color = "on_surface_variant" }),
|
||||
ui.label({ text = tr(t.label_key), maxWidth = 100, maxLines = 1, textAlign = "center", fontSize = 11, color = color }),
|
||||
})
|
||||
end
|
||||
|
||||
local function toolsRow(tools)
|
||||
local function fullToolsRow(tools)
|
||||
local row = {}
|
||||
for _, t in ipairs(tools) do
|
||||
row[#row + 1] = toolTile(t)
|
||||
row[#row + 1] = fullTile(t)
|
||||
end
|
||||
return ui.row({ flexGrow = 1, gap = 6 }, row)
|
||||
end
|
||||
@@ -121,30 +238,40 @@ local function sectionLabel(text)
|
||||
return ui.label({ text = text, fontSize = 11, fontWeight = "bold", color = "on_surface_variant" })
|
||||
end
|
||||
|
||||
local function toolGrid()
|
||||
local function fullGrid()
|
||||
return ui.column({ flexGrow = 1, gap = 4, justify = "space_between" }, {
|
||||
sectionLabel(tr("panel.section_capture")),
|
||||
toolsRow(TOOLS),
|
||||
fullToolsRow(TOOLS),
|
||||
sectionLabel(tr("panel.section_annotate")),
|
||||
toolsRow(ANNOTATE_TOOLS),
|
||||
fullToolsRow(ANNOTATE_TOOLS),
|
||||
sectionLabel(tr("panel.section_record")),
|
||||
toolsRow(RECORD_TOOLS),
|
||||
fullToolsRow(RECORD_TOOLS),
|
||||
})
|
||||
end
|
||||
|
||||
-- ── Main render ────────────────────────────────────────────────────────────
|
||||
|
||||
local function render()
|
||||
panel.render(ui.column({ flexGrow = 1, gap = 10 }, {
|
||||
header(),
|
||||
toolGrid(),
|
||||
}))
|
||||
render = function()
|
||||
local body
|
||||
if getMode() == "full" then
|
||||
body = ui.column({ flexGrow = 1, gap = 10 }, {
|
||||
fullHeader(),
|
||||
fullGrid(),
|
||||
})
|
||||
else
|
||||
body = ui.column({ flexGrow = 1, gap = 6 }, {
|
||||
compactHeader(),
|
||||
compactGrid(),
|
||||
})
|
||||
end
|
||||
panel.render(body)
|
||||
end
|
||||
|
||||
-- ── Lifecycle ──────────────────────────────────────────────────────────────
|
||||
|
||||
function onOpen(_context)
|
||||
panelOpen = true
|
||||
hoveredTool = nil
|
||||
render()
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user