0 |
--[[ |
1 |
Subtitles — adds subtitles to Minetest. |
2 |
|
3 |
Copyright © 2022‒2023, Silver Sandstone <@SilverSandstone@craftodon.social> |
4 |
|
5 |
Permission is hereby granted, free of charge, to any person obtaining a |
6 |
copy of this software and associated documentation files (the "Software"), |
7 |
to deal in the Software without restriction, including without limitation |
8 |
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
9 |
and/or sell copies of the Software, and to permit persons to whom the |
10 |
Software is furnished to do so, subject to the following conditions: |
11 |
|
12 |
The above copyright notice and this permission notice shall be included |
13 |
in all copies or substantial portions of the Software. |
14 |
|
15 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
20 |
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
21 |
DEALINGS IN THE SOFTWARE. |
22 |
]] |
23 |
|
24 |
|
25 |
--- Implements the subtitle menu GUI. |
26 |
|
27 |
|
28 |
local S = subtitles.S; |
29 |
|
30 |
|
31 |
local MAX_HEIGHT = 10.5; |
32 |
|
33 |
|
34 |
--- Creates a menu formspec for the specified player. |
35 |
-- @param player The username or ObjectRef of the player. |
36 |
-- @param embedded This should be true if the menu is being embedded in another formspec. |
37 |
-- @param options A table of extra options: width, height. |
38 |
-- @return A formspec string. |
39 |
function subtitles.get_menu_formspec(player, embedded, options) |
40 |
local agent = subtitles.get_agent(player); |
41 |
player = agent.username; |
42 |
options = options or {}; |
43 |
|
44 |
local _esc = minetest.formspec_escape; |
45 |
local natural_height = 3.75 + #subtitles.SubtitleDisplay.implementations * 1.25; |
46 |
local width = options.width or 8; |
47 |
local height = options.height or math.min(natural_height, MAX_HEIGHT); |
48 |
local display_name = agent:get_display_name(); |
49 |
|
50 |
-- Style: |
51 |
local style = {}; |
52 |
local selection_colour = '#80808040'; |
53 |
local panel = 'subtitles_panel.png;4'; |
54 |
local disabled_mod = '^subtitles_slash.png'; |
55 |
local state_icon = 'subtitles_icon.png'; |
56 |
local footsteps_icon = 'subtitles_footsteps.png'; |
57 |
local circle = 'subtitles_circle.png'; |
58 |
|
59 |
if minetest.get_modpath('rp_formspec') then |
60 |
-- Repixture style: |
61 |
table.insert_all(style, |
62 |
{ |
63 |
'background9[0,0;0,0;ui_formspec_bg_short.png^[resize:384x192;true;24] bgcolor[#00000000]', -- Background |
64 |
'style_type[button,image_button;border=false]', -- Reset buttons |
65 |
'style_type[button,image_button;bgimg=ui_button_1w_inactive.png^[resize:48x48;bgimg_middle=21,18,-20,-24]', -- Passive button image |
66 |
'style_type[button:pressed,image_button:pressed;bgimg=ui_button_1w_active.png^[resize:48x48;bgimg_middle=21,21,-20,-21]', -- Active button image |
67 |
}); |
68 |
selection_colour = '#33333340'; |
69 |
panel = 'ui_itemslot.png^[resize:72x72;32'; |
70 |
elseif minetest.get_modpath('mcl_core') then |
71 |
-- MineClone style: |
72 |
panel = 'subtitles_mcl_panel.png;4'; |
73 |
elseif embedded and minetest.get_modpath('i3') then |
74 |
-- i3 style: |
75 |
panel = 'subtitles_i3_panel.png;4'; |
76 |
state_icon = 'subtitles_icon_flat_hd.png'; |
77 |
footsteps_icon = 'subtitles_i3_footsteps.png'; |
78 |
disabled_mod = '^[multiply:#9F9F9F'; |
79 |
circle = 'subtitles_i3_circle.png'; |
80 |
end; |
81 |
|
82 |
-- State: |
83 |
local enabled = agent:get_enabled(); |
84 |
local state_label; |
85 |
local toggle_tooltip; |
86 |
if enabled then |
87 |
state_label = S('Subtitles enabled'); |
88 |
toggle_tooltip = S('Click to disable subtitles'); |
89 |
else |
90 |
state_label = S('Subtitles disabled'); |
91 |
toggle_tooltip = S('Click to enable subtitles'); |
92 |
state_icon = state_icon .. disabled_mod; |
93 |
end; |
94 |
|
95 |
local footsteps = agent:get_footsteps_enabled(); |
96 |
local footsteps_tooltip; |
97 |
if footsteps then |
98 |
footsteps_tooltip = S('Footsteps enabled'); |
99 |
else |
100 |
footsteps_tooltip = S('Footsteps disabled'); |
101 |
footsteps_icon = footsteps_icon .. disabled_mod; |
102 |
end; |
103 |
|
104 |
-- Main menu: |
105 |
local formspec = |
106 |
{ |
107 |
('size[%f,%f] real_coordinates[true]'):format(width, height), |
108 |
table.concat(style, ''), |
109 |
-- Toggle button: |
110 |
('image_button[0.25,0.25;1,1;%s;toggle;]'):format(_esc(state_icon)), |
111 |
('tooltip[toggle;%s]'):format(_esc(toggle_tooltip)), |
112 |
-- State label: |
113 |
--('label[1.5,0.75;%s]'):format(_esc(state_label)), |
114 |
('hypertext[1.5,0.5;%f,1;state_label;<big><b>%s</b></big>]'):format(width - 1.75, _esc(state_label)), |
115 |
|
116 |
-- Mode panel: |
117 |
('image[0.25,2.0;%f,%f;%s]'):format(width - 0.5, height - 3.5, panel), |
118 |
('label[0.25,1.75;%s]'):format(_esc(S('Display mode:'))), |
119 |
|
120 |
-- Footsteps button: |
121 |
('image_button[0.25,%f;1,1;%s;footsteps;]'):format(height - 1.25, _esc(footsteps_icon)), |
122 |
('tooltip[footsteps;%s]'):format(_esc(footsteps and S('Footsteps enabled') or S('Footsteps disabled'))), |
123 |
-- Exit button: |
124 |
('button_exit[%f,%f;3,1;done;%s]'):format(width - 3.25, height - 1.25, _esc(S('Done'))), |
125 |
|
126 |
-- Mode list: |
127 |
('scroll_container[0.25,2.0;%f,%f;mode_list_scrollbar;vertical;]'):format(width - 0.5, height - 3.5), |
128 |
('scrollbaroptions[max=%d]'):format(math.max(0, natural_height - height)), |
129 |
('scrollbar[%f,2.0;0.5,%f;vertical;mode_list_scrollbar;]'):format(width - 0.5, height - 3.5), |
130 |
}; |
131 |
if embedded then |
132 |
formspec[1] = ''; |
133 |
end; |
134 |
|
135 |
-- Display modes: |
136 |
for index, impl in ipairs(subtitles.SubtitleDisplay.implementations) do |
137 |
local y = 0.25 + (index - 1) * 1.25; |
138 |
if impl.NAME == display_name then |
139 |
local colour = enabled and '#00FF00' or '#BFBFBF'; |
140 |
table.insert_all(formspec, |
141 |
{ |
142 |
('box[0.125,%f;%f,1.25;%s]'):format(y - 0.125, width - 0.75, selection_colour), |
143 |
('image[%f,%f;1,1;%s^[multiply:%s]'):format(width - 1.75, y, circle, colour), |
144 |
}); |
145 |
end; |
146 |
table.insert_all(formspec, |
147 |
{ |
148 |
('image[0.25,%f;1,1;%s]'):format(y, _esc(impl.ICON)), |
149 |
('button[1.5,%f;%f,1;display_%s;%s]'):format(y, width - 3.5, impl.NAME, _esc(impl.TITLE)), |
150 |
('tooltip[display_%s;%s]'):format(impl.NAME, _esc(impl.DESCRIPTION or impl.TITLE)), |
151 |
}); |
152 |
end; |
153 |
|
154 |
table.insert(formspec, 'scroll_container_end[]'); |
155 |
|
156 |
return table.concat(formspec, ''); |
157 |
end; |
158 |
|
159 |
|
160 |
--- Shows the subtitle menu to the specified player. |
161 |
-- @param player The username or ObjectRef of the player. |
162 |
function subtitles.show_menu(player) |
163 |
local formspec = subtitles.get_menu_formspec(player); |
164 |
if type(player) ~= 'string' then |
165 |
player = player:get_player_name(); |
166 |
end; |
167 |
minetest.show_formspec(player, 'subtitles:menu', formspec); |
168 |
end; |
169 |
|
170 |
|
171 |
--- Handles receiving fields from the menu formspec. |
172 |
-- @param player The username or ObjectRef of the player. |
173 |
-- @param fields A table of fields. |
174 |
-- @param embedded If this is true, the menu will not be re-shown. |
175 |
function subtitles.menu_receive_fields(player, fields, embedded) |
176 |
local function _reshow() |
177 |
if not embedded then |
178 |
subtitles.show_menu(player); |
179 |
end; |
180 |
end; |
181 |
|
182 |
local agent = subtitles.get_agent(player); |
183 |
|
184 |
if fields.toggle then |
185 |
agent:toggle_enabled(); |
186 |
_reshow(); |
187 |
return; |
188 |
end; |
189 |
|
190 |
if fields.footsteps then |
191 |
agent:toggle_footsteps_enabled(); |
192 |
_reshow(); |
193 |
return; |
194 |
end; |
195 |
|
196 |
for __, impl in ipairs(subtitles.SubtitleDisplay.implementations) do |
197 |
if fields['display_' .. impl.NAME] then |
198 |
agent:set_display_name(impl.NAME); |
199 |
_reshow(); |
200 |
return; |
201 |
end; |
202 |
end; |
203 |
end; |
204 |
|
205 |
|
206 |
if not subtitles.settings.inventory_integration then |
207 |
return; |
208 |
end; |
209 |
|
210 |
|
211 |
-- Unified Inventory integration: |
212 |
if minetest.get_modpath('unified_inventory') then |
213 |
unified_inventory.register_button('subtitles', |
214 |
{ |
215 |
type = 'image'; |
216 |
image = 'subtitles_icon.png'; |
217 |
tooltip = S('Subtitles'); |
218 |
hide_lite = false; |
219 |
action = subtitles.show_menu; |
220 |
}); |
221 |
end; |
222 |
|
223 |
|
224 |
-- i3 integration: |
225 |
if minetest.get_modpath('i3') then |
226 |
i3.new_tab('subtitles', |
227 |
{ |
228 |
description = S('Subtitles'); |
229 |
image = 'subtitles_icon.png'; |
230 |
formspec = |
231 |
function(player, data, fs) |
232 |
fs(subtitles.get_menu_formspec(player, true, {width = 10.25, height = 12})); |
233 |
end; |
234 |
fields = |
235 |
function(player, data, fields) |
236 |
subtitles.menu_receive_fields(player, fields, true); |
237 |
i3.set_fs(player); |
238 |
end; |
239 |
}); |
240 |
end; |
241 |
|
242 |
|
243 |
-- SFInv integration via SFInv Buttons: |
244 |
if minetest.get_modpath('sfinv_buttons') then |
245 |
sfinv_buttons.register_button('subtitles', |
246 |
{ |
247 |
image = 'subtitles_icon.png'; |
248 |
tooltip = S('Configure your subtitle preferences'); |
249 |
title = S('Subtitles'); |
250 |
action = subtitles.show_menu; |
251 |
}); |
252 |
end; |
253 |
|
254 |
|
255 |
-- Repixture integration: |
256 |
if minetest.get_modpath('rp_formspec') then |
257 |
rp_formspec.register_page('subtitles:subtitles', 'label[1,1;?]'); |
258 |
rp_formspec.register_invpage('subtitles:subtitles', {}); |
259 |
rp_formspec.register_invtab('subtitles:subtitles', {icon = 'subtitles_rp_icon.png', tooltip = S('Subtitles')}); |
260 |
|
261 |
local old_set_current_invpage = rp_formspec.set_current_invpage; |
262 |
function rp_formspec.set_current_invpage(player, page) |
263 |
if page == 'subtitles:subtitles' then |
264 |
subtitles.show_menu(player); |
265 |
else |
266 |
old_set_current_invpage(player, page); |
267 |
end; |
268 |
end; |
269 |
end; |