Gesture manager and Profiles: improve Dispatcher actions menu (#13078)

This commit is contained in:
hius07
2025-01-26 11:10:49 +02:00
committed by GitHub
parent 9eeb23104f
commit 9bcd09633c
5 changed files with 181 additions and 154 deletions

View File

@@ -676,45 +676,50 @@ function Dispatcher:getArgFromValue(item, value)
end
-- Add the item to the end of the execution order.
-- If item or the order is nil all items will be added.
function Dispatcher:_addToOrder(location, settings, item)
if location[settings] then
if not location[settings].settings then location[settings].settings = {} end
if not location[settings].settings.order or item == nil then
location[settings].settings.order = {}
for k in pairs(location[settings]) do
-- If order is nil all items will be added.
function Dispatcher._addToOrder(location, settings, item)
local actions = location[settings]
local count = Dispatcher:_itemsCount(actions)
if count == 2 then
local first_item
for k in pairs(actions) do
if k ~= "settings" and k~= item then
first_item = k
break
end
end
actions.settings = actions.settings or {}
actions.settings.order = { first_item, item }
elseif count > 2 then
local order = util.tableGetValue(actions, "settings", "order")
if order then
table.insert(location[settings].settings.order, item)
else -- old unordered actions
util.tableSetValue(actions, {}, "settings", "order")
for k in pairs(actions) do
if settingsList[k] ~= nil then
table.insert(location[settings].settings.order, k)
end
end
else
if not util.arrayContains(location[settings].settings.order, item) then
table.insert(location[settings].settings.order, item)
end
end
end
end
-- Remove the item from the execution order.
-- If item is nil all items will be removed.
-- If the resulting order is empty it will be nilled
function Dispatcher:_removeFromOrder(location, settings, item)
if location[settings] and location[settings].settings then
if location[settings].settings.order then
if item then
local k = util.arrayContains(location[settings].settings.order, item)
if k then table.remove(location[settings].settings.order, k) end
else
location[settings].settings.order = {}
end
if next(location[settings].settings.order) == nil then
location[settings].settings.order = nil
if next(location[settings].settings) == nil then
location[settings].settings = nil
end
-- If the resulting order is empty it will be nilled.
function Dispatcher._removeFromOrder(location, settings, item)
local actions = location[settings]
local order = util.tableGetValue(actions, "settings", "order")
if order then
local k = util.arrayContains(order, item)
if k then
table.remove(order, k)
if Dispatcher:_itemsCount(actions) < 2 then
util.tableRemoveValue(actions, "settings", "order")
end
end
end
util.tableRemoveValue(actions, "settings", "quickmenu_separators", item)
end
-- Get a textual representation of the enabled actions to display in a menu item.
@@ -734,37 +739,46 @@ function Dispatcher:menuTextFunc(settings)
end
-- Get a list of all enabled actions to display in a menu.
function Dispatcher:getDisplayList(settings)
function Dispatcher.getDisplayList(settings, for_sorting)
local item_table = {}
if not settings then return item_table end
local is_check_mark = for_sorting and settings.settings and settings.settings.show_as_quickmenu
for item, v in iter_func(settings) do
if type(item) == "number" then item = v end
if settingsList[item] ~= nil and (settingsList[item].condition == nil or settingsList[item].condition == true) then
table.insert(item_table, {text = Dispatcher:getNameFromItem(item, settings), key = item})
if settingsList[item] ~= nil and settingsList[item].condition ~= false then
table.insert(item_table, {
text = Dispatcher:getNameFromItem(item, settings),
key = item,
checked_func = is_check_mark and function()
return settings.settings.quickmenu_separators and settings.settings.quickmenu_separators[item]
end,
callback = is_check_mark and function()
if settings.settings.quickmenu_separators and settings.settings.quickmenu_separators[item] then
util.tableRemoveValue(settings.settings, "quickmenu_separators", item)
else
util.tableSetValue(settings.settings, true, "quickmenu_separators", item)
end
end,
})
end
end
return item_table
end
-- Display a SortWidget to sort the enable actions execution order.
function Dispatcher:_sortActions(caller, location, settings, touchmenu_instance)
local display_list = Dispatcher:getDisplayList(location[settings])
function Dispatcher._sortActions(caller, actions)
local show_as_quickmenu = util.tableGetValue(actions, "settings", "show_as_quickmenu")
local display_list = Dispatcher.getDisplayList(actions, true)
local SortWidget = require("ui/widget/sortwidget")
local sort_widget
sort_widget = SortWidget:new{
title = _("Arrange actions"),
local sort_widget = SortWidget:new{
title = show_as_quickmenu and _("Arrange actions and QuickMenu separators") or _("Arrange actions"),
underscore_checked_item = show_as_quickmenu,
item_table = display_list,
callback = function()
if location[settings] and next(location[settings]) ~= nil then
if not location[settings].settings then
location[settings].settings = {}
end
location[settings].settings.order = {}
for i, v in ipairs(sort_widget.item_table) do
location[settings].settings.order[i] = v.key
end
util.tableSetValue(actions, {}, "settings", "order")
for i, v in ipairs(display_list) do
actions.settings.order[i] = v.key
end
if touchmenu_instance then touchmenu_instance:updateItems() end
caller.updated = true
end
}
@@ -778,10 +792,10 @@ function Dispatcher:_addItem(caller, menu, location, settings, section)
location[settings] = {}
end
location[settings][k] = value
Dispatcher:_addToOrder(location, settings, k)
Dispatcher._addToOrder(location, settings, k)
else
location[settings][k] = nil
Dispatcher:_removeFromOrder(location, settings, k)
Dispatcher._removeFromOrder(location, settings, k)
end
caller.updated = true
if touchmenu_instance then
@@ -926,6 +940,21 @@ function Dispatcher:_addItem(caller, menu, location, settings, section)
end
end
function Dispatcher.removeActions(actions, do_remove)
if actions then
local count = Dispatcher:_itemsCount(actions)
if count == 1 then
do_remove()
elseif count > 1 then
local ConfirmBox = require("ui/widget/confirmbox")
UIManager:show(ConfirmBox:new{
text = T(NC_("Dispatcher", "1 action will be removed.", "%1 actions will be removed.", count), count),
ok_callback = do_remove,
})
end
end
end
--[[--
Add a submenu to edit which items are dispatched
arguments are:
@@ -941,19 +970,22 @@ function Dispatcher:addSubMenu(caller, menu, location, settings)
menu.ignored_by_menu_search = true -- all those would be duplicated
table.insert(menu, {
text = _("Nothing"),
separator = true,
keep_menu_open = true,
no_refresh_on_check = true,
checked_func = function()
return location[settings] ~= nil and Dispatcher:_itemsCount(location[settings]) == 0
end,
callback = function(touchmenu_instance)
local name = location[settings] and location[settings].settings and location[settings].settings.name
location[settings] = {}
if name then
location[settings].settings = { name = name }
local function do_remove()
local actions = location[settings]
local name = actions and actions.settings and actions.settings.name
location[settings] = name and { settings = { name = name } } or {}
caller.updated = true
touchmenu_instance:updateItems()
end
caller.updated = true
if touchmenu_instance then touchmenu_instance:updateItems() end
Dispatcher.removeActions(location[settings], do_remove)
end,
separator = true,
})
local section_list = {
{"general", _("General")},
@@ -983,7 +1015,7 @@ function Dispatcher:addSubMenu(caller, menu, location, settings)
for k, _ in pairs(location[settings]) do
if settingsList[k] ~= nil and settingsList[k][section[1]] == true then
location[settings][k] = nil
Dispatcher:_removeFromOrder(location, settings, k)
Dispatcher._removeFromOrder(location, settings, k)
caller.updated = true
end
end
@@ -995,45 +1027,34 @@ function Dispatcher:addSubMenu(caller, menu, location, settings)
end
menu.max_per_page = #menu -- next items in page 2
table.insert(menu, {
text = _("Arrange actions"),
checked_func = function()
return location[settings] ~= nil
and location[settings].settings ~= nil
and location[settings].settings.order ~= nil
text_func = function()
return util.tableGetValue(location[settings], "settings", "show_as_quickmenu")
and _("Arrange actions and QuickMenu separators") or _("Arrange actions")
end,
enabled_func = function()
return location[settings] and Dispatcher:_itemsCount(location[settings]) > 1 or false
end,
callback = function(touchmenu_instance)
Dispatcher:_sortActions(caller, location, settings, touchmenu_instance)
end,
hold_callback = function(touchmenu_instance)
if location[settings]
and location[settings].settings
and location[settings].settings.order then
Dispatcher:_removeFromOrder(location, settings)
caller.updated = true
if touchmenu_instance then touchmenu_instance:updateItems() end
end
Dispatcher._sortActions(caller, location[settings])
end,
keep_menu_open = true,
separator = true,
})
table.insert(menu, {
text = _("Show as QuickMenu"),
checked_func = function()
return location[settings] ~= nil
and location[settings].settings ~= nil
and location[settings].settings.show_as_quickmenu
return util.tableGetValue(location[settings], "settings", "show_as_quickmenu")
end,
callback = function()
if location[settings] then
if location[settings].settings then
if location[settings].settings.show_as_quickmenu then
location[settings].settings.show_as_quickmenu = nil
if next(location[settings].settings) == nil then
location[settings].settings = nil
end
else
location[settings].settings.show_as_quickmenu = true
end
local actions = location[settings]
if actions then
if util.tableGetValue(actions, "settings", "show_as_quickmenu") then
util.tableRemoveValue(actions, "settings", "show_as_quickmenu")
util.tableRemoveValue(actions, "settings", "quickmenu_separators")
util.tableRemoveValue(actions, "settings", "keep_open_on_apply")
util.tableRemoveValue(actions, "settings", "anchor_quickmenu")
else
location[settings].settings = {["show_as_quickmenu"] = true}
util.tableSetValue(actions, true, "settings", "show_as_quickmenu")
end
caller.updated = true
end
@@ -1041,24 +1062,19 @@ function Dispatcher:addSubMenu(caller, menu, location, settings)
})
table.insert(menu, {
text = _("Keep QuickMenu open"),
enabled_func = function()
return util.tableGetValue(location[settings], "settings", "show_as_quickmenu") or false
end,
checked_func = function()
return location[settings] ~= nil
and location[settings].settings ~= nil
and location[settings].settings.keep_open_on_apply
return util.tableGetValue(location[settings], "settings", "keep_open_on_apply")
end,
callback = function()
if location[settings] then
if location[settings].settings then
if location[settings].settings.keep_open_on_apply then
location[settings].settings.keep_open_on_apply = nil
if next(location[settings].settings) == nil then
location[settings].settings = nil
end
else
location[settings].settings.keep_open_on_apply = true
end
local actions = location[settings]
if actions then
if util.tableGetValue(actions, "settings", "keep_open_on_apply") then
util.tableRemoveValue(actions, "settings", "keep_open_on_apply")
else
location[settings].settings = {["keep_open_on_apply"] = true}
util.tableSetValue(actions, true, "settings", "keep_open_on_apply")
end
caller.updated = true
end
@@ -1082,10 +1098,10 @@ function Dispatcher:isActionEnabled(action)
return not disabled
end
function Dispatcher:_showAsMenu(settings, exec_props)
local title = settings.settings.name or _("QuickMenu")
function Dispatcher._showAsMenu(settings, exec_props)
local title = settings.settings.name
local keep_open_on_apply = settings.settings.keep_open_on_apply
local display_list = Dispatcher:getDisplayList(settings)
local display_list = Dispatcher.getDisplayList(settings)
local quickmenu
local buttons = {}
if exec_props and exec_props.qm_show then
@@ -1123,6 +1139,9 @@ function Dispatcher:_showAsMenu(settings, exec_props)
end
end,
}})
if settings.settings.quickmenu_separators and settings.settings.quickmenu_separators[v.key] then
table.insert(buttons, {})
end
end
local ButtonDialog = require("ui/widget/buttondialog")
quickmenu = ButtonDialog:new{
@@ -1148,12 +1167,16 @@ arguments are:
function Dispatcher:execute(settings, exec_props)
if ((exec_props == nil or exec_props.qm_show == nil) and settings.settings and settings.settings.show_as_quickmenu)
or (exec_props and exec_props.qm_show) then
return Dispatcher:_showAsMenu(settings, exec_props)
return Dispatcher._showAsMenu(settings, exec_props)
end
local has_many = Dispatcher:_itemsCount(settings) > 1
if has_many then
UIManager:broadcastEvent(Event:new("BatchedUpdate"))
end
Notification:setNotifySource(Notification.SOURCE_DISPATCHER)
if settings.settings and settings.settings.notify then
Notification:notify(T(_("Executing profile: %1"), settings.settings.name))
end
local gesture = exec_props and exec_props.gesture
for k, v in iter_func(settings) do
if type(k) == "number" then
@@ -1161,10 +1184,6 @@ function Dispatcher:execute(settings, exec_props)
v = settings[k]
end
if Dispatcher:isActionEnabled(settingsList[k]) then
Notification:setNotifySource(Notification.SOURCE_DISPATCHER)
if settings.settings and settings.settings.notify then
Notification:notify(T(_("Executing profile: %1"), settings.settings.name))
end
if settingsList[k].configurable then
local value = v
if type(v) ~= "number" then
@@ -1174,7 +1193,6 @@ function Dispatcher:execute(settings, exec_props)
end
UIManager:sendEvent(Event:new("ConfigChange", settingsList[k].configurable.name, value))
end
local category = settingsList[k].category
local event = settingsList[k].event
if category == "none" then
@@ -1195,8 +1213,8 @@ function Dispatcher:execute(settings, exec_props)
UIManager:sendEvent(Event:new(event, arg))
end
end
Notification:resetNotifySource()
end
Notification:resetNotifySource()
if has_many then
UIManager:broadcastEvent(Event:new("BatchedUpdateDone"))
end

View File

@@ -83,10 +83,17 @@ function SortItemWidget:init()
dimen = Geom:new{ w = checked_widget:getSize().w },
self.checkmark_widget,
},
TextWidget:new{
text = self.item.text,
max_width = text_max_width,
face = self.item.face or self.face,
VerticalGroup:new{
align = "left",
TextWidget:new{
text = self.item.text,
max_width = text_max_width,
face = self.item.face or self.face,
},
self.show_parent.underscore_checked_item and item_checked and LineWidget:new{
dimen = Geom:new{ w = text_max_width, h = Size.line.thick },
background = Blitbuffer.COLOR_DARK_GRAY,
},
},
},
},

View File

@@ -921,7 +921,9 @@ function TouchMenu:onMenuSelect(item, tap_on_checkmark)
-- must set keep_menu_open=true if that is wished)
callback(self)
if refresh then
self:updateItems()
if not item.no_refresh_on_check then
self:updateItems()
end
elseif not item.keep_menu_open then
self:closeMenu()
end

View File

@@ -4,7 +4,6 @@ local DataStorage = require("datastorage")
local Device = require("device")
local Dispatcher = require("dispatcher")
local Event = require("ui/event")
local FFIUtil = require("ffi/util")
local Geom = require("ui/geometry")
local GestureDetector = require("device/gesturedetector")
local GestureRange = require("ui/gesturerange")
@@ -16,12 +15,13 @@ local Screen = require("device").screen
local SpinWidget = require("ui/widget/spinwidget")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local ffiUtil = require("ffi/util")
local logger = require("logger")
local util = require("util")
local T = FFIUtil.template
local time = require("ui/time")
local util = require("util")
local _ = require("gettext")
local C_ = _.pgettext
local T = ffiUtil.template
if not Device:isTouchDevice() then
return { disabled = true, }
@@ -35,7 +35,7 @@ local Gestures = WidgetContainer:extend{
custom_multiswipes = nil,
updated = false,
}
local gestures_path = FFIUtil.joinPath(DataStorage:getSettingsDir(), "gestures.lua")
local gestures_path = ffiUtil.joinPath(DataStorage:getSettingsDir(), "gestures.lua")
local gestures_list = {
tap_top_left_corner = _("Top left"),
@@ -138,7 +138,7 @@ function Gestures:isGestureAlwaysActive(ges, multiswipe_directions)
end
function Gestures:init()
local defaults_path = FFIUtil.joinPath(self.path, "defaults.lua")
local defaults_path = ffiUtil.joinPath(self.path, "defaults.lua")
self.ignore_hold_corners = G_reader_settings:isTrue("ignore_hold_corners")
self.multiswipes_enabled = G_reader_settings:isTrue("multiswipes_enabled")
self.is_docless = self.ui == nil or self.ui.document == nil
@@ -149,7 +149,7 @@ function Gestures:init()
if not next(self.settings_data.data) then
logger.warn("No gestures file or invalid gestures file found, copying defaults")
self.settings_data:purge()
FFIUtil.copyFile(defaults_path, gestures_path)
ffiUtil.copyFile(defaults_path, gestures_path)
self.settings_data = LuaSettings:open(gestures_path)
end
end
@@ -224,74 +224,70 @@ function Gestures:genMenu(ges)
table.insert(sub_items, {
text = T(_("%1 (default)"), Dispatcher:menuTextFunc(self.defaults[ges])),
keep_menu_open = true,
separator = true,
no_refresh_on_check = true,
checked_func = function()
return util.tableEquals(self.gestures[ges], self.defaults[ges])
end,
callback = function()
self.gestures[ges] = util.tableDeepCopy(self.defaults[ges])
self.updated = true
callback = function(touchmenu_instance)
local function do_remove()
self.gestures[ges] = util.tableDeepCopy(self.defaults[ges])
self.updated = true
touchmenu_instance:updateItems()
end
Dispatcher.removeActions(self.gestures[ges], do_remove)
end,
separator = true,
})
end
table.insert(sub_items, {
text = _("Pass through"),
keep_menu_open = true,
no_refresh_on_check = true,
checked_func = function()
return self.gestures[ges] == nil
end,
callback = function()
self.gestures[ges] = nil
self.updated = true
callback = function(touchmenu_instance)
local function do_remove()
self.gestures[ges] = nil
self.updated = true
touchmenu_instance:updateItems()
end
Dispatcher.removeActions(self.gestures[ges], do_remove)
end,
})
Dispatcher:addSubMenu(self, sub_items, self.gestures, ges)
sub_items.max_per_page = nil -- restore default, settings in page 2
table.insert(sub_items, {
text = _("Anchor QuickMenu to gesture position"),
enabled_func = function()
return util.tableGetValue(self.gestures, ges, "settings", "show_as_quickmenu") or false
end,
checked_func = function()
return self.gestures[ges] ~= nil
and self.gestures[ges].settings ~= nil
and self.gestures[ges].settings.anchor_quickmenu
return util.tableGetValue(self.gestures, ges, "settings", "anchor_quickmenu")
end,
callback = function()
if self.gestures[ges] then
if self.gestures[ges].settings then
if self.gestures[ges].settings.anchor_quickmenu then
self.gestures[ges].settings.anchor_quickmenu = nil
if next(self.gestures[ges].settings) == nil then
self.gestures[ges].settings = nil
end
else
self.gestures[ges].settings.anchor_quickmenu = true
end
if util.tableGetValue(self.gestures, ges, "settings", "anchor_quickmenu") then
util.tableRemoveValue(self.gestures, ges, "settings", "anchor_quickmenu")
else
self.gestures[ges].settings = {["anchor_quickmenu"] = true}
util.tableSetValue(self.gestures, true, ges, "settings", "anchor_quickmenu")
end
self.updated = true
end
end,
separator = true,
})
table.insert(sub_items, {
text = _("Always active"),
checked_func = function()
return self.gestures[ges] ~= nil
and self.gestures[ges].settings ~= nil
and self.gestures[ges].settings.always_active
return util.tableGetValue(self.gestures, ges, "settings", "always_active")
end,
callback = function()
if self.gestures[ges] then
if self.gestures[ges].settings then
if self.gestures[ges].settings.always_active then
self.gestures[ges].settings.always_active = nil
if next(self.gestures[ges].settings) == nil then
self.gestures[ges].settings = nil
end
else
self.gestures[ges].settings.always_active = true
end
if util.tableGetValue(self.gestures, ges, "settings", "always_active") then
util.tableRemoveValue(self.gestures, ges, "settings", "always_active")
else
self.gestures[ges].settings = {["always_active"] = true}
util.tableSetValue(self.gestures, true, ges, "settings", "always_active")
end
self.updated = true
end
@@ -471,7 +467,7 @@ function Gestures:genCustomMultiswipeSubmenu()
help_text = _("The number of possible multiswipe gestures is theoretically infinite. With the multiswipe recorder you can easily record your own."),
}
}
for item in FFIUtil.orderedPairs(self.custom_multiswipes) do
for item in ffiUtil.orderedPairs(self.custom_multiswipes) do
local hold_callback = function(touchmenu_instance)
UIManager:show(ConfirmBox:new{
text = T(_("Remove custom multiswipe %1?"), self:friendlyMultiswipeName(item)),

View File

@@ -207,7 +207,7 @@ function Profiles:getSubMenuItems()
},
{
text_func = function()
return T(_("Edit actions: (%1)"), Dispatcher:menuTextFunc(v))
return T(_("Edit actions: (%1)"), Dispatcher:menuTextFunc(self.data[k]))
end,
sub_item_table_func = function()
local edit_actions_sub_items = {}
@@ -259,7 +259,6 @@ function Profiles:getSubMenuItems()
{
text = _("Delete"),
keep_menu_open = true,
separator = true,
callback = function(touchmenu_instance)
UIManager:show(ConfirmBox:new{
text = _("Do you want to delete this profile?"),
@@ -278,6 +277,7 @@ function Profiles:getSubMenuItems()
end,
})
end,
separator = true,
},
}
table.insert(sub_item_table, {
@@ -545,6 +545,7 @@ function Profiles:genAutoExecPathChangedMenuItem(text, event, profile_name, sepa
local value = util.tableGetValue(self.autoexec, event, profile_name, conditions[i][2])
return value and txt .. ": " .. value or txt
end,
no_refresh_on_check = true,
checked_func = function()
return util.tableGetValue(self.autoexec, event, profile_name, conditions[i][2])
end,
@@ -672,6 +673,7 @@ function Profiles:genAutoExecDocConditionalMenuItem(text, event, profile_name, s
local txt = util.tableGetValue(self.autoexec, event, profile_name, condition, prop)
return txt and title .. " " .. txt or title:sub(1, -2)
end,
no_refresh_on_check = true,
checked_func = function()
return util.tableGetValue(self.autoexec, event, profile_name, condition, prop) and true
end,
@@ -741,6 +743,7 @@ function Profiles:genAutoExecDocConditionalMenuItem(text, event, profile_name, s
enabled_func = function()
return not util.tableGetValue(self.autoexec, event_always, profile_name)
end,
no_refresh_on_check = true,
checked_func = function()
return util.tableGetValue(self.autoexec, event, profile_name, conditions[3][2]) and true
end,
@@ -805,6 +808,7 @@ function Profiles:genAutoExecDocConditionalMenuItem(text, event, profile_name, s
enabled_func = function()
return not util.tableGetValue(self.autoexec, event_always, profile_name)
end,
no_refresh_on_check = true,
checked_func = function()
return util.tableGetValue(self.autoexec, event, profile_name, conditions[4][2]) and true
end,