Files
Mush-Soundpack/cosmic rage/lua/tabbed_window.lua
2025-07-01 23:28:00 +03:00

313 lines
11 KiB
Lua

-- tabbed_window.lua
--[[
Author: Nick Gammon
Date: 7th February 2018
Generic tabbed miniwindow drawer.
Exposed functions:
init (context) -- call once (from OnPluginInstall), supply:
context.win -- window ID for this tabbed window
context.tabfont -- table of font info for non-active tabs (name, size, unicode, bold, italic)
context.activefont -- table of font info for the selected tab (name, size, unicode, bold, italic)
context.titlefont -- table of font info for the title bar (name, size, unicode, bold, italic)
context.colours -- table of assorted colours (see below for examples)
context.window -- table (width and height) of the desired window size
context.tab_filler -- gap between tabs
context.can_move -- can window be dragged around?
context.active_tab -- the currently active tab
context.tabs -- table of tables - one per tab (name, handler)
draw_window (context, which_tab) -- draws the window with which_tab as the active tab
hide_window (context) -- hides this window
save_state (context) -- for saving the window position (call from OnPluginSaveState)
Exposed variables:
VERSION -- version of this module
--]]
module (..., package.seeall)
VERSION = 1.1 -- -- for querying by plugins
require "movewindow" -- load the movewindow.lua module
local default_context = {
-- window ID
win = "tabbed_window_" .. GetUniqueID (),
-- font for inactive tab text
tabfont = {
id = "fn",
name = "Lucida Console",
size = 10, -- points
unicode = false, -- Unicode?
bold = false, -- make bold?
italic = false, -- make italic?
}, -- end of tabfont
-- font for active tab text
activefont = {
id = "fa",
name = "Lucida Console",
size = 10, -- points
unicode = false, -- Unicode?
bold = false, -- make bold?
italic = false, -- make italic?
}, -- end of activefont
-- font for title text
titlefont = {
id = "ft",
name = "Lucida Console",
size = 12, -- points
unicode = false, -- Unicode?
bold = true, -- make bold?
italic = false, -- make italic?
}, -- end of titlefont
colours = {
background = ColourNameToRGB ("lightskyblue"), -- entire window background
frame = ColourNameToRGB ("mediumorchid"), -- frame around window
title_bar = ColourNameToRGB ("palegoldenrod"), -- behind the title
title = ColourNameToRGB ("mediumblue"), -- title text
tab = ColourNameToRGB ("green"), -- tab background
tab_text = ColourNameToRGB ("white"), -- inactive tab text
active_tab_text = ColourNameToRGB ("lightgreen"), -- active tab text
upper_line = ColourNameToRGB ("lightgray"), -- line above tab text
lower_line = ColourNameToRGB ("lightgray"), -- line below tab text
vertical_line = ColourNameToRGB ("lightgray"), -- line between tabs
}, -- end of colours
-- miniwindow size
window = {
width = 300,
height = 200,
}, -- end of window info
-- gap between text in tabs
tab_filler = 10,
-- can it be moved?
can_move = true,
-- which tab is active
active_tab = 1,
-- tabs
-- table of tabs. For each one supply a table containing a name and a handler,
-- eg. { name = "Circle", handler = DrawCircle },
tabs = { },
} -- end of default_context
-- -------------------------------------------------------------------------------
-- init - initialize this module ready for use (do once)
-- -------------------------------------------------------------------------------
function init (context)
-- make copy of colours, sizes etc.
assert (type (context) == "table", "No 'context' table supplied to tabbed_window.")
-- force some context defaults if not supplied
for k, v in pairs (default_context) do
-- some context items are tables so copy the table entries
if type (v) == 'table' then
if type (context [k]) ~= 'table' then
assert (context [k] == nil, "tabbed window context item '" .. k .. "' should be a table or nil")
context [k] = { } -- make table if required
end -- not having a table there
for k1, v1 in pairs (v) do
context [k] [k1] = context [k] [k1] or v1
end -- for each sub-table item
else
context [k] = context [k] or v
end -- if table or not
end -- for
if next (context.tabs) == nil then
ColourNote ("orange", "", "Warning: no tabs define for tabbed window")
end -- if
-- install the window movement handler, get back the window position
context.windowinfo = movewindow.install (context.win, miniwin.pos_top_left, 0) -- default to top left
-- save a bit of typing
local windowinfo = context.windowinfo
local win = context.win
local tabfont = context.tabfont
local activefont = context.activefont
local titlefont = context.titlefont
local window = context.window
local colours = context.colours
-- make window so I can grab the font info
WindowCreate (win,
windowinfo.window_left,
windowinfo.window_top,
window.width,
window.height,
windowinfo.window_mode,
windowinfo.window_flags,
colours.background)
WindowFont (win, tabfont.id, tabfont.name, tabfont.size, tabfont.bold, tabfont.italic, false, false, 0, 0)
tabfont.height = WindowFontInfo (win, tabfont.id, 1) -- height
WindowFont (win, activefont.id, activefont.name, activefont.size, activefont.bold, activefont.italic, false, false, 0, 0)
activefont.height = WindowFontInfo (win, activefont.id, 1) -- height
WindowFont (win, titlefont.id, titlefont.name, titlefont.size, titlefont.bold, titlefont.italic, false, false, 0, 0)
titlefont.height = WindowFontInfo (win, titlefont.id, 1) -- height
context.client_bottom = context.window.height - context.tabfont.height - 8
end -- init
function save_state (context)
-- save window current location for next time
movewindow.save_state (context.win)
end -- save_state
-- -------------------------------------------------------------------------------
-- draw_window - draw the tabbed window
-- -------------------------------------------------------------------------------
function draw_window (context, whichTab)
context.active_tab = whichTab
-- save a bit of typing
local windowinfo = context.windowinfo
local win = context.win
local tabfont = context.tabfont
local activefont = context.activefont
local titlefont = context.titlefont
local window = context.window
local colours = context.colours
local tabs = context.tabs
local client_bottom = context.client_bottom
local tab_filler = context.tab_filler
-- clear window
WindowRectOp (win, miniwin.rect_fill, 0, 0, 0, 0, colours.background)
WindowDeleteAllHotspots (win)
-- draw drag bar rectangle
WindowRectOp (win, miniwin.rect_fill, 1, 1, 0, titlefont.height + 2, colours.title_bar)
-- add the drag handler so they can move the window around
if context.can_move then
movewindow.add_drag_handler (win, 0, 0, 0, titlefont.height)
end -- if can move the window
local thisTab = tabs [whichTab]
if not thisTab then
ColourNote ("orange", "", "Tab " .. whichTab .. " does not exist")
return
end -- no such tab
-- find title width so we can center it
local title_width = WindowTextWidth (win, titlefont.id, thisTab.name, titlefont.unicode)
-- draw title
WindowText(win, titlefont.id, thisTab.name, (window.width - title_width )/ 2 + 1, 1, 0, 0, colours.title, titlefont.unicode)
-- frame window
WindowRectOp (win, miniwin.rect_frame, 0, 0, 0, 0, colours.frame)
-- draw tabs
local left = 1
-- we have to make a function to handle each tabbed window, so its
-- name must include the window ID
local mouse_down_function_name = "tabbed_window_mouse_down_handler_" .. win
-- the function will have the context as a closure
getfenv () [mouse_down_function_name] = function (flags, hotspot_id)
local whichTab = string.match (hotspot_id, "^hs(%d)$")
if not whichTab then -- tab unknown for some reason
ColourNote ("orange", "", "Mousedown tab unknown " .. hotspot_id)
return
end -- if
draw_window (context, tonumber (whichTab))
end -- mouse_down_function_name
for k, v in ipairs (tabs) do
local tab_width
if k == whichTab then
tab_width = WindowTextWidth (win, activefont.id, v.name, activefont.unicode)
else
tab_width = WindowTextWidth (win, tabfont.id, v.name, tabfont.unicode)
end -- if
-- tab background
WindowRectOp (win, miniwin.rect_fill, left, client_bottom, left + tab_width + tab_filler, window.height - 1, colours.tab)
-- tab text
if k == whichTab then
WindowText(win, activefont.id, v.name, left + tab_filler / 2, client_bottom + 4, 0, 0, colours.active_tab_text, activefont.unicode)
else
WindowText(win, tabfont.id, v.name, left + tab_filler / 2, client_bottom + 4, 0, 0, colours.tab_text, tabfont.unicode)
end -- if
-- draw upper line if not active tab
if k ~= whichTab then
WindowLine(win, left, client_bottom + 2, left + tab_width + tab_filler, client_bottom + 2,
colours.upper_line, miniwin.pen_solid, 2)
end -- if not active
-- draw lower line
WindowLine(win, left, window.height - 1, left + tab_width + tab_filler, window.height - 1,
colours.lower_line, miniwin.pen_solid, 1)
-- draw vertical lines
WindowLine(win, left, client_bottom + 2, left, window.height - 1,
colours.vertical_line, miniwin.pen_solid, 1)
WindowLine(win, left + tab_width + tab_filler, client_bottom + 2, left + tab_width + tab_filler, window.height - 1,
colours.vertical_line, miniwin.pen_solid, 1)
-- now add a hotspot for this tab
WindowAddHotspot(win, "hs" .. k,
left, client_bottom, left + tab_width + tab_filler, window.height - 1, -- rectangle
"", -- mouseover
"", -- cancelmouseover
"tabbed_window." .. mouse_down_function_name,
"", -- cancelmousedown
"", -- mouseup
"Show " .. v.name .. " tab", -- tooltip text
miniwin.cursor_hand, 0) -- hand cursor
left = left + tab_width + tab_filler
end -- for each tab
-- call handler to draw rest of window
local handler = thisTab.handler
if handler then
handler (win, 1, titlefont.height + 2, window.width - 1, client_bottom, context)
else
ColourNote ("orange", "", "No tab handler for " .. thisTab.name)
end -- if
WindowShow (win, true)
end -- draw_window
-- -------------------------------------------------------------------------------
-- hide_window - hide the tabbed window
-- -------------------------------------------------------------------------------
function hide_window (context)
WindowShow (context.win, false)
end -- hide_window