Added nicer window handling at startup. Initial gui protocol working well.

This commit is contained in:
dlannan 2025-10-17 19:11:10 +10:30
parent d06bcb0c7a
commit 84e0301ce5
8 changed files with 256 additions and 62 deletions

View File

@ -0,0 +1,13 @@
-- Add your panel actions here.
local sapp = require("sokol_app")
local panel_actions = {}
panel_actions["quit_app"] = function(event, element)
sapp.sapp_quit()
end
return panel_actions

View File

@ -0,0 +1,29 @@
-- All gui ui elements will follow a std format (under development)
--- Primary goal: A formal gui protocol like svg or html that allows for clear gui creation and control.
local nk = sg
local panels = {}
panels.windows = {}
panels.windows.popup = {
type = "panel",
title = "Main",
size = {600, "auto"},
pos = {"left", "top"}, -- Can be pixels or strings
color = { background = 0x000000ff, text = 0xffffffff, border = 0x888888ff },
window_flags = bit.bor(nk.NK_WINDOW_TITLE, nk.NK_WINDOW_MOVABLE),
layout = {
{
type = "row", height = 24, layout = {
{ type = "label", text = "Some text", width = 0.1 },
{ type = "button", text = "Build Project", action = "build_project", width = "auto" },
{ type = "button", text = "Export", action = "export_assets", width = 200 },
{ type = "button", text = "Quit", action = "quit_app", width = "auto" },
}
},
}
}
return panels

View File

@ -1,27 +0,0 @@
-- All gui ui elements will follow a std format (under development)
--- Primary goal: A formal gui protocol like svg or html that allows for clear gui creation and control.
local panels = {}
panels.windows = {}
panels.windows.popup = {
title = "",
size = {500, 300},
pos = { "center", "center"}, -- Can be pixels or strings
color = { background = 0x000000ff, text = 0xffffffff, border = 0x888888ff },
layout = {
{
type = "row", height = 100, children = {
{ type = "label", text = "Some text in the row", width = "auto" },
{ type = "button", label = "Build Project", action = "build_project", width = "auto" },
{ type = "button", label = "Export", action = "export_assets", width = 200 },
{ type = "button", label = "Quit", action = "quit_app", width = "auto" },
}
},
}
}
return panels

View File

@ -18,8 +18,9 @@ local utils = require("utils")
local ffi = require("ffi")
gSmgr = require("engine.utils.statemanager")
-- --------------------------------------------------------------------------------------
local engineState = require("engine.state_engine")
-- --------------------------------------------------------------------------------------
local shell32 = ffi.load("shell32")
@ -31,9 +32,6 @@ ffi.cdef[[
]]
local SW_MAXIMIZE = 3
-- --------------------------------------------------------------------------------------
local engineState = require("engine.state_engine")
-- --------------------------------------------------------------------------------------
local enabled_profile = arg[1] == "-profile"
@ -49,6 +47,7 @@ end
-- --------------------------------------------------------------------------------------
-- Grab some gui elements for testing
local SmainGui = require("editor.states.mainGui")
engineState.queue( "mainGui", SmainGui, true )
-- --------------------------------------------------------------------------------------
@ -95,25 +94,25 @@ local function init()
print("Sokol Is Valid: "..tostring(sg.sg_isvalid()))
local hwnd = sapp.sapp_win32_get_hwnd()
ffi.C.ShowWindow(hwnd, SW_MAXIMIZE)
engineState.ready = nil
mainState.ctx = nil
sapp.sapp_show_mouse(true)
sapp.sapp_set_window_title("Thunc Editor v")
sapp.sapp_set_window_title("Thunc Editor v"..PROJECT_VERSION)
-- io.popen(cmd, "r")
ffi.C.Sleep(500)
-- Add the state to the statemanager and jump to it
gSmgr:Init()
gSmgr:CreateState("MainGui", SmainGui)
gSmgr:ChangeState("MainGui")
local hwnd = sapp.sapp_win32_get_hwnd()
ffi.C.ShowWindow(hwnd, SW_MAXIMIZE)
end
-- --------------------------------------------------------------------------------------
local function input(event)
-- Only kick things off once the window is ready.
if(event.type == sapp.SAPP_EVENTTYPE_RESIZED) then
engineState.ready = true
end
engineState.input(event, {})
end
@ -128,18 +127,27 @@ local function frame()
local dt = sapp.sapp_frame_duration()
local ctx = nk.snk_new_frame()
if(mainState.ctx == nil) then
if(engineState.ready == true and mainState.ctx == nil) then
mainState.ctx = ctx
ErrorCheck( pcall( engineState.init ) )
print("----------->>> Init")
ErrorCheck( pcall( engineState.init, w, h ) )
nk.nk_style_show_cursor(ctx)
else
elseif(mainState.ctx) then
mainState.ctx = ctx
end
ErrorCheck( pcall( engineState.update, dt ) )
-- // the sokol_gfx draw pass
local pass = ffi.new("sg_pass[1]")
pass[0].action.colors[0].load_action = sg.SG_LOADACTION_CLEAR
pass[0].action.colors[0].clear_value = { 0.25, 0.5, 0.7, 1.0 }
pass[0].swapchain = slib.sglue_swapchain()
sg.sg_begin_pass(pass)
nk.snk_render(sapp.sapp_width(), sapp.sapp_height())
sg.sg_end_pass()
sg.sg_commit()
-- Display frame stats in console.
-- hutils.show_stats()
end
@ -160,6 +168,7 @@ app_desc[0].cleanup_cb = cleanup
app_desc[0].event_cb = input
app_desc[0].width = width
app_desc[0].height = height
app_desc[0].high_dpi = true
app_desc[0].window_title = "editor - sokol"
app_desc[0].fullscreen = false
-- app_desc[0].icon.sokol_default = true

