mirror of https://github.com/koreader/koreader
[feat, UX] Add new OpenWithDialog based on RadioButton and RadioButtonTable (#3678)
Fixes #3659pull/3679/head
parent
f15732bbe5
commit
dc0dc7e962
@ -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…
Reference in New Issue