Skip to content

Commit 561eeab

Browse files
committed
implement gui/spectate.lua
1 parent bb4972f commit 561eeab

File tree

1 file changed

+305
-3
lines changed

1 file changed

+305
-3
lines changed

gui/spectate.lua

Lines changed: 305 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,319 @@ local gui = require('gui')
22
local spectate = require('plugins.spectate')
33
local widgets = require('gui.widgets')
44

5+
--------------------------------------------------------------------------------
6+
--- ToggleLabel
7+
8+
-- pens are the same as gui/control-panel.lua
9+
local textures = require('gui.textures')
10+
local function get_icon_pens()
11+
local enabled_pen_left = dfhack.pen.parse{fg=COLOR_CYAN,
12+
tile=curry(textures.tp_control_panel, 1), ch=string.byte('[')}
13+
local enabled_pen_center = dfhack.pen.parse{fg=COLOR_LIGHTGREEN,
14+
tile=curry(textures.tp_control_panel, 2) or nil, ch=251} -- check
15+
local enabled_pen_right = dfhack.pen.parse{fg=COLOR_CYAN,
16+
tile=curry(textures.tp_control_panel, 3) or nil, ch=string.byte(']')}
17+
local disabled_pen_left = dfhack.pen.parse{fg=COLOR_CYAN,
18+
tile=curry(textures.tp_control_panel, 4) or nil, ch=string.byte('[')}
19+
local disabled_pen_center = dfhack.pen.parse{fg=COLOR_RED,
20+
tile=curry(textures.tp_control_panel, 5) or nil, ch=string.byte('x')}
21+
local disabled_pen_right = dfhack.pen.parse{fg=COLOR_CYAN,
22+
tile=curry(textures.tp_control_panel, 6) or nil, ch=string.byte(']')}
23+
local button_pen_left = dfhack.pen.parse{fg=COLOR_CYAN,
24+
tile=curry(textures.tp_control_panel, 7) or nil, ch=string.byte('[')}
25+
local button_pen_right = dfhack.pen.parse{fg=COLOR_CYAN,
26+
tile=curry(textures.tp_control_panel, 8) or nil, ch=string.byte(']')}
27+
local help_pen_center = dfhack.pen.parse{
28+
tile=curry(textures.tp_control_panel, 9) or nil, ch=string.byte('?')}
29+
local configure_pen_center = dfhack.pen.parse{
30+
tile=curry(textures.tp_control_panel, 10) or nil, ch=15} -- gear/masterwork symbol
31+
return enabled_pen_left, enabled_pen_center, enabled_pen_right,
32+
disabled_pen_left, disabled_pen_center, disabled_pen_right,
33+
button_pen_left, button_pen_right,
34+
help_pen_center, configure_pen_center
35+
end
36+
local ENABLED_PEN_LEFT, ENABLED_PEN_CENTER, ENABLED_PEN_RIGHT,
37+
DISABLED_PEN_LEFT, DISABLED_PEN_CENTER, DISABLED_PEN_RIGHT,
38+
BUTTON_PEN_LEFT, BUTTON_PEN_RIGHT,
39+
HELP_PEN_CENTER, CONFIGURE_PEN_CENTER = get_icon_pens()
40+
41+
ToggleLabel = defclass(ToggleLabel, widgets.CycleHotkeyLabel)
42+
ToggleLabel.ATTRS{
43+
options={{value=true},
44+
{value=false}},
45+
}
46+
function ToggleLabel:init()
47+
ToggleLabel.super.init(self)
48+
49+
local text = self.text
50+
-- the very last token is the On/Off text -- we'll repurpose it as an indicator
51+
text[#text] = { tile = function() return self:getOptionValue() and ENABLED_PEN_LEFT or DISABLED_PEN_LEFT end }
52+
text[#text + 1] = { tile = function() return self:getOptionValue() and ENABLED_PEN_CENTER or DISABLED_PEN_CENTER end }
53+
text[#text + 1] = { tile = function() return self:getOptionValue() and ENABLED_PEN_RIGHT or DISABLED_PEN_RIGHT end }
54+
self:setText(text)
55+
end
56+
57+
--------------------------------------------------------------------------------
58+
--- Spectate config window
559
Spectate = defclass(Spectate, widgets.Window)
660
Spectate.ATTRS {
761
frame_title='Spectate',
8-
frame={w=50, h=45},
62+
frame={w=35, h=30},
963
resizable=true,
10-
resize_min={w=50, h=20},
64+
resize_min={w=35, h=30},
1165
}
1266

67+
local function append(t, args)
68+
for i = 1, #args do
69+
t[#t + 1] = args[i]
70+
end
71+
return t
72+
end
73+
74+
local function create_toggle_button(cfg, cfg_key, left, top, hotkey, label)
75+
return ToggleLabel{
76+
frame={t=top,l=left},
77+
initial_option = cfg[cfg_key],
78+
on_change = function(new, old) cfg[cfg_key] = new; spectate.save_state() end,
79+
key = hotkey,
80+
label = label,
81+
}
82+
end
83+
84+
local function create_numeric_edit_field(cfg, cfg_key, left, top, hotkey, label)
85+
local editOnSubmit
86+
local ef = widgets.EditField{
87+
frame={t=top,l=left},
88+
label_text = label,
89+
text = tostring(cfg[cfg_key]),
90+
modal = true,
91+
key = hotkey,
92+
on_char = function(new_char,text) return '0' <= new_char and new_char <= '9' end,
93+
on_submit = function(text) editOnSubmit(text) end,
94+
}
95+
editOnSubmit = function(text)
96+
if text == '' then
97+
ef:setText(tostring(cfg[cfg_key]))
98+
else
99+
cfg[cfg_key] = tonumber(text)
100+
spectate.save_state()
101+
end
102+
end
103+
104+
return ef
105+
end
106+
107+
local function create_toggle_buttons(cfgFollow, keyFollow, cfgHover, keyHover, colFollow, colHover, top)
108+
local tlFollow = create_toggle_button(cfgFollow, keyFollow, colFollow + 2, top)
109+
local tlHover = create_toggle_button(cfgHover, keyHover, colHover + 1, top)
110+
111+
return tlFollow, tlHover
112+
end
113+
114+
local function create_row(label, hotkey, suffix, colFollow, colHover, top)
115+
local config = spectate.config
116+
117+
suffix = suffix or ''
118+
if suffix ~= '' then suffix = '-'..suffix end
119+
120+
local keyFollow = 'tooltip-follow'..suffix
121+
local keyHover = 'tooltip-hover'..suffix
122+
123+
local tlFollow, tlHover = create_toggle_buttons(config, keyFollow, config, keyHover, colFollow, colHover, top)
124+
local views = {
125+
widgets.HotkeyLabel{
126+
frame={t=top,l=0,w=1},
127+
key = 'CUSTOM_' .. hotkey,
128+
key_sep = '',
129+
on_activate = function() tlFollow:cycle() end,
130+
},
131+
widgets.HotkeyLabel{
132+
frame={t=top,l=1,w=1},
133+
key = 'CUSTOM_SHIFT_' .. hotkey,
134+
key_sep = '',
135+
on_activate = function() tlHover:cycle() end,
136+
},
137+
widgets.Label{
138+
frame={t=top,l=2},
139+
text = ': ' .. label,
140+
},
141+
tlFollow,
142+
tlHover,
143+
}
144+
145+
return views
146+
end
147+
148+
local function make_choice(text, tlFollow, tlHover)
149+
return {
150+
text=text,
151+
data={tlFollow=tlFollow, tlHover=tlHover},
152+
}
153+
end
154+
155+
local function pairsByKeys(t, f)
156+
local a = {}
157+
for n in pairs(t) do table.insert(a, n) end
158+
table.sort(a, f)
159+
local i = 0 -- iterator variable
160+
local iter = function () -- iterator function
161+
i = i + 1
162+
if a[i] == nil then return nil
163+
else return a[i], t[a[i]]
164+
end
165+
end
166+
return iter
167+
end
168+
169+
local function rpad(s, i)
170+
return string.format("%-"..i.."s", s)
171+
end
172+
173+
local overlay = require('plugins.overlay')
174+
local OVERLAY_NAME = 'spectate.tooltip'
175+
local function isOverlayEnabled()
176+
return overlay.get_state().config[OVERLAY_NAME].enabled
177+
end
178+
179+
local function enable_overlay(enabled)
180+
local tokens = {'overlay'}
181+
table.insert(tokens, enabled and 'enable' or 'disable')
182+
table.insert(tokens, OVERLAY_NAME)
183+
dfhack.run_command(tokens)
184+
end
185+
186+
function Spectate:updateOverlayDisabledGag(widget)
187+
local w = widget or self.subviews.overlayIsDisabledGag
188+
189+
if isOverlayEnabled() then
190+
if w.frame.t < 500 then
191+
w.frame.t = w.frame.t + 500
192+
end
193+
else
194+
if w.frame.t > 500 then
195+
w.frame.t = w.frame.t - 500
196+
end
197+
end
198+
199+
if not widget then
200+
self:updateLayout()
201+
end
202+
end
203+
13204
function Spectate:init()
14-
self:addviews{
205+
local config = spectate.config
206+
207+
local views = {}
208+
local t = 0
209+
210+
local len = 20
211+
append(views, {create_toggle_button(config, 'auto-disengage', 0, t, 'CUSTOM_ALT_D', rpad("Auto disengage", len))}); t = t + 1
212+
append(views, {create_toggle_button(config, 'auto-unpause', 0, t, 'CUSTOM_ALT_U', rpad("Auto unpause", len))}); t = t + 1
213+
append(views, {create_toggle_button(config, 'cinematic-action', 0, t, 'CUSTOM_ALT_C', rpad("Cinematic action", len))}); t = t + 1
214+
append(views, {create_numeric_edit_field(config, 'follow-seconds', 0, t, 'CUSTOM_ALT_F', "Follow (s): ")}); t = t + 1
215+
append(views, {create_toggle_button(config, 'include-animals', 0, t, 'CUSTOM_ALT_A', rpad("Include animals", len))}); t = t + 1
216+
append(views, {create_toggle_button(config, 'include-hostiles', 0, t, 'CUSTOM_ALT_H', rpad("Include hostiles", len))}); t = t + 1
217+
append(views, {create_toggle_button(config, 'include-visitors', 0, t, 'CUSTOM_ALT_V', rpad("Include visitors", len))}); t = t + 1
218+
append(views, {create_toggle_button(config, 'include-wildlife', 0, t, 'CUSTOM_ALT_W', rpad("Include wildlife", len))}); t = t + 1
219+
append(views, {create_toggle_button(config, 'prefer-conflict', 0, t, 'CUSTOM_ALT_B', rpad("Prefer conflict", len))}); t = t + 1
220+
append(views, {create_toggle_button(config, 'prefer-new-arrivals', 0, t, 'CUSTOM_ALT_N', rpad("Prefer new arrivals", len))}); t = t + 1
221+
222+
t = t + 1 -- add a blank line
223+
local colFollow, colHover = 15, 25
224+
-- tooltips headers
225+
append(views, {
226+
widgets.Label{
227+
frame={t=t,l=0},
228+
text="Tooltips:"
229+
},
230+
})
231+
-- t = t + 1
232+
-- overlay is prerequisite for any other tooltip option
233+
local overlayIsDisabledGag = nil
234+
local lblOverlayOnChange = function(new, old)
235+
enable_overlay(new)
236+
self:updateOverlayDisabledGag()
237+
end
238+
append(views, {
239+
ToggleLabel{
240+
frame={t=t,l=12},
241+
initial_option = isOverlayEnabled(),
242+
on_change = lblOverlayOnChange,
243+
key = 'CUSTOM_ALT_O',
244+
label = "Overlay ",
245+
}
246+
})
247+
t = t + 1
248+
overlayIsDisabledGag = widgets.Panel{
249+
view_id='overlayIsDisabledGag',
250+
frame={t=t,l=0,r=0,b=1}, -- b=1 because the very last row is where the HelpButton is placed
251+
frame_background=gui.CLEAR_PEN,
252+
subviews = {
253+
widgets.WrappedLabel{
254+
frame={t=0,l=0,r=0,b=0},
255+
frame_background=gui.CLEAR_PEN,
256+
text_to_wrap="Overlay has to be enabled for tooltips.",
257+
}
258+
},
15259
}
260+
261+
append(views, {
262+
widgets.Label{
263+
frame={t=t,l=colFollow},
264+
text="Follow"
265+
},
266+
widgets.Label{
267+
frame={t=t,l=colHover},
268+
text="Hover"
269+
},
270+
})
271+
t = t + 1
272+
273+
-- enable/disable
274+
append(views, create_row("Enable", 'E', '', colFollow, colHover, t)); t = t + 1
275+
append(views, create_row("Job", 'J', 'job', colFollow, colHover, t)); t = t + 1
276+
append(views, create_row("Name", 'N', 'name', colFollow, colHover, t)); t = t + 1
277+
append(views, create_row("Stress", 'S', 'stress', colFollow, colHover, t)); t = t + 1
278+
279+
-- next are individual stress levels
280+
-- a list on the left to select one, individual buttons in two columns to be able to click on them
281+
local choices = {}
282+
local levels = config['tooltip-stress-levels']
283+
local stressFollow = config['tooltip-follow-stress-levels']
284+
local stressHover = config['tooltip-hover-stress-levels']
285+
local tList = t
286+
for l, cfg in pairsByKeys(levels) do
287+
local tlFollow, tlHover = create_toggle_buttons(stressFollow, l, stressHover, l, colFollow, colHover, t)
288+
append(views, { tlFollow, tlHover })
289+
290+
table.insert(choices, make_choice({{text=cfg.text, pen=cfg.pen}, ' ', cfg.name}, tlFollow, tlHover))
291+
292+
t = t + 1
293+
end
294+
append(views,{
295+
widgets.List{
296+
frame={t=tList,l=2},
297+
view_id='list_levels',
298+
on_submit=function(index, choice) choice.data.tlFollow:cycle() end,
299+
on_submit2=function(index, choice) choice.data.tlHover:cycle() end,
300+
row_height=1,
301+
choices = choices,
302+
},
303+
})
304+
305+
append(views, {create_numeric_edit_field(config, 'tooltip-follow-blink-milliseconds', 0, t, 'CUSTOM_B', "Blink duration (ms): ")}); t = t + 1
306+
307+
append(views, {
308+
widgets.HelpButton{
309+
frame={b=0,r=0},
310+
command = 'spectate',
311+
}
312+
})
313+
314+
append(views, {overlayIsDisabledGag}) -- must be the very last thing
315+
self:updateOverlayDisabledGag(overlayIsDisabledGag)
316+
317+
self:addviews(views)
16318
end
17319

18320
SpectateScreen = defclass(SpectateScreen, gui.ZScreen)

0 commit comments

Comments
 (0)