View File

@ -4,6 +4,7 @@
-- Decription:
-- Runs most of the scene.
local sapp = require("sokol_app")
local smgr = require("engine.utils.statemanager")
local nk = sg
local hmm = require("hmm")
@ -32,7 +33,8 @@ local follow = require("engine.libs.camera.follow")
local socket = require("socket.core")
local copas = require("copas")
local panels = require("data.gui.widgets.panels")
local panels = require("data.gui.panels.panels")
local panelactions = require("data.gui.actions.panels")
------------------------------------------------------------------------------------------------------------
@ -49,6 +51,8 @@ function SmainGui:Init(wwidth, wheight)
-- SmenuMain:Init(wwidth, wheight)
-- SenvHandler:Init(wwidth, wheight)
print("Display: "..wwidth.." x "..wheight)
panels.windows.popup.parent = { size = { wwidth, wheight } }
end
------------------------------------------------------------------------------------------------------------
@ -93,6 +97,156 @@ end
------------------------------------------------------------------------------------------------------------
local function get_parent_size( ele )
local size = { 1920, 1440 }
-- Calc size first since this may be used in positioning (like center etc)
if(ele.size) then
if(ele.size[1]) then
if(type(ele.size[1]) == "string") then
if(ele.size[1] == "auto") then
size[1] = ele.parent.size[1] or 1920
end
else
size[1] = tonumber(ele.size[1]) or 100
end
end
if(ele.size[2]) then
if(type(ele.size[2]) == "string") then
if(ele.size[2] == "auto") then
size[2] = ele.parent.size[2] or 1440
end
else
size[2] = tonumber(ele.size[2]) or 100
end
end
end
return size
end
------------------------------------------------------------------------------------------------------------
local function calc_row_geometry( ele )
local esizes = {}
local autosize = 0
local pwidth = get_parent_size(ele)[1]
local remainwidth = pwidth
for i,v in ipairs(ele.layout) do
if(v.width and type(v.width) == "number") then
if(v.width > 0.99) then
esizes[i] = v.width / pwidth
remainwidth = remainwidth - v.width
else
esizes[i] = v.width
remainwidth = remainwidth - v.width * pwidth
end
else
esizes[i] = 0
autosize = autosize + 1
end
end
local autosize = (remainwidth / pwidth) / autosize
for i,v in ipairs(ele.layout) do
if(esizes[i] == 0) then esizes[i] = autosize end
end
return esizes
end
------------------------------------------------------------------------------------------------------------
local function get_geometry( ele, winrect )
local size = get_parent_size( ele )
winrect[0].w, winrect[0].h = size[1], size[2]
if(ele.pos) then
if(ele.pos[1]) then
if(type(ele.pos[1]) == "string") then
if(ele.pos[1] == "center") then
local pwidth = ele.parent.size[1] or 1920
winrect[0].x = pwidth * 0.5 - winrect[0].w * 0.5
elseif(ele.pos[1] == "left") then
winrect[0].x = 0.0
elseif(ele.pos[1] == "right") then
local pwidth = ele.parent.size[1] or 1920
winrect[0].x = pwidth - winrect[0].w
end
else
winrect[0].x = tonumber(ele.pos[1]) or 0
end
end
if(ele.pos[2]) then
if(type(ele.pos[2]) == "string") then
if(ele.pos[2] == "center") then
local pheight = ele.parent.size[2] or 1440
winrect[0].y = pheight * 0.5 - winrect[0].h * 0.5
elseif(ele.pos[2] == "top") then
winrect[0].y = 0.0
elseif(ele.pos[2] == "bottom") then
local pheight = ele.parent.size[2] or 1440
winrect[0].y = pheight - winrect[0].h
end
else
winrect[0].y = tonumber(ele.pos[2]) or 0
end
end
end
end
------------------------------------------------------------------------------------------------------------
local function render_element( ctx, element )
if(element.type == "panel") then
element.winrect = element.winrect or ffi.new("struct nk_rect[1]", {{0, 0, 400, 600}})
get_geometry(element, element.winrect)
if (nk.nk_begin(ctx, element.title or "", element.winrect[0], element.window_flags or 0) == true) then
if(element.layout) then
for i,v in ipairs(element.layout) do
v.parent = element
render_element(ctx, v)
end
end
end
nk.nk_end(ctx)
elseif(element.type == "row") then
local row_height = 30
local children = 1
if(element.height) then row_height = tonumber(element.height) end
if(element.layout) then children = #element.layout end
nk.nk_layout_row_begin(ctx, nk.NK_DYNAMIC, row_height, children)
if(element.layout) then
local esizes = calc_row_geometry(element)
for i,v in ipairs(element.layout) do
v.parent = element
v.row_height = row_height
nk.nk_layout_row_push(ctx, esizes[i])
render_element(ctx, v)
end
end
nk.nk_layout_row_end(ctx)
elseif(element.type == "label") then
local alignment = nk.NK_TEXT_LEFT
if(element.align) then alignment = ALIGNMENT[element.align] or alignment end
nk.nk_label(ctx, element.text or "", alignment)
elseif(element.type == "button") then
local styled_button = ffi.new("struct nk_style_button[1]", { ctx[0].style.button})
-- Override rounded corners (can do other things here too - can also make your own button style)
styled_button[0].rounding = 0.0
if( nk.nk_button_label_styled(ctx, styled_button, element.text or "Missing Text")) then
-- if (nk.nk_button_label(ctx, element.text or "Missing Text")) then
if(element.action) then
local action = panelactions[element.action]
if(action) then action("button", element) end
end
end
end
end
------------------------------------------------------------------------------------------------------------
function SmainGui:Update(mxi, myi, buttons)
--nkgui:update()
@ -103,7 +257,7 @@ function SmainGui:Update(mxi, myi, buttons)
-- SmenuMain:Update(mxi, myi, buttons)
-- SenvHandler:Update(mxi, myi, buttons)
render_element(mainState.ctx, panels.windows.popup)
--nkgui:render()
self.prevmxi = mxi

