diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index 81cd01fb1..0092372ab 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -259,6 +259,12 @@ function ReaderHighlight:onReaderReady() self:setupTouchZones() end +local highlight_style = { + {_("Lighten"), "lighten"}, + {_("Underline"), "underscore"}, + {_("Invert"), "invert"}, +} + local long_press_action = { {_("Ask with popup dialog"), "ask"}, {_("Do nothing"), "nothing"}, @@ -273,8 +279,60 @@ function ReaderHighlight:addToMainMenu(menu_items) -- insert table to main reader menu menu_items.highlight_options = { text = _("Highlight style"), - sub_item_table = self:genHighlightDrawerMenu(), + sub_item_table = {}, } + for _, v in ipairs(highlight_style) do + table.insert(menu_items.highlight_options.sub_item_table, { + text_func = function() + local text = v[1] + if v[2] == G_reader_settings:readSetting("highlight_drawing_style") then + text = text .. " ★" + end + return text + end, + checked_func = function() + return self.view.highlight.saved_drawer == v[2] + end, + callback = function() + self.view.highlight.saved_drawer = v[2] + end, + hold_callback = function(touchmenu_instance) + G_reader_settings:saveSetting("highlight_drawing_style", v[2]) + if touchmenu_instance then touchmenu_instance:updateItems() end + end, + }) + end + table.insert(menu_items.highlight_options.sub_item_table, { + text_func = function() + return T(_("Highlight opacity: %1"), G_reader_settings:readSetting("highlight_lighten_factor", 0.2)) + end, + enabled_func = function() + return self.view.highlight.saved_drawer == "lighten" + end, + callback = function(touchmenu_instance) + local SpinWidget = require("ui/widget/spinwidget") + local curr_val = G_reader_settings:readSetting("highlight_lighten_factor", 0.2) + local spin_widget = SpinWidget:new{ + value = curr_val, + value_min = 0, + value_max = 1, + precision = "%.2f", + value_step = 0.1, + value_hold_step = 0.25, + default_value = 0.2, + keep_shown_on_apply = true, + title_text = _("Highlight opacity"), + info_text = _("The higher the value, the darker the highlight."), + callback = function(spin) + G_reader_settings:saveSetting("highlight_lighten_factor", spin.value) + self.view.highlight.lighten_factor = spin.value + UIManager:setDirty(self.dialog, "ui") + if touchmenu_instance then touchmenu_instance:updateItems() end + end + } + UIManager:show(spin_widget) + end, + }) if self.document.info.has_pages then menu_items.panel_zoom_options = { text = _("Panel zoom (manga/comic)"), @@ -314,12 +372,6 @@ function ReaderHighlight:addToMainMenu(menu_items) end end -local highlight_style = { - lighten = _("Lighten"), - underscore = _("Underline"), - invert = _("Invert"), -} - function ReaderHighlight:genPanelZoomMenu() return { { @@ -354,65 +406,6 @@ function ReaderHighlight:genPanelZoomMenu() } end -function ReaderHighlight:genHighlightDrawerMenu() - local get_highlight_style = function(style) - return { - text_func = function() - local text = highlight_style[style] - if style == G_reader_settings:readSetting("highlight_drawing_style") then - text = text .. " ★" - end - return text - end, - checked_func = function() - return self.view.highlight.saved_drawer == style - end, - callback = function() - self.view.highlight.saved_drawer = style - end, - hold_callback = function(touchmenu_instance) - G_reader_settings:saveSetting("highlight_drawing_style", style) - if touchmenu_instance then touchmenu_instance:updateItems() end - end, - } - end - return { - get_highlight_style("lighten"), - get_highlight_style("underscore"), - get_highlight_style("invert"), - { - text_func = function() - return T(_("Highlight opacity: %1"), G_reader_settings:readSetting("highlight_lighten_factor", 0.2)) - end, - enabled_func = function() - return self.view.highlight.saved_drawer == "lighten" - end, - callback = function() - local SpinWidget = require("ui/widget/spinwidget") - local curr_val = G_reader_settings:readSetting("highlight_lighten_factor", 0.2) - local items = SpinWidget:new{ - value = curr_val, - value_min = 0, - value_max = 1, - precision = "%.2f", - value_step = 0.1, - value_hold_step = 0.25, - default_value = 0.2, - keep_shown_on_apply = true, - title_text = _("Highlight opacity"), - info_text = _("The higher the value, the darker the highlight."), - callback = function(spin) - G_reader_settings:saveSetting("highlight_lighten_factor", spin.value) - self.view.highlight.lighten_factor = spin.value - UIManager:setDirty(self.dialog, "ui") - end - } - UIManager:show(items) - end, - }, - } -end - -- Returns a unique id, that can be provided on delayed call to :clear(id) -- to ensure current highlight has not already been cleared, and that we -- are not going to clear a new highlight @@ -611,7 +604,7 @@ function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_c page = highlight_beginning, datetime = highlight_time, updated_highlight = new_highlight - }, true) + }) if side == 0 then -- Ensure we show the page with the new beginning of highlight if not self.ui.document:isXPointerInCurrentPage(new_beginning) then @@ -636,6 +629,11 @@ function ReaderHighlight:updateHighlight(page, index, side, direction, move_by_c end function ReaderHighlight:onShowHighlightDialog(page, index) + local item = self.view.highlight.saved[page][index] + local is_auto_text = self.ui.bookmark:isHighlightAutoText({ + page = self.ui.document.info.has_pages and item.pos0.page or item.pos0, + datetime = item.datetime, + }) local buttons = { { { @@ -648,7 +646,15 @@ function ReaderHighlight:onShowHighlightDialog(page, index) end, }, { - text = _("Edit"), + text = _("Style"), + callback = function() + self:editHighlightStyle(page, index) + UIManager:close(self.edit_highlight_dialog) + self.edit_highlight_dialog = nil + end, + }, + { + text = is_auto_text and _("Add note") or _("Edit note"), callback = function() self:editHighlight(page, index) UIManager:close(self.edit_highlight_dialog) @@ -1278,15 +1284,21 @@ function ReaderHighlight:onCycleHighlightAction() end function ReaderHighlight:onCycleHighlightStyle() - local next_actions = { - lighten = "underscore", - underscore = "invert", - invert = "lighten" - } - self.view.highlight.saved_drawer = next_actions[self.view.highlight.saved_drawer] + local current_style = self.view.highlight.saved_drawer + local next_style_num + for i, v in ipairs(highlight_style) do + if v[2] == current_style then + next_style_num = i + 1 + break + end + end + if next_style_num > #highlight_style then + next_style_num = 1 + end + self.view.highlight.saved_drawer = highlight_style[next_style_num][2] self.ui.doc_settings:saveSetting("highlight_drawer", self.view.highlight.saved_drawer) UIManager:show(Notification:new{ - text = T(_("Default highlight style changed to '%1'."), highlight_style[self.view.highlight.saved_drawer]), + text = T(_("Default highlight style changed to '%1'."), highlight_style[next_style_num][1]), }) return true end @@ -1546,6 +1558,32 @@ function ReaderHighlight:editHighlight(page, i) }, true) end +function ReaderHighlight:editHighlightStyle(page, i) + local item = self.view.highlight.saved[page][i] + local radio_buttons = {} + for _, v in ipairs(highlight_style) do + table.insert(radio_buttons, { + { + text = v[1], + checked = item.drawer == v[2], + provider = v[2], + }, + }) + end + UIManager:show(require("ui/widget/radiobuttonwidget"):new{ + title_text = _("Highlight style"), + width_factor = 0.5, + keep_shown_on_apply = true, + radio_buttons = radio_buttons, + default_provider = self.view.highlight.saved_drawer or + G_reader_settings:readSetting("highlight_drawing_style", "lighten"), + callback = function(radio) + self.view.highlight.saved[page][i].drawer = radio.provider + UIManager:setDirty(self.dialog, "ui") + end, + }) +end + function ReaderHighlight:onReadSettings(config) self.view.highlight.saved_drawer = config:readSetting("highlight_drawer") or G_reader_settings:readSetting("highlight_drawing_style") or self.view.highlight.saved_drawer diff --git a/frontend/ui/widget/radiobutton.lua b/frontend/ui/widget/radiobutton.lua index f6b9f4393..350ad7b30 100644 --- a/frontend/ui/widget/radiobutton.lua +++ b/frontend/ui/widget/radiobutton.lua @@ -20,8 +20,10 @@ local Font = require("ui/font") local FrameContainer = require("ui/widget/container/framecontainer") local Geom = require("ui/geometry") local GestureRange = require("ui/gesturerange") +local HorizontalGroup = require("ui/widget/horizontalgroup") local InputContainer = require("ui/widget/container/inputcontainer") local LeftContainer = require("ui/widget/container/leftcontainer") +local TextBoxWidget = require("ui/widget/textboxwidget") local TextWidget = require("ui/widget/textwidget") local UIManager = require("ui/uimanager") @@ -36,20 +38,51 @@ local RadioButton = InputContainer:new{ } function RadioButton:init() - self._checked_widget = TextWidget:new{ - text = "◉ " .. self.text, + local fgcolor = self.enabled and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_DARK_GRAY + local dummy_widget = TextWidget:new{ -- to get width of radiomark + text = "◉ ", face = self.face, - max_width = self.max_width, } - self._unchecked_widget = TextWidget:new{ - text = "◯ " .. self.text, + local radiomark_width = dummy_widget:getSize().w + dummy_widget:free() + local text_widget = TextBoxWidget:new{ + text = self.text, face = self.face, - max_width = self.max_width, + width = self.max_width - radiomark_width, + fgcolor = fgcolor, } - self._empty_widget = TextWidget:new{ - text = "" .. self.text, + local checked_widget = TextBoxWidget:new{ + text = "◉ ", face = self.face, - max_width = self.max_width, + width = radiomark_width, + fgcolor = fgcolor, + } + local unchecked_widget = TextBoxWidget:new{ + text = "◯ ", + face = self.face, + width = radiomark_width, + fgcolor = fgcolor, + } + local empty_widget = TextBoxWidget:new{ + text = "", + face = self.face, + width = radiomark_width, + fgcolor = fgcolor, + } + self._checked_widget = HorizontalGroup:new{ + align = "top", + checked_widget, + text_widget, + } + self._unchecked_widget = HorizontalGroup:new{ + align = "top", + unchecked_widget, + text_widget, + } + self._empty_widget = HorizontalGroup:new{ + align = "top", + empty_widget, + text_widget, } self._widget_size = self._unchecked_widget:getSize() if self.width == nil then diff --git a/frontend/ui/widget/radiobuttonwidget.lua b/frontend/ui/widget/radiobuttonwidget.lua new file mode 100644 index 000000000..7fc75e4ec --- /dev/null +++ b/frontend/ui/widget/radiobuttonwidget.lua @@ -0,0 +1,267 @@ +local Blitbuffer = require("ffi/blitbuffer") +local ButtonTable = require("ui/widget/buttontable") +local CenterContainer = require("ui/widget/container/centercontainer") +local Device = require("device") +local FrameContainer = require("ui/widget/container/framecontainer") +local Geom = require("ui/geometry") +local GestureRange = require("ui/gesturerange") +local Font = require("ui/font") +local HorizontalGroup = require("ui/widget/horizontalgroup") +local InputContainer = require("ui/widget/container/inputcontainer") +local LineWidget = require("ui/widget/linewidget") +local MovableContainer = require("ui/widget/container/movablecontainer") +local RadioButtonTable = require("ui/widget/radiobuttontable") +local Size = require("ui/size") +local TextBoxWidget = require("ui/widget/textboxwidget") +local TextWidget = require("ui/widget/textwidget") +local UIManager = require("ui/uimanager") +local VerticalGroup = require("ui/widget/verticalgroup") +local WidgetContainer = require("ui/widget/container/widgetcontainer") +local _ = require("gettext") +local Screen = Device.screen + +local RadioButtonWidget = InputContainer:new{ + title_text = "", + title_face = Font:getFace("x_smalltfont"), + info_text = nil, + width = nil, + width_factor = nil, + height = nil, + radio_buttons = nil, -- row x column table + cancel_text = _("Close"), + ok_text = _("Apply"), + cancel_callback = nil, + callback = nil, + close_callback = nil, + keep_shown_on_apply = false, + default_provider = nil, + default_text = _("Use default"), + extra_text = nil, + extra_callback = nil, + -- output + provider = nil, -- provider of the checked button + row = nil, -- row of the checked button + col = nil, -- column of the checked button +} + +function RadioButtonWidget:init() + self.screen_width = Screen:getWidth() + self.screen_height = Screen:getHeight() + if not self.width then + if not self.width_factor then + self.width_factor = 0.6 + end + self.width = math.floor(math.min(self.screen_width, self.screen_height) * self.width_factor) + end + if Device:hasKeys() then + self.key_events = { + Close = { {"Back"}, doc = "close widget" } + } + end + if Device:isTouchDevice() then + self.ges_events = { + TapClose = { + GestureRange:new{ + ges = "tap", + range = Geom:new{ + w = self.screen_width, + h = self.screen_height, + } + }, + }, + } + end + self:update() +end + +function RadioButtonWidget:update() + if self.default_provider then + local row, col = self:getButtonIndex(self.default_provider) + self.radio_buttons[row][col].text = self.radio_buttons[row][col].text .. "\u{A0}\u{A0}★" + end + + local value_widget = RadioButtonTable:new{ + radio_buttons = self.radio_buttons, + width = math.floor(self.width * 0.9), + focused = true, + scroll = false, + parent = self, + face = self.face, + } + local value_group = HorizontalGroup:new{ + align = "center", + value_widget, + } + + local value_title = FrameContainer:new{ + padding = Size.padding.default, + margin = Size.margin.title, + bordersize = 0, + TextWidget:new{ + text = self.title_text, + max_width = self.width - 2 * (Size.padding.default + Size.margin.title), + face = self.title_face, + }, + } + local value_line = LineWidget:new{ + dimen = Geom:new{ + w = self.width, + h = Size.line.thick, + } + } + local buttons = { + { + { + text = self.cancel_text, + callback = function() + if self.cancel_callback then + self.cancel_callback() + end + self:onClose() + end, + }, + { + text = self.ok_text, + callback = function() + if self.callback then + self.provider = value_widget.checked_button.provider + self.row, self.col = self:getButtonIndex(self.provider) + self.callback(self) + end + if not self.keep_shown_on_apply then + self:onClose() + end + end, + }, + } + } + + if self.extra_text then + table.insert(buttons,{ + { + text = self.extra_text, + callback = function() + if self.extra_callback then + self.provider = value_widget.checked_button.provider + self.row, self.col = self:getButtonIndex(self.provider) + self.extra_callback(self) + end + if not self.keep_shown_on_apply then + self:onClose() + end + end, + }, + }) + end + + local ok_cancel_buttons = ButtonTable:new{ + width = self.width - 2*Size.padding.default, + buttons = buttons, + zero_sep = true, + show_parent = self, + } + + local vgroup = VerticalGroup:new{ + align = "left", + value_title, + value_line, + } + if self.info_text then + table.insert(vgroup, FrameContainer:new{ + padding = Size.padding.default, + margin = Size.margin.small, + bordersize = 0, + TextBoxWidget:new{ + text = self.info_text, + face = Font:getFace("x_smallinfofont"), + width = math.floor(self.width * 0.9), + } + }) + end + table.insert(vgroup, CenterContainer:new{ + dimen = Geom:new{ + w = self.width, + h = value_group:getSize().h + 4 * Size.padding.large, + }, + value_group + }) + table.insert(vgroup, CenterContainer:new{ + dimen = Geom:new{ + w = self.width, + h = ok_cancel_buttons:getSize().h, + }, + ok_cancel_buttons + }) + self.widget_frame = FrameContainer:new{ + radius = Size.radius.window, + padding = 0, + margin = 0, + background = Blitbuffer.COLOR_WHITE, + vgroup, + } + self.movable = MovableContainer:new{ + self.widget_frame, + } + self[1] = WidgetContainer:new{ + align = "center", + dimen = Geom:new{ + x = 0, y = 0, + w = self.screen_width, + h = self.screen_height, + }, + self.movable, + } + UIManager:setDirty(self, function() + return "ui", self.widget_frame.dimen + end) +end + +function RadioButtonWidget:getButtonIndex(provider) + for i = 1, #self.radio_buttons do -- rows + for j = 1, #self.radio_buttons[i] do -- columns + if self.radio_buttons[i][j].provider == provider then + return i, j + end + end + end +end + +function RadioButtonWidget:hasMoved() + local offset = self.movable:getMovedOffset() + return offset.x ~= 0 or offset.y ~= 0 +end + +function RadioButtonWidget:onCloseWidget() + UIManager:setDirty(nil, function() + return "ui", self.widget_frame.dimen + end) +end + +function RadioButtonWidget:onShow() + UIManager:setDirty(self, function() + return "ui", self.widget_frame.dimen + end) + return true +end + +function RadioButtonWidget:onAnyKeyPressed() + self:onClose() + return true +end + +function RadioButtonWidget:onTapClose(arg, ges_ev) + if ges_ev.pos:notIntersectWith(self.widget_frame.dimen) then + self:onClose() + end + return true +end + +function RadioButtonWidget:onClose() + UIManager:close(self) + if self.close_callback then + self.close_callback() + end + return true +end + +return RadioButtonWidget