[feat, UX] Add new OpenWithDialog based on RadioButton and RadioButtonTable (#3678)

Fixes #3659
pull/3679/head
Frans de Jonge 6 years ago committed by GitHub
parent f15732bbe5
commit dc0dc7e962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,8 +2,8 @@
This is a registry for document providers
]]--
local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
local ConfirmBox = require("ui/widget/confirmbox")
local OpenWithDialog = require("ui/widget/openwithdialog")
local UIManager = require("ui/uimanager")
local gettext = require("gettext")
local logger = require("logger")
@ -124,30 +124,36 @@ function DocumentRegistry:showSetProviderButtons(file, filemanager_instance, ui
local filename_suffix = util.getFileNameSuffix(file)
local buttons = {}
local radio_buttons = {}
local providers = self:getProviders(file)
for _, provider in ipairs(providers) do
-- we have no need for extension, mimetype, weights, etc. here
provider = provider.provider
table.insert(buttons, {
table.insert(radio_buttons, {
{
text = string.format("** %s **", provider.provider_name),
text = provider.provider_name,
checked = self:getProvider(file) == provider,
provider = provider,
},
})
table.insert(buttons, {
{
text = gettext("Just once"),
callback = function()
filemanager_instance:onClose()
reader_ui:showReader(file, provider)
UIManager:close(self.set_provider_dialog)
end,
},
})
table.insert(buttons, {
{
text = gettext("This document"),
callback = function()
end
table.insert(buttons, {
{
text = gettext("Cancel"),
callback = function()
UIManager:close(self.set_provider_dialog)
end,
},
{
text = gettext("Open"),
is_enter_default = true,
callback = function()
local provider = self.set_provider_dialog.radio_button_table.checked_button.provider
-- always for this file
if self.set_provider_dialog._check_file_button.checked then
UIManager:show(ConfirmBox:new{
text = T(gettext("Always open '%2' with %1?"),
provider.provider_name, filename_pure),
@ -160,13 +166,8 @@ function DocumentRegistry:showSetProviderButtons(file, filemanager_instance, ui
UIManager:close(self.set_provider_dialog)
end,
})
end,
},
})
table.insert(buttons, {
{
text = gettext("All documents"),
callback = function()
-- always for all files of this file type
elseif self.set_provider_dialog._check_global_button.checked then
UIManager:show(ConfirmBox:new{
text = T(gettext("Always open %2 files with %1?"),
provider.provider_name, filename_suffix),
@ -179,15 +180,19 @@ function DocumentRegistry:showSetProviderButtons(file, filemanager_instance, ui
UIManager:close(self.set_provider_dialog)
end,
})
end,
},
})
-- little trick for visual separation
table.insert(buttons, {})
end
else
-- just once
filemanager_instance:onClose()
reader_ui:showReader(file, provider)
UIManager:close(self.set_provider_dialog)
end
end,
},
})
self.set_provider_dialog = ButtonDialogTitle:new{
self.set_provider_dialog = OpenWithDialog:new{
title = T(gettext("Open %1 with:"), filename_pure),
radio_buttons = radio_buttons,
buttons = buttons,
}
UIManager:show(self.set_provider_dialog)

@ -49,6 +49,8 @@ function CheckButton:initCheckButton(checked)
self.checked = checked
self._checkmark = CheckMark:new{
checked = self.checked,
parent = self.parent or self,
show_parent = self.show_parent or self,
}
self._textwidget = TextWidget:new{
text = self.text,
@ -64,8 +66,9 @@ function CheckButton:initCheckButton(checked)
padding = 0,
self._horizontalgroup,
}
self[1] = self._frame
self.dimen = self._frame:getSize()
self[1] = self._frame
if Device:isTouchDevice() then
self.ges_events = {
TapCheckButton = {

@ -0,0 +1,157 @@
--[[--
This widget displays an open with dialog.
]]
local Blitbuffer = require("ffi/blitbuffer")
local CenterContainer = require("ui/widget/container/centercontainer")
local CheckButton = require("ui/widget/checkbutton")
local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry")
local InputDialog = require("ui/widget/inputdialog")
local LeftContainer = require("ui/widget/container/leftcontainer")
local LineWidget = require("ui/widget/linewidget")
local RadioButtonTable = require("ui/widget/radiobuttontable")
local Size = require("ui/size")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local _ = require("gettext")
local Screen = require("device").screen
local OpenWithDialog = InputDialog:extend{}
function OpenWithDialog:init()
-- init title and buttons in base class
InputDialog.init(self)
self.radio_button_table = RadioButtonTable:new{
radio_buttons = self.radio_buttons,
width = self.width * 0.9,
focused = true,
scroll = false,
parent = self,
}
self._check_file_button = self._check_file_button or CheckButton:new{
text = _("Always use this engine for this file"),
callback = function()
if self._check_file_button.checked then
self._check_file_button:unCheck()
else
self._check_file_button:check()
end
end,
width = self.width * 0.9,
height = self.height,
parent = self,
}
self._always_file_toggle = LeftContainer:new{
bordersize = 0,
dimen = Geom:new{
w = self.width * 0.9,
h = self._check_file_button:getSize().h,
},
self._check_file_button,
}
self._check_global_button = self._check_global_button or CheckButton:new{
text = _("Always use this engine for file type"),
callback = function()
if self._check_global_button.checked then
self._check_global_button:unCheck()
else
self._check_global_button:check()
end
end,
width = self.width * 0.9,
height = self.height,
parent = self,
}
self._always_global_toggle = LeftContainer:new{
bordersize = 0,
dimen = Geom:new{
w = self.width * 0.9,
h = self._check_global_button:getSize().h,
},
self._check_global_button,
}
self.dialog_frame = FrameContainer:new{
radius = Size.radius.window,
padding = 0,
margin = 0,
background = Blitbuffer.COLOR_WHITE,
VerticalGroup:new{
align = "left",
self.title,
self.title_bar,
VerticalSpan:new{
width = Size.span.vertical_large*2,
},
CenterContainer:new{
dimen = Geom:new{
w = self.title_bar:getSize().w,
h = self.radio_button_table:getSize().h,
},
self.radio_button_table,
},
CenterContainer:new{
dimen = Geom:new{
w = self.title_bar:getSize().w,
h = Size.span.vertical_large*2,
},
LineWidget:new{
background = Blitbuffer.COLOR_GREY,
dimen = Geom:new{
w = self.width * 0.9,
h = Size.line.medium,
}
},
},
CenterContainer:new{
dimen = Geom:new{
w = self.title_bar:getSize().w,
h = self._always_file_toggle:getSize().h,
},
self._always_file_toggle,
},
CenterContainer:new{
dimen = Geom:new{
w = self.title_bar:getSize().w,
h = self._always_global_toggle:getSize().h,
},
self._always_global_toggle,
},
VerticalSpan:new{
width = Size.span.vertical_large*2,
},
-- buttons
CenterContainer:new{
dimen = Geom:new{
w = self.title_bar:getSize().w,
h = self.button_table:getSize().h,
},
self.button_table,
}
}
}
self._input_widget = self.radio_button_table
self[1] = CenterContainer:new{
dimen = Geom:new{
w = Screen:getWidth(),
h = Screen:getHeight(),
},
self.dialog_frame,
}
end
function OpenWithDialog:onCloseWidget()
return true
end
return OpenWithDialog

@ -0,0 +1,167 @@
--[[--
Widget that shows a radiobutton checked (``) or unchecked (``)
or nothing of the same size.
Example:
local RadioButton = require("ui/widget/radiobutton")
local parent_widget = FrameContainer:new{}
table.insert(parent_widget, RadioButton:new{
checkable = false, -- shows nothing when false, defaults to true
checked = function() end, -- whether the box is checked
})
UIManager:show(parent_widget)
]]
local Blitbuffer = require("ffi/blitbuffer")
local Device = require("device")
local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange")
local InputContainer = require("ui/widget/container/inputcontainer")
local LeftContainer = require("ui/widget/container/leftcontainer")
local TextWidget = require("ui/widget/textwidget")
local UIManager = require("ui/uimanager")
local RadioButton = InputContainer:new{
checkable = true,
checked = false,
enabled = true,
face = Font:getFace("smallinfofont"),
background = Blitbuffer.COLOR_WHITE,
width = 0,
height = 0,
}
function RadioButton:init()
self._checked_widget = TextWidget:new{
text = "" .. self.text,
face = self.face,
}
self._unchecked_widget = TextWidget:new{
text = "" .. self.text,
face = self.face,
}
self._empty_widget = TextWidget:new{
text = "" .. self.text,
face = self.face,
}
self._widget_size = self._unchecked_widget:getSize()
if self.width == nil then
self.width = self._widget_size.w
end
self._radio_button = self.checkable
and (self.checked and self._checked_widget or self._unchecked_widget)
or self._empty_widget
self:update()
self.dimen = self.frame:getSize()
if Device:isTouchDevice() then
self.ges_events = {
TapCheckButton = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
doc = "Tap Button",
},
HoldCheckButton = {
GestureRange:new{
ges = "hold",
range = self.dimen,
},
doc = "Hold Button",
}
}
end
end
function RadioButton:update()
if self[1] then
self[1]:free()
end
self.frame = FrameContainer:new{
margin = self.margin,
bordersize = self.bordersize,
background = self.background,
radius = self.radius,
padding = self.padding,
LeftContainer:new{
dimen = Geom:new{
w = self.width,
h = self._widget_size.h
},
self._radio_button,
}
}
self[1] = self.frame
end
function RadioButton:onFocus()
self.frame.invert = true
return true
end
function RadioButton:onUnfocus()
self.frame.invert = false
return true
end
function RadioButton:onTapCheckButton()
if self.enabled and self.callback then
if G_reader_settings:isFalse("flash_ui") then
self.callback()
else
UIManager:scheduleIn(0.0, function()
self.invert = true
UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen
end)
end)
UIManager:scheduleIn(0.1, function()
self.callback()
self.invert = false
UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen
end)
end)
end
elseif self.tap_input then
self:onInput(self.tap_input)
elseif type(self.tap_input_func) == "function" then
self:onInput(self.tap_input_func())
end
return true
end
function RadioButton:onHoldCheckButton()
if self.enabled and self.hold_callback then
self.hold_callback()
elseif self.hold_input then
self:onInput(self.hold_input)
elseif type(self.hold_input_func) == "function" then
self:onInput(self.hold_input_func())
end
return true
end
function RadioButton:check(callback)
self._radio_button = self._checked_widget
self.checked = true
self:update()
UIManager:setDirty(self.parent, function()
return "ui", self.dimen
end)
end
function RadioButton:unCheck()
self._radio_button = self._unchecked_widget
self.checked = false
self:update()
UIManager:setDirty(self.parent, function()
return "ui", self.dimen
end)
end
return RadioButton

@ -0,0 +1,160 @@
local Blitbuffer = require("ffi/blitbuffer")
local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
local Geom = require("ui/geometry")
local HorizontalGroup = require("ui/widget/horizontalgroup")
local LineWidget = require("ui/widget/linewidget")
local RadioButton = require("ui/widget/radiobutton")
local Size = require("ui/size")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local dbg = require("dbg")
local Screen = Device.screen
local RadioButtonTable = FocusManager:new{
width = Screen:getWidth(),
radio_buttons = {
{
{text="Cancel", enabled=false, callback=nil},
{text="OK", enabled=true, callback=nil},
},
},
sep_width = Size.line.medium,
padding = Size.padding.button,
zero_sep = false,
button_font_face = "cfont",
button_font_size = 20,
_first_button = nil,
checked_button = nil,
}
function RadioButtonTable:init()
self.selected = { x = 1, y = 1 }
self.radio_buttons_layout = {}
self.container = VerticalGroup:new{ width = self.width }
table.insert(self, self.container)
if self.zero_sep then
-- If we're asked to add a first line, don't add a vspan before: caller
-- must do its own padding before.
-- Things look better when the first line is gray like the others.
self:addHorizontalSep(false, true, true)
else
self:addHorizontalSep(false, false, true)
end
local row_cnt = #self.radio_buttons
for i = 1, row_cnt do
self.radio_buttons_layout[i] = {}
local horizontal_group = HorizontalGroup:new{}
local row = self.radio_buttons[i]
local column_cnt = #row
local sizer_space = self.sep_width * (column_cnt - 1) + 2
for j = 1, column_cnt do
local btn_entry = row[j]
local button = RadioButton:new{
text = btn_entry.text,
enabled = btn_entry.enabled,
checked = btn_entry.checked,
provider = btn_entry.provider,
width = (self.width - sizer_space)/column_cnt,
max_width = (self.width - sizer_space)/column_cnt - 2*self.sep_width - 2*self.padding,
bordersize = 0,
margin = 0,
padding = 0,
text_font_face = self.button_font_face,
text_font_size = self.button_font_size,
show_parent = self.show_parent or self,
parent = self.parent or self,
}
local button_callback = function()
self:_checkButton(button)
end
button.callback = button_callback
if i == 1 and j == 1 then
self._first_button = button
end
if button.checked and not self.checked_button then
self.checked_button = button
elseif dbg.is_on and
button.checked and self.checked_button then
error("RadioButtonGroup: multiple checked RadioButtons")
end
local button_dim = button:getSize()
local vertical_sep = LineWidget:new{
background = Blitbuffer.COLOR_GREY,
dimen = Geom:new{
w = self.sep_width,
h = button_dim.h,
}
}
self.radio_buttons_layout[i][j] = button
table.insert(horizontal_group, button)
if j < column_cnt then
table.insert(horizontal_group, vertical_sep)
end
end -- end for each button
table.insert(self.container, horizontal_group)
--if i < row_cnt then
--self:addHorizontalSep(true, true, true)
--end
end -- end for each button line
self:addHorizontalSep(true, false, false)
-- check first entry unless otherwise specified
if not self.checked_button then
self._first_button:check()
self.checked_button = self._first_button
end
if Device:hasDPad() or Device:hasKeyboard() then
self.layout = self.radio_buttons_layout
self.layout[1][1]:onFocus()
self.key_events.SelectByKeyPress = { {{"Press", "Enter"}} }
else
self.key_events = {} -- deregister all key press event listeners
end
end
function RadioButtonTable:addHorizontalSep(vspan_before, add_line, vspan_after, black_line)
if vspan_before then
table.insert(self.container,
VerticalSpan:new{ width = Size.span.vertical_default })
end
if add_line then
table.insert(self.container, LineWidget:new{
background = black_line and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_GREY,
dimen = Geom:new{
w = self.width,
h = self.sep_width,
}
})
end
if vspan_after then
table.insert(self.container,
VerticalSpan:new{ width = Size.span.vertical_default })
end
end
function RadioButtonTable:onSelectByKeyPress()
self:getFocusItem().callback()
end
function RadioButtonTable:_checkButton(button)
-- nothing to do
if button.checked then return end
self.checked_button:unCheck()
button:check()
self.checked_button = button
end
return RadioButtonTable
Loading…
Cancel
Save