View File

@ -45,7 +45,7 @@ end
------------------------------------------------------------------------------------------------------------
function mainState:Begin()
Sassets:Begin()
-- Sassets:Begin()
-- bins.bin_add_func(bins.BTYPE_OPAQUE, RenderNuklear)
end

View File

@ -12,6 +12,7 @@ print("PROJECT_VERSION: "..PROJECT_VERSION)
------------------------------------------------------------------------------------------------------------
local sapp = require("sokol_app")
local tinsert = table.insert
------------------------------------------------------------------------------------------------------------
-- Global because states need to use it themselves
@ -26,6 +27,7 @@ local defaults = {
}
engineState = {}
engineState.queued = {}
engineState.events = defaults
engineState.sm = require("engine.utils.statemanager")
@ -35,31 +37,45 @@ engineState.sm = require("engine.utils.statemanager")
local mainState = require("engine.app.state_main")
local SengineRunner = require("engine.app.states.engineRunner")
-- local SgeomModels = require("engine.app.states.geometry.models")
------------------------------------------------------------------------------------------------------------
-- Queue a state for execution from beginning
---- States
-- local SmainBG = require("engine.app.states.mainBG")
engineState.queue = function( name, state, startup )
if(name == nil or state == nil) then return end
tinsert(engineState.queued, { name = name, state = state, startup = startup } )
end
------------------------------------------------------------------------------------------------------------
-- Register every state with the statemanager.
engineState.init = function( )
engineState.init = function( w, h )
engineState.sm:Init()
-- engineState.sm:CreateState("MainBG", SmainBG)
engineState.sm:CreateState("EngineRunner", SengineRunner)
-- engineState.sm:CreateState("MainState", mainState)
engineState.sm:CreateState("MainState", mainState)
-- engineState.sm:CreateState("GeomModels", SgeomModels)
engineState.sm:AddSibling("MainState", "EngineRunner")
-- engineState.sm:AddSibling("MainState", "EngineRunner")
-- Add any queued states
local startup = nil
for i, v in ipairs(engineState.queued) do
engineState.sm:CreateState(v.name, v.state)
if(v.startup) then startup = v.name end
v.state:Init(w, h)
end
-- Init some modules ready for kick off.
mainState:Init(sapp.sapp_widthf(), sapp.sapp_heightf())
SengineRunner:Init(sapp.sapp_widthf(), sapp.sapp_heightf())
mainState:Init(w, h)
SengineRunner:Init(w, h)
-- This kicks things off!
engineState.sm:ChangeState("EngineRunner")
if(startup) then
engineState.sm:AddSibling(startup, "EngineRunner")
end
end
------------------------------------------------------------------------------------------------------------

View File

@ -1448,8 +1448,8 @@ local function draw_demo_ui(ctx)
end
nk.nk_tree_pop(ctx)
end
nk.nk_end(ctx)
end
nk.nk_end(ctx)
return not nk.nk_window_is_closed(ctx, "Overview")
end