reserved texpos range

develop
shevernitskiy 2023-09-01 18:12:59 +03:00
parent 2b11baa35f
commit 6f26650255
7 changed files with 222 additions and 119 deletions

@ -2595,10 +2595,12 @@ The ``textures`` module solves this problem by providing a stable handle instead
raw ``texpos``. When we need to draw a particular tile, we can look up the current
``texpos`` value via the handle.
* ``loadTileset(file, tile_px_w, tile_px_h)``
* ``loadTileset(file, tile_px_w, tile_px_h, reserved?)``
Loads a tileset from the image ``file`` with give tile dimensions in pixels. The
image will be sliced in row major order. Returns an array of ``TexposHandle``.
``reserved`` is optional boolean argument, which indicates texpos range.
``true`` - reserved, ``false`` - dynamic (default).
Example usage::
@ -2611,18 +2613,22 @@ raw ``texpos``. When we need to draw a particular tile, we can look up the curre
get the ``texpos`` for your texture. ``texpos`` can change when game textures are
reset, but the handle will be the same.
* ``createTile(pixels, tile_px_w, tile_px_h)``
* ``createTile(pixels, tile_px_w, tile_px_h, reserved?)``
Create and register a new texture with the given tile dimensions and an array of
``pixels`` in row major order. Each pixel is an integer representing color in packed
RBGA format (for example, #0022FF11). Returns a ``TexposHandle``.
``reserved`` is optional boolean argument, which indicates texpos range.
``true`` - reserved, ``false`` - dynamic (default).
* ``createTileset(pixels, texture_px_w, texture_px_h, tile_px_w, tile_px_h)``
* ``createTileset(pixels, texture_px_w, texture_px_h, tile_px_w, tile_px_h, reserved?)``
Create and register a new texture with the given texture dimensions and an array of
``pixels`` in row major order. Then slice it into tiles with the given tile
dimensions. Each pixel is an integer representing color in packed RBGA format (for
example #0022FF11). Returns an array of ``TexposHandle``.
``reserved`` is optional boolean argument, which indicates texpos range.
``true`` - reserved, ``false`` - dynamic (default).
* ``deleteHandle(handle)``

@ -1760,7 +1760,8 @@ static int textures_loadTileset(lua_State *state)
std::string file = luaL_checkstring(state, 1);
auto tile_w = luaL_checkint(state, 2);
auto tile_h = luaL_checkint(state, 3);
auto handles = Textures::loadTileset(file, tile_w, tile_h);
bool reserved = lua_isboolean(state, 4) ? lua_toboolean(state, 4) : false;
auto handles = Textures::loadTileset(file, tile_w, tile_h, reserved);
Lua::PushVector(state, handles);
return 1;
}
@ -1798,7 +1799,8 @@ static int textures_createTile(lua_State *state)
Lua::GetVector(state, pixels);
auto tile_w = luaL_checkint(state, 2);
auto tile_h = luaL_checkint(state, 3);
auto handle = Textures::createTile(pixels, tile_w, tile_h);
bool reserved = lua_isboolean(state, 4) ? lua_toboolean(state, 4) : false;
auto handle = Textures::createTile(pixels, tile_w, tile_h, reserved);
Lua::Push(state, handle);
return 1;
}
@ -1811,7 +1813,8 @@ static int textures_createTileset(lua_State *state)
auto texture_h = luaL_checkint(state, 3);
auto tile_w = luaL_checkint(state, 4);
auto tile_h = luaL_checkint(state, 5);
auto handles = Textures::createTileset(pixels, texture_w, texture_h, tile_w, tile_h);
bool reserved = lua_isboolean(state, 6) ? lua_toboolean(state, 6) : false;
auto handles = Textures::createTileset(pixels, texture_w, texture_h, tile_w, tile_h, reserved);
Lua::PushVector(state, handles);
return 1;
}

@ -26,7 +26,7 @@ const uint32_t TILE_HEIGHT_PX = 12;
* Load texture and get handle.
* Keep it to obtain valid texpos.
*/
DFHACK_EXPORT TexposHandle loadTexture(SDL_Surface* surface);
DFHACK_EXPORT TexposHandle loadTexture(SDL_Surface* surface, bool reserved = false);
/**
* Load tileset from image file.
@ -34,7 +34,8 @@ DFHACK_EXPORT TexposHandle loadTexture(SDL_Surface* surface);
*/
DFHACK_EXPORT std::vector<TexposHandle> loadTileset(const std::string& file,
int tile_px_w = TILE_WIDTH_PX,
int tile_px_h = TILE_HEIGHT_PX);
int tile_px_h = TILE_HEIGHT_PX,
bool reserved = false);
/**
* Get texpos by handle.
@ -53,7 +54,7 @@ DFHACK_EXPORT void deleteHandle(TexposHandle handle);
* Register this texture and return TexposHandle.
*/
DFHACK_EXPORT TexposHandle createTile(std::vector<uint32_t>& pixels, int tile_px_w = TILE_WIDTH_PX,
int tile_px_h = TILE_HEIGHT_PX);
int tile_px_h = TILE_HEIGHT_PX, bool reserved = false);
/**
* Create new textures as tileset with RGBA32 format and pixels as data in row major order.
@ -62,7 +63,8 @@ DFHACK_EXPORT TexposHandle createTile(std::vector<uint32_t>& pixels, int tile_px
DFHACK_EXPORT std::vector<TexposHandle> createTileset(std::vector<uint32_t>& pixels,
int texture_px_w, int texture_px_h,
int tile_px_w = TILE_WIDTH_PX,
int tile_px_h = TILE_HEIGHT_PX);
int tile_px_h = TILE_HEIGHT_PX,
bool reserved = false);
/**
* Call this on DFHack init just once to setup interposed handlers and

@ -6,18 +6,18 @@ local _ENV = mkmodule('gui.textures')
-- Preloaded DFHack Assets.
-- Use this handles if you need to get dfhack standard textures.
---@type table<string, TexposHandle>
---@type table<string, TexposHandle[]>
local texpos_handles = {
green_pin = dfhack.textures.loadTileset('hack/data/art/green-pin.png', 8, 12),
red_pin = dfhack.textures.loadTileset('hack/data/art/red-pin.png', 8, 12),
icons = dfhack.textures.loadTileset('hack/data/art/icons.png', 8, 12),
on_off = dfhack.textures.loadTileset('hack/data/art/on-off.png', 8, 12),
control_panel = dfhack.textures.loadTileset('hack/data/art/control-panel.png', 8, 12),
border_thin = dfhack.textures.loadTileset('hack/data/art/border-thin.png', 8, 12),
border_medium = dfhack.textures.loadTileset('hack/data/art/border-medium.png', 8, 12),
border_bold = dfhack.textures.loadTileset('hack/data/art/border-bold.png', 8, 12),
border_panel = dfhack.textures.loadTileset('hack/data/art/border-panel.png', 8, 12),
border_window = dfhack.textures.loadTileset('hack/data/art/border-window.png', 8, 12),
green_pin = dfhack.textures.loadTileset('hack/data/art/green-pin.png', 8, 12, true),
red_pin = dfhack.textures.loadTileset('hack/data/art/red-pin.png', 8, 12, true),
icons = dfhack.textures.loadTileset('hack/data/art/icons.png', 8, 12, true),
on_off = dfhack.textures.loadTileset('hack/data/art/on-off.png', 8, 12, true),
control_panel = dfhack.textures.loadTileset('hack/data/art/control-panel.png', 8, 12, true),
border_thin = dfhack.textures.loadTileset('hack/data/art/border-thin.png', 8, 12, true),
border_medium = dfhack.textures.loadTileset('hack/data/art/border-medium.png', 8, 12, true),
border_bold = dfhack.textures.loadTileset('hack/data/art/border-bold.png', 8, 12, true),
border_panel = dfhack.textures.loadTileset('hack/data/art/border-panel.png', 8, 12, true),
border_window = dfhack.textures.loadTileset('hack/data/art/border-window.png', 8, 12, true),
}
-- Get valid texpos for preloaded texture in tileset

@ -1,3 +1,4 @@
#include <atomic>
#include <mutex>
#include <numeric>
#include <unordered_map>
@ -29,8 +30,30 @@ DBG_DECLARE(core, textures, DebugCategory::LINFO);
}
static std::unordered_map<TexposHandle, long> g_handle_to_texpos;
static std::unordered_map<TexposHandle, long> g_handle_to_reserved_texpos;
static std::unordered_map<TexposHandle, SDL_Surface*> g_handle_to_surface;
static std::unordered_map<std::string, std::vector<TexposHandle>> g_tileset_to_handles;
static std::mutex g_adding_mutex;
static std::atomic<bool> loading_state = false;
struct Reserved {
static void init(int32_t start) {
reserved_range.start = start;
reserved_range.end = start + Reserved::size;
reserved_range.current = start;
}
static long get_new_texpos() {
if (reserved_range.current == reserved_range.end)
return -1;
current = reserved_range.current;
reserved_range.current++;
return current;
}
static const int32_t size = 10000; // size of reserved texpos buffer
inline static int32_t start = -1;
inline static int32_t end = -1;
inline static long current = -1;
} reserved_range;
// Converts an arbitrary Surface to something like the display format
// (32-bit RGBA), and converts magenta to transparency if convert_magenta is set
@ -71,6 +94,12 @@ static long add_texture(SDL_Surface* surface) {
return texpos;
}
// register surface in texture raws to specific texpos, returns a texpos
static void insert_texture(SDL_Surface* surface, long texpos) {
std::lock_guard<std::mutex> lg_add_texture(g_adding_mutex);
enabler->textures.raws[texpos] = surface;
}
// delete surface from texture raws
static void delete_texture(long texpos) {
std::lock_guard<std::mutex> lg_add_texture(g_adding_mutex);
@ -94,7 +123,8 @@ SDL_Surface* create_texture(std::vector<uint32_t>& pixels, int texture_px_w, int
// convert single surface into tiles according w/h
// register tiles in texture raws and return handles
std::vector<TexposHandle> slice_tileset(SDL_Surface* surface, int tile_px_w, int tile_px_h) {
std::vector<TexposHandle> slice_tileset(SDL_Surface* surface, int tile_px_w, int tile_px_h,
bool reserved) {
std::vector<TexposHandle> handles{};
if (!surface)
return handles;
@ -109,7 +139,7 @@ std::vector<TexposHandle> slice_tileset(SDL_Surface* surface, int tile_px_w, int
surface->format->Bmask, surface->format->Amask);
SDL_Rect vp{tile_px_w * x, tile_px_h * y, tile_px_w, tile_px_h};
DFSDL_UpperBlit(surface, &vp, tile, NULL);
auto handle = Textures::loadTexture(tile);
auto handle = Textures::loadTexture(tile, reserved);
handles.push_back(handle);
}
}
@ -118,22 +148,38 @@ std::vector<TexposHandle> slice_tileset(SDL_Surface* surface, int tile_px_w, int
return handles;
}
TexposHandle Textures::loadTexture(SDL_Surface* surface) {
TexposHandle Textures::loadTexture(SDL_Surface* surface, bool reserved) {
if (!surface || !enabler)
return 0; // should be some error, i guess
if (loading_state) {
ERR(textures).printerr("unable to load texture during game loading\n");
return 0;
}
auto handle = reinterpret_cast<uintptr_t>(surface);
g_handle_to_surface.emplace(handle, surface);
surface->refcount++; // prevent destruct on next FreeSurface by game
if (reserved) {
auto texpos = reserved_range.get_new_texpos();
if (texpos == -1) {
ERR(textures).printerr("reserved range limit has been reached, use dynamic range\n");
return 0;
}
insert_texture(surface, texpos);
g_handle_to_reserved_texpos.emplace(handle, texpos);
} else {
auto texpos = add_texture(surface);
g_handle_to_texpos.emplace(handle, texpos);
}
return handle;
}
std::vector<TexposHandle> Textures::loadTileset(const std::string& file, int tile_px_w,
int tile_px_h) {
int tile_px_h, bool reserved) {
if (!enabler)
return std::vector<TexposHandle>{};
if (g_tileset_to_handles.contains(file))
return g_tileset_to_handles[file];
SDL_Surface* surface = DFIMG_Load(file.c_str());
if (!surface) {
@ -142,10 +188,12 @@ std::vector<TexposHandle> Textures::loadTileset(const std::string& file, int til
}
surface = canonicalize_format(surface);
auto handles = slice_tileset(surface, tile_px_w, tile_px_h);
auto handles = slice_tileset(surface, tile_px_w, tile_px_h, reserved);
DEBUG(textures).print("loaded %zd textures from '%s'\n", handles.size(), file.c_str());
DEBUG(textures).print("loaded %zd textures from '%s' to %s range\n", handles.size(),
file.c_str(), reserved ? "reserved" : "dynamic");
g_tileset_to_handles[file] = handles;
return handles;
}
@ -153,10 +201,15 @@ long Textures::getTexposByHandle(TexposHandle handle) {
if (!handle || !enabler)
return -1;
if (g_handle_to_reserved_texpos.contains(handle))
return g_handle_to_reserved_texpos[handle];
if (g_handle_to_texpos.contains(handle))
return g_handle_to_texpos[handle];
if (g_handle_to_surface.contains(handle)) {
if (loading_state) {
ERR(textures).printerr("unable reinit texture from dynamic range during loading\n");
return -1;
}
g_handle_to_surface[handle]->refcount++; // prevent destruct on next FreeSurface by game
auto texpos = add_texture(g_handle_to_surface[handle]);
g_handle_to_texpos.emplace(handle, texpos);
@ -166,22 +219,24 @@ long Textures::getTexposByHandle(TexposHandle handle) {
return -1;
}
TexposHandle Textures::createTile(std::vector<uint32_t>& pixels, int tile_px_w, int tile_px_h) {
TexposHandle Textures::createTile(std::vector<uint32_t>& pixels, int tile_px_w, int tile_px_h,
bool reserved) {
if (!enabler)
return 0;
auto texture = create_texture(pixels, tile_px_w, tile_px_h);
auto handle = Textures::loadTexture(texture);
auto handle = Textures::loadTexture(texture, reserved);
return handle;
}
std::vector<TexposHandle> Textures::createTileset(std::vector<uint32_t>& pixels, int texture_px_w,
int texture_px_h, int tile_px_w, int tile_px_h) {
int texture_px_h, int tile_px_w, int tile_px_h,
bool reserved) {
if (!enabler)
return std::vector<TexposHandle>{};
auto texture = create_texture(pixels, texture_px_w, texture_px_h);
auto handles = slice_tileset(texture, tile_px_w, tile_px_h);
auto handles = slice_tileset(texture, tile_px_w, tile_px_h, reserved);
return handles;
}
@ -192,6 +247,8 @@ void Textures::deleteHandle(TexposHandle handle) {
auto texpos = Textures::getTexposByHandle(handle);
if (texpos > 0)
delete_texture(texpos);
if (g_handle_to_reserved_texpos.contains(handle))
g_handle_to_reserved_texpos.erase(handle);
if (g_handle_to_texpos.contains(handle))
g_handle_to_texpos.erase(handle);
if (g_handle_to_surface.contains(handle)) {
@ -207,7 +264,18 @@ static void reset_texpos() {
g_handle_to_texpos.clear();
}
static void reset_reserved_texpos() {
DEBUG(textures).print("resetting reserved texture mappings\n");
g_handle_to_reserved_texpos.clear();
}
static void reset_tilesets() {
DEBUG(textures).print("resetting tileset to handle mappings\n");
g_tileset_to_handles.clear();
}
static void reset_surface() {
DEBUG(textures).print("deleting cached surfaces\n");
for (auto& entry : g_handle_to_surface) {
DFSDL_FreeSurface(entry.second);
}
@ -220,7 +288,9 @@ struct tracking_stage_new_region : df::viewscreen_new_regionst {
DEFINE_VMETHOD_INTERPOSE(void, logic, ()) {
if (this->m_raw_load_stage != this->raw_load_stage) {
TRACE(textures).print("raw_load_stage %d -> %d\n", this->m_raw_load_stage, this->raw_load_stage);
TRACE(textures).print("raw_load_stage %d -> %d\n", this->m_raw_load_stage,
this->raw_load_stage);
loading_state = this->raw_load_stage >= 0 && this->raw_load_stage < 3 ? true : false;
this->m_raw_load_stage = this->raw_load_stage;
if (this->m_raw_load_stage == 1)
reset_texpos();
@ -240,6 +310,7 @@ struct tracking_stage_adopt_region : df::viewscreen_adopt_regionst {
DEFINE_VMETHOD_INTERPOSE(void, logic, ()) {
if (this->m_cur_step != this->cur_step) {
TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step);
loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false;
this->m_cur_step = this->cur_step;
if (this->m_cur_step == 1)
reset_texpos();
@ -259,6 +330,7 @@ struct tracking_stage_load_region : df::viewscreen_loadgamest {
DEFINE_VMETHOD_INTERPOSE(void, logic, ()) {
if (this->m_cur_step != this->cur_step) {
TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step);
loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false;
this->m_cur_step = this->cur_step;
if (this->m_cur_step == 1)
reset_texpos();
@ -278,6 +350,7 @@ struct tracking_stage_new_arena : df::viewscreen_new_arenast {
DEFINE_VMETHOD_INTERPOSE(void, logic, ()) {
if (this->m_cur_step != this->cur_step) {
TRACE(textures).print("step %d -> %d\n", this->m_cur_step, this->cur_step);
loading_state = this->cur_step >= 0 && this->cur_step < 3 ? true : false;
this->m_cur_step = this->cur_step;
if (this->m_cur_step == 0)
reset_texpos();
@ -304,12 +377,25 @@ static void uninstall_reset_point() {
INTERPOSE_HOOK(tracking_stage_new_arena, logic).remove();
}
static void reserve_static_range() {
reserved_range.init(enabler->textures.init_texture_size);
auto dummy_surface =
DFSDL_CreateRGBSurfaceWithFormat(0, 0, 0, 32, SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGBA32);
for (int32_t i = 0; i < Reserved::size; i++) {
add_texture(dummy_surface);
}
enabler->textures.init_texture_size += Reserved::size;
}
void Textures::init(color_ostream& out) {
if (!enabler)
return;
reserve_static_range();
install_reset_point();
DEBUG(textures, out).print("dynamic texture loading ready");
DEBUG(textures, out)
.print("dynamic texture loading ready, reserved range %d-%d\n", reserved_range.start,
reserved_range.end);
}
void Textures::cleanup() {
@ -317,6 +403,8 @@ void Textures::cleanup() {
return;
reset_texpos();
reset_reserved_texpos();
reset_tilesets();
reset_surface();
uninstall_reset_point();
}

@ -5,8 +5,8 @@ local helpdb = require('helpdb')
local overlay = require('plugins.overlay')
local widgets = require('gui.widgets')
local logo_textures = dfhack.textures.loadTileset('hack/data/art/logo.png', 8, 12)
local logo_hovered_textures = dfhack.textures.loadTileset('hack/data/art/logo_hovered.png', 8, 12)
local logo_textures = dfhack.textures.loadTileset('hack/data/art/logo.png', 8, 12, true)
local logo_hovered_textures = dfhack.textures.loadTileset('hack/data/art/logo_hovered.png', 8, 12, true)
local function get_command(cmdline)
local first_word = cmdline:trim():split(' +')[1]
@ -26,11 +26,11 @@ end
-- ----------------- --
HotspotMenuWidget = defclass(HotspotMenuWidget, overlay.OverlayWidget)
HotspotMenuWidget.ATTRS{
default_pos={x=5,y=1},
default_enabled=true,
version=2,
viewscreens={
HotspotMenuWidget.ATTRS {
default_pos = { x = 5, y = 1 },
default_enabled = true,
version = 2,
viewscreens = {
'adopt_region',
'choose_game_type',
-- 'choose_start_site', -- conflicts with vanilla panel layouts
@ -48,51 +48,51 @@ HotspotMenuWidget.ATTRS{
'update_region',
'world'
},
frame={w=4, h=3}
frame = { w = 4, h = 3 }
}
function HotspotMenuWidget:init()
local to_pen = dfhack.pen.parse
local function tp(idx, ch)
return to_pen{
tile=function() return dfhack.textures.getTexposByHandle(logo_textures[idx]) end,
ch=ch,
fg=COLOR_GREY,
return to_pen {
tile = function() return dfhack.textures.getTexposByHandle(logo_textures[idx]) end,
ch = ch,
fg = COLOR_GREY,
}
end
local function tph(idx, ch)
return to_pen{
tile=function() return dfhack.textures.getTexposByHandle(logo_hovered_textures[idx]) end,
ch=ch,
fg=COLOR_WHITE,
return to_pen {
tile = function() return dfhack.textures.getTexposByHandle(logo_hovered_textures[idx]) end,
ch = ch,
fg = COLOR_WHITE,
}
end
local function get_tile_token(idx, ch)
return {
tile=tp(idx, ch),
htile=tph(idx, ch),
width=1,
tile = tp(idx, ch),
htile = tph(idx, ch),
width = 1,
}
end
self:addviews{
widgets.Label{
text={
self:addviews {
widgets.Label {
text = {
get_tile_token(1, '!'), get_tile_token(2, 'D'), get_tile_token(3, 'F'), get_tile_token(4, '!'), NEWLINE,
get_tile_token(5, '!'), get_tile_token(6, 'H'), get_tile_token(7, 'a'), get_tile_token(8, '!'), NEWLINE,
get_tile_token(9, '!'), get_tile_token(10, 'c'), get_tile_token(11, 'k'), get_tile_token(12, '!'),
},
on_click=function() dfhack.run_command('hotkeys') end,
on_click = function() dfhack.run_command('hotkeys') end,
},
}
end
function HotspotMenuWidget:overlay_trigger()
return MenuScreen{hotspot=self}:show()
return MenuScreen { hotspot = self }:show()
end
-- register the menu hotspot with the overlay
OVERLAY_WIDGETS = {menu=HotspotMenuWidget}
OVERLAY_WIDGETS = { menu = HotspotMenuWidget }
-- ---- --
-- Menu --
@ -103,15 +103,15 @@ local MAX_LIST_WIDTH = 45
local MAX_LIST_HEIGHT = 15
Menu = defclass(Menu, widgets.Panel)
Menu.ATTRS{
hotspot=DEFAULT_NIL,
Menu.ATTRS {
hotspot = DEFAULT_NIL,
}
-- get a map from the binding string to a list of hotkey strings that all
-- point to that binding
local function get_bindings_to_hotkeys(hotkeys, bindings)
local bindings_to_hotkeys = {}
for _,hotkey in ipairs(hotkeys) do
for _, hotkey in ipairs(hotkeys) do
local binding = bindings[hotkey]
table.insert(ensure_key(bindings_to_hotkeys, binding), hotkey)
end
@ -126,17 +126,17 @@ local function get_choices(hotkeys, bindings, is_inverted)
local bindings_to_hotkeys = get_bindings_to_hotkeys(hotkeys, bindings)
-- build list choices
for _,hotkey in ipairs(hotkeys) do
for _, hotkey in ipairs(hotkeys) do
local command = bindings[hotkey]
if seen[command] then goto continue end
seen[command] = true
local hk_width, tokens = 0, {}
for _,hk in ipairs(bindings_to_hotkeys[command]) do
for _, hk in ipairs(bindings_to_hotkeys[command]) do
if hk_width ~= 0 then
table.insert(tokens, ', ')
hk_width = hk_width + 2
end
table.insert(tokens, {text=hk, pen=COLOR_LIGHTGREEN})
table.insert(tokens, { text = hk, pen = COLOR_LIGHTGREEN })
hk_width = hk_width + #hk
end
local command_str = command
@ -144,16 +144,20 @@ local function get_choices(hotkeys, bindings, is_inverted)
local max_command_len = MAX_LIST_WIDTH - hk_width - LIST_BUFFER
command_str = command:sub(1, max_command_len - 3) .. '...'
end
table.insert(tokens, 1, {text=command_str})
local choice = {icon=ARROW, command=command, text=tokens,
hk_width=hk_width}
table.insert(tokens, 1, { text = command_str })
local choice = {
icon = ARROW,
command = command,
text = tokens,
hk_width = hk_width
}
max_width = math.max(max_width, hk_width + #command_str + LIST_BUFFER)
table.insert(choices, is_inverted and 1 or #choices + 1, choice)
::continue::
end
-- adjust width of command fields so the hotkey tokens are right justified
for _,choice in ipairs(choices) do
for _, choice in ipairs(choices) do
local command_token = choice.text[1]
command_token.width = max_width - choice.hk_width - (LIST_BUFFER - 1)
end
@ -164,17 +168,17 @@ end
function Menu:init()
local hotkeys, bindings = getHotkeys()
if #hotkeys == 0 then
hotkeys = {''}
bindings = {['']='gui/launcher'}
hotkeys = { '' }
bindings = { [''] = 'gui/launcher' }
end
local is_inverted = not not self.hotspot.frame.b
local choices,list_width = get_choices(hotkeys, bindings, is_inverted)
local choices, list_width = get_choices(hotkeys, bindings, is_inverted)
list_width = math.max(35, list_width)
local list_frame = copyall(self.hotspot.frame)
local list_widget_frame = {h=math.min(#choices, MAX_LIST_HEIGHT)}
local list_widget_frame = { h = math.min(#choices, MAX_LIST_HEIGHT) }
local quickstart_frame = {}
list_frame.w = list_width + 2
list_frame.h = list_widget_frame.h + 4
@ -193,51 +197,51 @@ function Menu:init()
list_frame.r = math.max(0, list_frame.r + 5)
end
local help_frame = {w=list_frame.w, l=list_frame.l, r=list_frame.r}
local help_frame = { w = list_frame.w, l = list_frame.l, r = list_frame.r }
if list_frame.t then
help_frame.t = list_frame.t + list_frame.h
else
help_frame.b = list_frame.b + list_frame.h
end
self:addviews{
widgets.Panel{
view_id='list_panel',
frame=list_frame,
frame_style=gui.PANEL_FRAME,
frame_background=gui.CLEAR_PEN,
subviews={
widgets.List{
view_id='list',
frame=list_widget_frame,
choices=choices,
icon_width=2,
on_select=self:callback('onSelect'),
on_submit=self:callback('onSubmit'),
on_submit2=self:callback('onSubmit2'),
self:addviews {
widgets.Panel {
view_id = 'list_panel',
frame = list_frame,
frame_style = gui.PANEL_FRAME,
frame_background = gui.CLEAR_PEN,
subviews = {
widgets.List {
view_id = 'list',
frame = list_widget_frame,
choices = choices,
icon_width = 2,
on_select = self:callback('onSelect'),
on_submit = self:callback('onSubmit'),
on_submit2 = self:callback('onSubmit2'),
},
widgets.Panel{frame={h=1}},
widgets.HotkeyLabel{
frame=quickstart_frame,
label='Quickstart guide',
key='STRING_A063',
on_activate=function()
self:onSubmit(nil, {command='quickstart-guide'})
widgets.Panel { frame = { h = 1 } },
widgets.HotkeyLabel {
frame = quickstart_frame,
label = 'Quickstart guide',
key = 'STRING_A063',
on_activate = function()
self:onSubmit(nil, { command = 'quickstart-guide' })
end,
},
},
},
widgets.ResizingPanel{
view_id='help_panel',
autoarrange_subviews=true,
frame=help_frame,
frame_style=gui.PANEL_FRAME,
frame_background=gui.CLEAR_PEN,
subviews={
widgets.WrappedLabel{
view_id='help',
text_to_wrap='',
scroll_keys={},
widgets.ResizingPanel {
view_id = 'help_panel',
autoarrange_subviews = true,
frame = help_frame,
frame_style = gui.PANEL_FRAME,
frame_background = gui.CLEAR_PEN,
subviews = {
widgets.WrappedLabel {
view_id = 'help',
text_to_wrap = '',
scroll_keys = {},
},
},
},
@ -324,14 +328,14 @@ end
MenuScreen = defclass(MenuScreen, gui.ZScreen)
MenuScreen.ATTRS {
focus_path='hotkeys/menu',
initial_pause=false,
hotspot=DEFAULT_NIL,
focus_path = 'hotkeys/menu',
initial_pause = false,
hotspot = DEFAULT_NIL,
}
function MenuScreen:init()
self:addviews{
Menu{hotspot=self.hotspot},
self:addviews {
Menu { hotspot = self.hotspot },
}
end

@ -36,7 +36,7 @@ namespace DFHack {
static std::vector<TexposHandle> textures;
DFhackCExport command_result plugin_init(color_ostream &out, std::vector<PluginCommand> &commands) {
textures = Textures::loadTileset("hack/data/art/pathable.png", 32, 32);
textures = Textures::loadTileset("hack/data/art/pathable.png", 32, 32, true);
return CR_OK;
}