[RTL UI] update widgets and apps for UI mirroring

Small tweaks all around to handle UI mirroring:
- swap existing symbols like arrows, or use alternative ones
- rotate some images, like chevrons and dogear icons
- flip some left and right swipe handling
- flip some geometry arithmetic like tap on left or right
  side of page or dict window
- use new ProgressWidget:getPercentageFromPosition() instead
  of geometry arithmetic
- BD.wrap() some concatenated string bits, like in reader
  and menu footers
- flip inverse_reading_order when UI is mirrored

More specific tweaks:
- ReaderGesture: reset some specific gestures when UI direction
  has changed (tap on top/bottom left/right corners, for
  bookmarks and FileManager "Plus menu").
- ReaderRolling: show markers on the correct side of page,
  in single or dual page mode.
- KoptOptions: swap left and right icons in Alignment toggle
- CheckMark: proper rendering in all 4 mirroring/rtl combinations.
- VirtualKeyboard: forbid any mirroring
- Move util.getMenuText into Menu.lua
pull/5680/head
poire-z 4 years ago
parent 36ce82d8c2
commit 7952fa2c09

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
local ButtonDialogTitle = require("ui/widget/buttondialogtitle") local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
@ -457,9 +458,10 @@ function FileManager:onShowPlusMenu()
end end
function FileManager:onSwipeFM(ges) function FileManager:onSwipeFM(ges)
if ges.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges.direction)
if direction == "west" then
self.file_chooser:onNextPage() self.file_chooser:onNextPage()
elseif ges.direction == "east" then elseif direction == "east" then
self.file_chooser:onPrevPage() self.file_chooser:onPrevPage()
end end
return true return true

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local CloudStorage = require("apps/cloudstorage/cloudstorage") local CloudStorage = require("apps/cloudstorage/cloudstorage")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
@ -611,10 +612,10 @@ function FileManagerMenu:_getTabIndexFromLocation(ges)
return last_tab_index return last_tab_index
-- if the start position is far right -- if the start position is far right
elseif ges.pos.x > 2 * Screen:getWidth() / 3 then elseif ges.pos.x > 2 * Screen:getWidth() / 3 then
return #self.tab_item_table return BD.mirroredUILayout() and 1 or #self.tab_item_table
-- if the start position is far left -- if the start position is far left
elseif ges.pos.x < Screen:getWidth() / 3 then elseif ges.pos.x < Screen:getWidth() / 3 then
return 1 return BD.mirroredUILayout() and #self.tab_item_table or 1
-- if center return the last index -- if center return the last index
else else
return last_tab_index return last_tab_index

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device") local Device = require("device")
@ -231,7 +232,7 @@ function ReaderBookmark:onShowBookmark()
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
}, },
direction = "east" direction = BD.flipDirectionIfMirroredUILayout("east")
} }
} }
} }

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Device = require("device") local Device = require("device")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local ImageWidget = require("ui/widget/imagewidget") local ImageWidget = require("ui/widget/imagewidget")
@ -33,6 +34,7 @@ function ReaderDogear:setupDogear(new_dogear_size)
dimen = Geom:new{w = Screen:getWidth(), h = self.dogear_size}, dimen = Geom:new{w = Screen:getWidth(), h = self.dogear_size},
ImageWidget:new{ ImageWidget:new{
file = "resources/icons/dogear.png", file = "resources/icons/dogear.png",
rotation_angle = BD.mirroredUILayout() and 90 or 0,
width = self.dogear_size, width = self.dogear_size,
height = self.dogear_size, height = self.dogear_size,
} }

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
@ -21,6 +22,7 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer")
local util = require("util") local util = require("util")
local T = require("ffi/util").template local T = require("ffi/util").template
local _ = require("gettext") local _ = require("gettext")
local C_ = _.pgettext
local Screen = Device.screen local Screen = Device.screen
local MODE = { local MODE = {
@ -41,26 +43,45 @@ local symbol_prefix = {
letters = { letters = {
time = nil, time = nil,
pages_left = "=>", pages_left = "=>",
battery = "B:", battery = C_("FooterLetterPrefix", "B:"),
percentage = "R:", percentage = C_("FooterLetterPrefix", "R:"),
book_time_to_read = "TB:", book_time_to_read = C_("FooterLetterPrefix", "TB:"),
chapter_time_to_read = "TC:", chapter_time_to_read = C_("FooterLetterPrefix", "TC:"),
frontlight = "L:", frontlight = C_("FooterLetterPrefix", "L:"),
mem_usage = "M:", mem_usage = C_("FooterLetterPrefix", "M:"),
wifi_status = "W:", wifi_status = C_("FooterLetterPrefix", "W:"),
}, },
icons = { icons = {
time = "", time = "",
pages_left = "", pages_left = BD.mirroredUILayout() and "" or "",
battery = "", battery = "",
percentage = "", percentage = BD.mirroredUILayout() and "" or "",
book_time_to_read = "", book_time_to_read = "",
chapter_time_to_read = "", chapter_time_to_read = BD.mirroredUILayout() and "" or "",
frontlight = "", frontlight = "",
mem_usage = "", mem_usage = "",
wifi_status = "", wifi_status = "",
} }
} }
if BD.mirroredUILayout() then
-- We need to RTL-wrap these letters and symbols for proper layout
for k, v in pairs(symbol_prefix.letters) do
local colon = v:find(":")
local wrapped
if colon then
local pre = v:sub(1, colon-1)
local post = v:sub(colon)
wrapped = BD.wrap(pre) .. BD.wrap(post)
else
wrapped = BD.wrap(v)
end
symbol_prefix.letters[k] = wrapped
end
for k, v in pairs(symbol_prefix.icons) do
symbol_prefix.icons[k] = BD.wrap(v)
end
end
local PROGRESS_BAR_STYLE_THICK_DEFAULT_HEIGHT = 7 local PROGRESS_BAR_STYLE_THICK_DEFAULT_HEIGHT = 7
local PROGRESS_BAR_STYLE_THIN_DEFAULT_HEIGHT = 3 local PROGRESS_BAR_STYLE_THIN_DEFAULT_HEIGHT = 3
@ -115,9 +136,9 @@ local footerTextGeneratorMap = {
prefix = "" prefix = ""
end end
end end
return prefix .. batt_lvl .. "%" return BD.wrap(prefix) .. batt_lvl .. "%"
else else
return prefix .. " " .. (powerd:isCharging() and "+" or "") .. batt_lvl .. "%" return BD.wrap(prefix) .. " " .. (powerd:isCharging() and "+" or "") .. batt_lvl .. "%"
end end
end, end,
time = function(footer) time = function(footer)
@ -1230,10 +1251,13 @@ function ReaderFooter:genAllFooterText()
elseif self.settings.items_separator == "bullet" then elseif self.settings.items_separator == "bullet" then
separator = "" separator = ""
end end
-- We need to BD.wrap() all items and separators, so we're
-- sure they are laid out in our order (reversed in RTL),
-- without ordering by the RTL Bidi algorithm.
for _, gen in ipairs(self.footerTextGenerators) do for _, gen in ipairs(self.footerTextGenerators) do
table.insert(info, gen(self)) table.insert(info, BD.wrap(gen(self)))
end end
return table.concat(info, separator) return table.concat(info, BD.wrap(separator))
end end
function ReaderFooter:setTocMarkers(reset) function ReaderFooter:setTocMarkers(reset)

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local DataStorage = require("datastorage") local DataStorage = require("datastorage")
local Device = require("device") local Device = require("device")
@ -225,6 +226,44 @@ function ReaderGesture:init()
} }
local gm = G_reader_settings:readSetting(self.ges_mode) local gm = G_reader_settings:readSetting(self.ges_mode)
if gm == nil then G_reader_settings:saveSetting(self.ges_mode, {}) end if gm == nil then G_reader_settings:saveSetting(self.ges_mode, {}) end
-- Some of these defaults need to be reversed in RTL mirrored UI,
-- and as we set them in the saved gestures, we need to reset them
-- to the defaults in case of UI language's direction change.
local mirrored_if_rtl = {
tap_top_left_corner = "tap_top_right_corner",
tap_right_bottom_corner = "tap_left_bottom_corner",
}
local is_rtl = BD.mirroredUILayout()
if is_rtl then
for k, v in pairs(mirrored_if_rtl) do
self.default_gesture[k], self.default_gesture[v] = self.default_gesture[v], self.default_gesture[k]
end
end
-- We remember the last UI direction gestures were made on. If it changes,
-- reset the mirrored_if_rtl ones to the default for the new direction.
local ges_dir_setting = self.ges_mode.."ui_lang_direction_rtl"
local prev_lang_dir_rtl = G_reader_settings:isTrue(ges_dir_setting)
if (is_rtl and not prev_lang_dir_rtl) or (not is_rtl and prev_lang_dir_rtl) then
local reset = false
for k, v in pairs(mirrored_if_rtl) do
-- We only replace them if they are still the other direction's default.
-- If not, the user has changed them: let him deal with setting new ones if needed.
if gm[k] == self.default_gesture[v] then
gm[k] = self.default_gesture[k]
reset = true
end
if gm[v] == self.default_gesture[k] then
gm[v] = self.default_gesture[v]
reset = true
end
end
if reset then
logger.info("UI language direction changed: resetting some gestures to direction default")
end
G_reader_settings:flipNilOrFalse(ges_dir_setting)
end
self.ui.menu:registerToMainMenu(self) self.ui.menu:registerToMainMenu(self)
self:initGesture() self:initGesture()
end end
@ -1577,8 +1616,12 @@ function ReaderGesture:onToggleReadingOrder()
local document_module = self.ui.document.info.has_pages and self.ui.paging or self.ui.rolling local document_module = self.ui.document.info.has_pages and self.ui.paging or self.ui.rolling
document_module.inverse_reading_order = not document_module.inverse_reading_order document_module.inverse_reading_order = not document_module.inverse_reading_order
document_module:setupTouchZones() document_module:setupTouchZones()
local is_rtl = BD.mirroredUILayout()
if document_module.inverse_reading_order then
is_rtl = not is_rtl
end
UIManager:show(Notification:new{ UIManager:show(Notification:new{
text = document_module.inverse_reading_order and _("RTL page turning.") or _("LTR page turning."), text = is_rtl and _("RTL page turning.") or _("LTR page turning."),
timeout = 2.5, timeout = 2.5,
}) })
return true return true

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local ButtonDialog = require("ui/widget/buttondialog") local ButtonDialog = require("ui/widget/buttondialog")
local Device = require("device") local Device = require("device")
local Event = require("ui/event") local Event = require("ui/event")
@ -350,9 +351,19 @@ function ReaderHighlight:onShowHighlightDialog(page, index)
} }
if not self.ui.document.info.has_pages then if not self.ui.document.info.has_pages then
local start_prev = "◁⇱"
local start_next = "⇱▷"
local end_prev = "◁⇲"
local end_next = "⇲▷"
if BD.mirroredUILayout() then
-- Sadly, there's only north west & south east arrow to corner,
-- north east and south west do not exist in Unicode.
start_prev, start_next = BD.ltr(start_next), BD.ltr(start_prev)
end_prev, end_next = BD.ltr(end_next), BD.ltr(end_prev)
end
table.insert(buttons, { table.insert(buttons, {
{ {
text = "◁⇱", text = start_prev,
callback = function() callback = function()
self:updateHighlight(page, index, 0, -1, false) self:updateHighlight(page, index, 0, -1, false)
end, end,
@ -362,7 +373,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index)
end end
}, },
{ {
text = "⇱▷", text = start_next,
callback = function() callback = function()
self:updateHighlight(page, index, 0, 1, false) self:updateHighlight(page, index, 0, 1, false)
end, end,
@ -372,7 +383,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index)
end end
}, },
{ {
text = "◁⇲", text = end_prev,
callback = function() callback = function()
self:updateHighlight(page, index, 1, -1, false) self:updateHighlight(page, index, 1, -1, false)
end, end,
@ -381,7 +392,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index)
end end
}, },
{ {
text = "⇲▷", text = end_next,
callback = function() callback = function()
self:updateHighlight(page, index, 1, 1, false) self:updateHighlight(page, index, 1, 1, false)
end, end,
@ -577,6 +588,14 @@ function ReaderHighlight:onHoldPan(_, ges)
and self.holdpan_pos.x < 1/8*Screen:getWidth() and self.holdpan_pos.x < 1/8*Screen:getWidth()
local is_in_bottom_right_corner = self.holdpan_pos.y > 7/8*Screen:getHeight() local is_in_bottom_right_corner = self.holdpan_pos.y > 7/8*Screen:getHeight()
and self.holdpan_pos.x > 7/8*Screen:getWidth() and self.holdpan_pos.x > 7/8*Screen:getWidth()
if BD.mirroredUILayout() then
-- Note: this might not be really usable, as crengine native selection
-- is not adapted to RTL text
is_in_top_left_corner = self.holdpan_pos.y < 1/8*Screen:getHeight()
and self.holdpan_pos.x > 7/8*Screen:getWidth()
is_in_bottom_right_corner = self.holdpan_pos.y > 7/8*Screen:getHeight()
and self.holdpan_pos.x < 1/8*Screen:getWidth()
end
if is_in_top_left_corner or is_in_bottom_right_corner then if is_in_top_left_corner or is_in_bottom_right_corner then
if self.was_in_some_corner then if self.was_in_some_corner then
-- Do nothing, wait for the user to move his finger out of that corner -- Do nothing, wait for the user to move his finger out of that corner

@ -2,6 +2,7 @@
ReaderLink is an abstraction for document-specific link interfaces. ReaderLink is an abstraction for document-specific link interfaces.
]] ]]
local BD = require("ui/bidi")
local ButtonDialogTitle = require("ui/widget/buttondialogtitle") local ButtonDialogTitle = require("ui/widget/buttondialogtitle")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device") local Device = require("device")
@ -689,7 +690,8 @@ function ReaderLink:onGoBackLink(show_notification_if_empty)
end end
function ReaderLink:onSwipe(arg, ges) function ReaderLink:onSwipe(arg, ges)
if ges.direction == "east" then local direction = BD.flipDirectionIfMirroredUILayout(ges.direction)
if direction == "east" then
if isSwipeToGoBackEnabled() then if isSwipeToGoBackEnabled() then
if #self.location_stack > 0 then if #self.location_stack > 0 then
-- Remember if location stack is going to be empty, so we -- Remember if location stack is going to be empty, so we
@ -709,7 +711,7 @@ function ReaderLink:onSwipe(arg, ges)
return true return true
end end
end end
elseif ges.direction == "west" then elseif direction == "west" then
local ret = false local ret = false
if isSwipeToFollowNearestLinkEnabled() then if isSwipeToFollowNearestLinkEnabled() then
ret = self:onGoToPageLink(ges, isSwipeIgnoreExternalLinksEnabled(), isFootnoteLinkInPopupEnabled()) ret = self:onGoToPageLink(ges, isSwipeIgnoreExternalLinksEnabled(), isFootnoteLinkInPopupEnabled())

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device") local Device = require("device")
@ -373,10 +374,10 @@ function ReaderMenu:_getTabIndexFromLocation(ges)
return self.last_tab_index return self.last_tab_index
-- if the start position is far right -- if the start position is far right
elseif ges.pos.x > 2 * Screen:getWidth() / 3 then elseif ges.pos.x > 2 * Screen:getWidth() / 3 then
return #self.tab_item_table return BD.mirroredUILayout() and 1 or #self.tab_item_table
-- if the start position is far left -- if the start position is far left
elseif ges.pos.x < Screen:getWidth() / 3 then elseif ges.pos.x < Screen:getWidth() / 3 then
return 1 return BD.mirroredUILayout() and #self.tab_item_table or 1
-- if center return the last index -- if center return the last index
else else
return self.last_tab_index return self.last_tab_index

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Device = require("device") local Device = require("device")
local Event = require("ui/event") local Event = require("ui/event")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
@ -111,7 +112,11 @@ function ReaderPaging:setupTapTouchZones()
ratio_w = DTAP_ZONE_BACKWARD.w, ratio_h = DTAP_ZONE_BACKWARD.h, ratio_w = DTAP_ZONE_BACKWARD.w, ratio_h = DTAP_ZONE_BACKWARD.h,
} }
local do_mirror = BD.mirroredUILayout()
if self.inverse_reading_order then if self.inverse_reading_order then
do_mirror = not do_mirror
end
if do_mirror then
forward_zone.ratio_x = 1 - forward_zone.ratio_x - forward_zone.ratio_w forward_zone.ratio_x = 1 - forward_zone.ratio_x - forward_zone.ratio_w
backward_zone.ratio_x = 1 - backward_zone.ratio_x - backward_zone.ratio_w backward_zone.ratio_x = 1 - backward_zone.ratio_x - backward_zone.ratio_w
end end
@ -373,39 +378,42 @@ function ReaderPaging:pageFlipping(flipping_page, flipping_ges)
local steps = #self.flip_steps local steps = #self.flip_steps
local stp_proportion = flipping_ges.distance / Screen:getWidth() local stp_proportion = flipping_ges.distance / Screen:getWidth()
local abs_proportion = flipping_ges.distance / Screen:getHeight() local abs_proportion = flipping_ges.distance / Screen:getHeight()
if flipping_ges.direction == "east" then local direction = BD.flipDirectionIfMirroredUILayout(flipping_ges.direction)
if direction == "east" then
self:_gotoPage(flipping_page - self.flip_steps[math.ceil(steps*stp_proportion)]) self:_gotoPage(flipping_page - self.flip_steps[math.ceil(steps*stp_proportion)])
elseif flipping_ges.direction == "west" then elseif direction == "west" then
self:_gotoPage(flipping_page + self.flip_steps[math.ceil(steps*stp_proportion)]) self:_gotoPage(flipping_page + self.flip_steps[math.ceil(steps*stp_proportion)])
elseif flipping_ges.direction == "south" then elseif direction == "south" then
self:_gotoPage(flipping_page - math.floor(whole*abs_proportion)) self:_gotoPage(flipping_page - math.floor(whole*abs_proportion))
elseif flipping_ges.direction == "north" then elseif direction == "north" then
self:_gotoPage(flipping_page + math.floor(whole*abs_proportion)) self:_gotoPage(flipping_page + math.floor(whole*abs_proportion))
end end
UIManager:setDirty(self.view.dialog, "partial") UIManager:setDirty(self.view.dialog, "partial")
end end
function ReaderPaging:bookmarkFlipping(flipping_page, flipping_ges) function ReaderPaging:bookmarkFlipping(flipping_page, flipping_ges)
if flipping_ges.direction == "east" then local direction = BD.flipDirectionIfMirroredUILayout(flipping_ges.direction)
if direction == "east" then
self.ui:handleEvent(Event:new("GotoPreviousBookmark", flipping_page)) self.ui:handleEvent(Event:new("GotoPreviousBookmark", flipping_page))
elseif flipping_ges.direction == "west" then elseif direction == "west" then
self.ui:handleEvent(Event:new("GotoNextBookmark", flipping_page)) self.ui:handleEvent(Event:new("GotoNextBookmark", flipping_page))
end end
UIManager:setDirty(self.view.dialog, "partial") UIManager:setDirty(self.view.dialog, "partial")
end end
function ReaderPaging:onSwipe(_, ges) function ReaderPaging:onSwipe(_, ges)
local direction = BD.flipDirectionIfMirroredUILayout(ges.direction)
if self.bookmark_flipping_mode then if self.bookmark_flipping_mode then
self:bookmarkFlipping(self.current_page, ges) self:bookmarkFlipping(self.current_page, ges)
elseif self.page_flipping_mode and self.original_page then elseif self.page_flipping_mode and self.original_page then
self:_gotoPage(self.original_page) self:_gotoPage(self.original_page)
elseif ges.direction == "west" then elseif direction == "west" then
if self.inverse_reading_order then if self.inverse_reading_order then
self:onGotoViewRel(-1) self:onGotoViewRel(-1)
else else
self:onGotoViewRel(1) self:onGotoViewRel(1)
end end
elseif ges.direction == "east" then elseif direction == "east" then
if self.inverse_reading_order then if self.inverse_reading_order then
self:onGotoViewRel(1) self:onGotoViewRel(1)
else else

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device") local Device = require("device")
@ -304,7 +305,11 @@ function ReaderRolling:setupTouchZones()
ratio_w = DDOUBLE_TAP_ZONE_PREV_CHAPTER.w, ratio_h = DDOUBLE_TAP_ZONE_PREV_CHAPTER.h, ratio_w = DDOUBLE_TAP_ZONE_PREV_CHAPTER.w, ratio_h = DDOUBLE_TAP_ZONE_PREV_CHAPTER.h,
} }
local do_mirror = BD.mirroredUILayout()
if self.inverse_reading_order then if self.inverse_reading_order then
do_mirror = not do_mirror
end
if do_mirror then
forward_zone.ratio_x = 1 - forward_zone.ratio_x - forward_zone.ratio_w forward_zone.ratio_x = 1 - forward_zone.ratio_x - forward_zone.ratio_w
backward_zone.ratio_x = 1 - backward_zone.ratio_x - backward_zone.ratio_w backward_zone.ratio_x = 1 - backward_zone.ratio_x - backward_zone.ratio_w
@ -464,13 +469,14 @@ function ReaderRolling:getLastPercent()
end end
function ReaderRolling:onSwipe(_, ges) function ReaderRolling:onSwipe(_, ges)
if ges.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges.direction)
if direction == "west" then
if self.inverse_reading_order then if self.inverse_reading_order then
self:onGotoViewRel(-1) self:onGotoViewRel(-1)
else else
self:onGotoViewRel(1) self:onGotoViewRel(1)
end end
elseif ges.direction == "east" then elseif direction == "east" then
if self.inverse_reading_order then if self.inverse_reading_order then
self:onGotoViewRel(1) self:onGotoViewRel(1)
else else
@ -597,20 +603,36 @@ function ReaderRolling:onGotoXPointer(xp, marker_xp)
-- Make it 4/5 of left margin wide (and bigger when huge margin) -- Make it 4/5 of left margin wide (and bigger when huge margin)
local marker_w = math.floor(math.max(doc_margins["left"] - Screen:scaleBySize(5), doc_margins["left"] * 4/5)) local marker_w = math.floor(math.max(doc_margins["left"] - Screen:scaleBySize(5), doc_margins["left"] * 4/5))
if self.ui.document:getVisiblePageCount() > 1 and screen_x > Screen:getWidth() / 2 then if self.ui.document:getVisiblePageCount() > 1 then -- 2-pages mode
-- On right page in 2-pages mode if screen_x < Screen:getWidth() / 2 then -- On left page
-- We could show the marker on the right of the page with: if BD.mirroredUILayout() then
-- screen_x = Screen:getWidth() - marker_w -- In the middle margin, on the right of text
-- But it's best to show it on the left of text, so in -- Same trick as below, assuming page2_x is equal to page 1 right x
-- the middle margin, so it still shows just left of a screen_x = Screen:getWidth() / 2
-- footnote number. local page2_x = self.ui.document:getPageOffsetX(self.ui.document:getCurrentPage()+1)
-- This is a bit tricky with how the middle margin is sized marker_w = page2_x + marker_w - screen_x
-- by crengine (see LVDocView::updateLayout() in lvdocview.cpp) screen_x = screen_x - marker_w
screen_x = Screen:getWidth() / 2 else
local page2_x = self.ui.document:getPageOffsetX(self.ui.document:getCurrentPage()+1) screen_x = 0 -- In left page left margin
marker_w = page2_x + marker_w - screen_x end
else else -- On right page
screen_x = 0 if BD.mirroredUILayout() then
screen_x = Screen:getWidth() - marker_w -- In right page right margin
else
-- In the middle margin, on the left of text
-- This is a bit tricky with how the middle margin is sized
-- by crengine (see LVDocView::updateLayout() in lvdocview.cpp)
screen_x = Screen:getWidth() / 2
local page2_x = self.ui.document:getPageOffsetX(self.ui.document:getCurrentPage()+1)
marker_w = page2_x + marker_w - screen_x
end
end
else -- 1-page mode
if BD.mirroredUILayout() then
screen_x = Screen:getWidth() - marker_w -- In right margin
else
screen_x = 0 -- In left margin
end
end end
self.mark_func = function() self.mark_func = function()

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local ButtonDialog = require("ui/widget/buttondialog") local ButtonDialog = require("ui/widget/buttondialog")
local InputContainer = require("ui/widget/container/inputcontainer") local InputContainer = require("ui/widget/container/inputcontainer")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
@ -28,6 +29,11 @@ function ReaderSearch:addToMainMenu(menu_items)
end end
function ReaderSearch:onShowFulltextSearchInput() function ReaderSearch:onShowFulltextSearchInput()
local backward_text = ""
local forward_text = ""
if BD.mirroredUILayout() then
backward_text, forward_text = forward_text, backward_text
end
self:onInput{ self:onInput{
title = _("Enter text to search for"), title = _("Enter text to search for"),
type = "text", type = "text",
@ -40,14 +46,14 @@ function ReaderSearch:onShowFulltextSearchInput()
end, end,
}, },
{ {
text = "", text = backward_text,
callback = function() callback = function()
self:onShowSearchDialog(self.input_dialog:getInputText(), 1) self:onShowSearchDialog(self.input_dialog:getInputText(), 1)
self:closeInputDialog() self:closeInputDialog()
end, end,
}, },
{ {
text = "", text = forward_text,
is_enter_default = true, is_enter_default = true,
callback = function() callback = function()
self:onShowSearchDialog(self.input_dialog:getInputText(), 0) self:onShowSearchDialog(self.input_dialog:getInputText(), 0)
@ -138,24 +144,33 @@ function ReaderSearch:onShowSearchDialog(text, direction)
end end
end end
end end
local from_start_text = "▕◁"
local backward_text = ""
local forward_text = ""
local from_end_text = "▷▏"
if BD.mirroredUILayout() then
backward_text, forward_text = forward_text, backward_text
-- Keep the LTR order of |< and >|:
from_start_text, from_end_text = BD.ltr(from_end_text), BD.ltr(from_start_text)
end
self.search_dialog = ButtonDialog:new{ self.search_dialog = ButtonDialog:new{
-- alpha = 0.7, -- alpha = 0.7,
buttons = { buttons = {
{ {
{ {
text = "▕◁", text = from_start_text,
callback = do_search(self.searchFromStart, text), callback = do_search(self.searchFromStart, text),
}, },
{ {
text = "", text = backward_text,
callback = do_search(self.searchNext, text, 1), callback = do_search(self.searchNext, text, 1),
}, },
{ {
text = "", text = forward_text,
callback = do_search(self.searchNext, text, 0), callback = do_search(self.searchNext, text, 0),
}, },
{ {
text = "▷▏", text = from_end_text,
callback = do_search(self.searchFromEnd, text), callback = do_search(self.searchFromEnd, text),
}, },
} }

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
@ -285,6 +286,7 @@ function ReaderToc:onShowToc()
-- update collapsible state -- update collapsible state
self.expand_button = Button:new{ self.expand_button = Button:new{
icon = "resources/icons/appbar.control.expand.png", icon = "resources/icons/appbar.control.expand.png",
icon_rotation_angle = BD.mirroredUILayout() and 180 or 0,
width = Screen:scaleBySize(30), width = Screen:scaleBySize(30),
bordersize = 0, bordersize = 0,
show_parent = self, show_parent = self,
@ -338,7 +340,7 @@ function ReaderToc:onShowToc()
w = Screen:getWidth(), w = Screen:getWidth(),
h = Screen:getHeight(), h = Screen:getHeight(),
}, },
direction = "west" direction = BD.flipDirectionIfMirroredUILayout("west")
} }
} }
} }
@ -352,7 +354,15 @@ function ReaderToc:onShowToc()
function toc_menu:onMenuSelect(item, pos) function toc_menu:onMenuSelect(item, pos)
-- if toc item has expand/collapse state and tap select on the left side -- if toc item has expand/collapse state and tap select on the left side
-- the state switch action is triggered, otherwise goto the linked page -- the state switch action is triggered, otherwise goto the linked page
if item.state and pos and pos.x < 0.3 then local do_toggle_state = false
if item.state and pos and pos.x then
if BD.mirroredUILayout() then
do_toggle_state = pos.x > 0.7
else
do_toggle_state = pos.x < 0.3
end
end
if do_toggle_state then
item.state.callback() item.state.callback()
else else
toc_menu:close_callback() toc_menu:close_callback()

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
@ -91,7 +92,7 @@ function SkimToWidget:init()
text = dialog_title, text = dialog_title,
face = self.title_face, face = self.title_face,
bold = true, bold = true,
width = self.screen_width * 0.95, max_width = self.screen_width * 0.95,
}, },
} }
@ -198,8 +199,16 @@ function SkimToWidget:init()
end, end,
} }
local chapter_next_text = "▷│"
local chapter_prev_text = "│◁"
local bookmark_next_text = "☆▷"
local bookmark_prev_text = "◁☆"
if BD.mirroredUILayout() then
chapter_next_text, chapter_prev_text = chapter_prev_text, chapter_next_text
bookmark_next_text, bookmark_prev_text = bookmark_prev_text, bookmark_next_text
end
local button_chapter_next = Button:new{ local button_chapter_next = Button:new{
text = '▷│', text = chapter_next_text,
bordersize = self.button_bordersize, bordersize = self.button_bordersize,
margin = self.button_margin, margin = self.button_margin,
radius = 0, radius = 0,
@ -218,7 +227,7 @@ function SkimToWidget:init()
} }
local button_chapter_prev = Button:new{ local button_chapter_prev = Button:new{
text = "│◁", text = chapter_prev_text,
bordersize = self.button_bordersize, bordersize = self.button_bordersize,
margin = self.button_margin, margin = self.button_margin,
radius = 0, radius = 0,
@ -237,7 +246,7 @@ function SkimToWidget:init()
} }
local button_bookmark_next = Button:new{ local button_bookmark_next = Button:new{
text = "☆▷", text = bookmark_next_text,
bordersize = self.button_bordersize, bordersize = self.button_bordersize,
margin = self.button_margin, margin = self.button_margin,
radius = 0, radius = 0,
@ -265,7 +274,7 @@ function SkimToWidget:init()
} }
local button_bookmark_prev = Button:new{ local button_bookmark_prev = Button:new{
text = "◁☆", text = bookmark_prev_text,
bordersize = self.button_bordersize, bordersize = self.button_bordersize,
margin = self.button_margin, margin = self.button_margin,
radius = 0, radius = 0,
@ -420,9 +429,10 @@ end
function SkimToWidget:onTapProgress(arg, ges_ev) function SkimToWidget:onTapProgress(arg, ges_ev)
if ges_ev.pos:intersectWith(self.progress_bar.dimen) then if ges_ev.pos:intersectWith(self.progress_bar.dimen) then
local width = self.progress_bar.dimen.w local perc = self.progress_bar:getPercentageFromPosition(ges_ev.pos)
local pos = ges_ev.pos.x - self.progress_bar.dimen.x if not perc then
local perc = pos / width return true
end
local page = Math.round(perc * self.page_count) local page = Math.round(perc * self.page_count)
self:addOriginToLocationStack() self:addOriginToLocationStack()
self.ui:handleEvent(Event:new("GotoPage", page )) self.ui:handleEvent(Event:new("GotoPage", page ))

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Device = require("device") local Device = require("device")
local S = require("ui/data/strings") local S = require("ui/data/strings")
local optionsutil = require("ui/data/optionsutil") local optionsutil = require("ui/data/optionsutil")
@ -315,4 +316,16 @@ This can also be used to remove some gray background or to convert a grayscale o
}, },
} }
if BD.mirroredUILayout() then
-- The justification items {AUTO, LEFT, CENTER, RIGHT, JUSTIFY} will
-- be mirrored - but that's not enough: we need to swap LEFT and RIGHT,
-- so they appear in a more expected and balanced order to RTL users:
-- {JUSTIFY, LEFT, CENTER, RIGHT, AUTO}
local j = KoptOptions[3].options[6]
assert(j.name == "justification")
j.item_icons[2], j.item_icons[4] = j.item_icons[4], j.item_icons[2]
j.values[2], j.values[4] = j.values[4], j.values[2]
j.labels[2], j.labels[4] = j.labels[4], j.labels[2]
end
return KoptOptions return KoptOptions

@ -33,6 +33,7 @@ local Button = InputContainer:new{
text = nil, -- mandatory text = nil, -- mandatory
text_func = nil, text_func = nil,
icon = nil, icon = nil,
icon_rotation_angle = 0,
preselect = false, preselect = false,
callback = nil, callback = nil,
enabled = true, enabled = true,
@ -74,6 +75,7 @@ function Button:init()
else else
self.label_widget = ImageWidget:new{ self.label_widget = ImageWidget:new{
file = self.icon, file = self.icon,
rotation_angle = self.icon_rotation_angle,
dim = not self.enabled, dim = not self.enabled,
scale_for_dpi = true, scale_for_dpi = true,
} }

@ -14,6 +14,7 @@ Example:
]] ]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local Font = require("ui/font") local Font = require("ui/font")
local InputContainer = require("ui/widget/container/inputcontainer") local InputContainer = require("ui/widget/container/inputcontainer")
@ -27,30 +28,40 @@ local CheckMark = InputContainer:new{
face = Font:getFace("smallinfofont"), face = Font:getFace("smallinfofont"),
width = 0, width = 0,
height = 0, height = 0,
_mirroredUI = BD.mirroredUILayout(),
} }
function CheckMark:init() function CheckMark:init()
-- Adjust these checkmarks if mirroring UI (para_direction_rtl should
-- follow BD.mirroredUILayout(), and not the set or reverted text
-- direction, for proper rendering on the right).
local para_direction_rtl = self._mirroredUI
local checked_widget = TextWidget:new{ local checked_widget = TextWidget:new{
text = " ✓", -- preceded by thin space for better alignment text = " ✓", -- preceded by thin space for better alignment
face = self.face, face = self.face,
para_direction_rtl = para_direction_rtl,
} }
local unchecked_widget = TextWidget:new{ local unchecked_widget = TextWidget:new{
text = "", text = "",
face = self.face, face = self.face,
para_direction_rtl = para_direction_rtl,
} }
local disabled_checked_widget = TextWidget:new{ local disabled_checked_widget = TextWidget:new{
text = " ✓", -- preceded by thin space for better alignment text = " ✓", -- preceded by thin space for better alignment
face = self.face, face = self.face,
fgcolor = Blitbuffer.COLOR_DARK_GRAY, fgcolor = Blitbuffer.COLOR_DARK_GRAY,
para_direction_rtl = para_direction_rtl,
} }
local disabled_unchecked_widget = TextWidget:new{ local disabled_unchecked_widget = TextWidget:new{
text = "", text = "",
face = self.face, face = self.face,
fgcolor = Blitbuffer.COLOR_DARK_GRAY, fgcolor = Blitbuffer.COLOR_DARK_GRAY,
para_direction_rtl = para_direction_rtl,
} }
local empty_widget = TextWidget:new{ local empty_widget = TextWidget:new{
text = "", text = "",
face = self.face, face = self.face,
para_direction_rtl = para_direction_rtl,
} }
local widget local widget
if self.checkable then if self.checkable then

@ -34,11 +34,11 @@ function CloseButton:init()
face = Font:getFace("cfont", 30), face = Font:getFace("cfont", 30),
} }
-- The text box height is greater than its width, and we want this × to be
-- diagonally aligned with the top right corner (assuming padding_right=0,
-- or padding_right = padding_top so the diagonal aligment is preserved).
local text_size = text_widget:getSize() local text_size = text_widget:getSize()
-- The text box height is greater than its width, and we want this × to
-- be diagonally aligned with our top right border
local text_width_pad = (text_size.h - text_size.w) / 2 local text_width_pad = (text_size.h - text_size.w) / 2
-- We also add the provided padding_right
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
bordersize = 0, bordersize = 0,

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
local ButtonTable = require("ui/widget/buttontable") local ButtonTable = require("ui/widget/buttontable")
@ -410,10 +411,15 @@ function DictQuickLookup:update()
}, },
} }
else else
local prev_dict_text = "◁◁"
local next_dict_text = "▷▷"
if BD.mirroredUILayout() then
prev_dict_text, next_dict_text = next_dict_text, prev_dict_text
end
buttons = { buttons = {
{ {
{ {
text = "◁◁", text = prev_dict_text,
enabled = self:isPrevDictAvaiable(), enabled = self:isPrevDictAvaiable(),
callback = function() callback = function()
self:changeToPrevDict() self:changeToPrevDict()
@ -432,7 +438,7 @@ function DictQuickLookup:update()
end, end,
}, },
{ {
text = "▷▷", text = next_dict_text,
enabled = self:isNextDictAvaiable(), enabled = self:isNextDictAvaiable(),
callback = function() callback = function()
self:changeToNextDict() self:changeToNextDict()
@ -462,7 +468,10 @@ function DictQuickLookup:update()
{ {
-- if more than one language, enable it and display "current lang > next lang" -- if more than one language, enable it and display "current lang > next lang"
-- otherwise, just display current lang -- otherwise, just display current lang
text = self.is_wiki and ( #self.wiki_languages > 1 and self.wiki_languages[1].." > "..self.wiki_languages[2] or self.wiki_languages[1] ) or _("Follow Link"), text = self.is_wiki
and ( #self.wiki_languages > 1 and BD.wrap(self.wiki_languages[1]).." > "..BD.wrap(self.wiki_languages[2])
or self.wiki_languages[1] ) -- (this " > " will be auro-mirrored by bidi)
or _("Follow Link"),
enabled = (self.is_wiki and #self.wiki_languages > 1) or self.selected_link ~= nil, enabled = (self.is_wiki and #self.wiki_languages > 1) or self.selected_link ~= nil,
callback = function() callback = function()
if self.is_wiki then if self.is_wiki then
@ -738,7 +747,7 @@ function DictQuickLookup:onTapCloseDict(arg, ges_ev)
-- processed for scrolling definition by ScrollTextWidget, which -- processed for scrolling definition by ScrollTextWidget, which
-- will pop it up for us here when it can't scroll anymore). -- will pop it up for us here when it can't scroll anymore).
-- This allow for continuous reading of results' definitions with tap. -- This allow for continuous reading of results' definitions with tap.
if ges_ev.pos.x < Screen:getWidth()/2 then if BD.flipIfMirroredUILayout(ges_ev.pos.x < Screen:getWidth()/2) then
local prev_index = self.dict_index local prev_index = self.dict_index
self:changeToPrevDict() self:changeToPrevDict()
if self.dict_index ~= prev_index then if self.dict_index ~= prev_index then
@ -792,9 +801,10 @@ function DictQuickLookup:onSwipe(arg, ges)
if ges.pos:intersectWith(self.definition_widget.dimen) then if ges.pos:intersectWith(self.definition_widget.dimen) then
-- if we want changeDict to still work with swipe outside window : -- if we want changeDict to still work with swipe outside window :
-- or not ges.pos:intersectWith(self.dict_frame.dimen) then -- or not ges.pos:intersectWith(self.dict_frame.dimen) then
if ges.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges.direction)
if direction == "west" then
self:changeToNextDict() self:changeToNextDict()
elseif ges.direction == "east" then elseif direction == "east" then
self:changeToPrevDict() self:changeToPrevDict()
else else
if self.refresh_callback then self.refresh_callback() end if self.refresh_callback then self.refresh_callback() end

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Device = require("device") local Device = require("device")
local DocSettings = require("docsettings") local DocSettings = require("docsettings")
local DocumentRegistry = require("document/documentregistry") local DocumentRegistry = require("document/documentregistry")
@ -121,7 +122,7 @@ end
function FileChooser:genItemTableFromPath(path) function FileChooser:genItemTableFromPath(path)
local dirs = {} local dirs = {}
local files = {} local files = {}
local up_folder_arrow = "⬆ ../" local up_folder_arrow = BD.mirroredUILayout() and BD.ltr("../ ⬆") or "⬆ ../"
self.list(path, dirs, files) self.list(path, dirs, files)
@ -213,15 +214,18 @@ function FileChooser:genItemTableFromPath(path)
local num_items = #sub_dirs + #dir_files local num_items = #sub_dirs + #dir_files
local istr = ffiUtil.template(N_("1 item", "%1 items", num_items), num_items) local istr = ffiUtil.template(N_("1 item", "%1 items", num_items), num_items)
local text local text
local bidi_wrap_func
if dir.name == ".." then if dir.name == ".." then
text = up_folder_arrow text = up_folder_arrow
elseif dir.name == "." then -- possible with show_current_dir_for_hold elseif dir.name == "." then -- possible with show_current_dir_for_hold
text = _("Long-press to select current directory") text = _("Long-press to select current directory")
else else
text = dir.name.."/" text = dir.name.."/"
bidi_wrap_func = BD.directory
end end
table.insert(item_table, { table.insert(item_table, {
text = text, text = text,
bidi_wrap_func = bidi_wrap_func,
mandatory = istr, mandatory = istr,
path = subdir_path, path = subdir_path,
is_go_up = dir.name == ".." is_go_up = dir.name == ".."
@ -239,6 +243,7 @@ function FileChooser:genItemTableFromPath(path)
local sstr = getFriendlySize(file_size) local sstr = getFriendlySize(file_size)
local file_item = { local file_item = {
text = file.name, text = file.name,
bidi_wrap_func = BD.filename,
mandatory = sstr, mandatory = sstr,
path = full_path path = full_path
} }
@ -437,7 +442,7 @@ function FileChooser:showSetProviderButtons(file, filemanager_instance, reader_u
if self.set_provider_dialog._check_file_button.checked then if self.set_provider_dialog._check_file_button.checked then
UIManager:show(ConfirmBox:new{ UIManager:show(ConfirmBox:new{
text = T(_("Always open '%2' with %1?"), text = T(_("Always open '%2' with %1?"),
provider.provider_name, filename_pure), provider.provider_name, BD.filename(filename_pure)),
ok_text = _("Always"), ok_text = _("Always"),
ok_callback = function() ok_callback = function()
DocumentRegistry:setProvider(file, provider, false) DocumentRegistry:setProvider(file, provider, false)
@ -485,7 +490,7 @@ function FileChooser:showSetProviderButtons(file, filemanager_instance, reader_u
end end
self.set_provider_dialog = OpenWithDialog:new{ self.set_provider_dialog = OpenWithDialog:new{
title = T(_("Open %1 with:"), filename_pure), title = T(_("Open %1 with:"), BD.filename(filename_pure)),
radio_buttons = radio_buttons, radio_buttons = radio_buttons,
buttons = buttons, buttons = buttons,
} }

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
@ -193,7 +194,11 @@ function FootnoteWidget:init()
-- bullets in its own left margin. To get a chance to have them -- bullets in its own left margin. To get a chance to have them
-- shown, we let MuPDF handle our left margin. -- shown, we let MuPDF handle our left margin.
local html_left_margin = self.doc_margins.left .. "px" local html_left_margin = self.doc_margins.left .. "px"
local css = T(PAGE_CSS, "0", "0", "0", html_left_margin, -- top right bottom left local html_right_margin = "0"
if BD.mirroredUILayout() then
html_left_margin, html_right_margin = html_right_margin, html_left_margin
end
local css = T(PAGE_CSS, "0", html_right_margin, "0", html_left_margin, -- top right bottom left
self.font_face, DEFAULT_CSS) self.font_face, DEFAULT_CSS)
if self.css then -- add any provided css if self.css then -- add any provided css
css = css .. "\n" .. self.css css = css .. "\n" .. self.css
@ -327,14 +332,15 @@ function FootnoteWidget:onTapClose(arg, ges)
end end
function FootnoteWidget:onSwipeFollow(arg, ges) function FootnoteWidget:onSwipeFollow(arg, ges)
if ges.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges.direction)
if direction == "west" then
if self.follow_callback then if self.follow_callback then
if self.close_callback then if self.close_callback then
self.close_callback(self.height) self.close_callback(self.height)
end end
return self.follow_callback() return self.follow_callback()
end end
elseif ges.direction == "south" or ges.direction == "east" then elseif direction == "south" or direction == "east" then
UIManager:close(self) UIManager:close(self)
-- We can close with swipe down. If footnote is scrollable, -- We can close with swipe down. If footnote is scrollable,
-- this event will be eaten by ScrollHtmlWidget, and it will -- this event will be eaten by ScrollHtmlWidget, and it will
@ -345,7 +351,7 @@ function FootnoteWidget:onSwipeFollow(arg, ges)
self.close_callback(self.height) self.close_callback(self.height)
end end
return true return true
elseif ges.direction == "north" then elseif direction == "north" then
-- no use for now -- no use for now
do end -- luacheck: ignore 541 do end -- luacheck: ignore 541
else -- diagonal swipe else -- diagonal swipe

@ -651,9 +651,10 @@ function FrontLightWidget:onTapProgress(arg, ges_ev)
-- Unschedule any pending updates. -- Unschedule any pending updates.
UIManager:unschedule(self.update) UIManager:unschedule(self.update)
local width = self.fl_group.dimen.w local perc = self.fl_group:getPercentageFromPosition(ges_ev.pos)
local pos = ges_ev.pos.x - self.fl_group.dimen.x if not perc then
local perc = pos / width return true
end
local num = Math.round(perc * self.fl_max) local num = Math.round(perc * self.fl_max)
-- Always set the frontlight intensity. -- Always set the frontlight intensity.

@ -2,6 +2,7 @@
ImageViewer displays an image with some simple manipulation options. ImageViewer displays an image with some simple manipulation options.
]] ]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local ButtonTable = require("ui/widget/buttontable") local ButtonTable = require("ui/widget/buttontable")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
@ -459,11 +460,19 @@ function ImageViewer:onTap(_, ges)
end end
if self._images_list then if self._images_list then
-- If it's a list of image (e.g. animated gifs), tap left/right 1/3 of screen to navigate -- If it's a list of image (e.g. animated gifs), tap left/right 1/3 of screen to navigate
local show_prev_image, show_next_image
if ges.pos.x < Screen:getWidth()/3 then if ges.pos.x < Screen:getWidth()/3 then
show_prev_image = not BD.mirroredUILayout()
show_next_image = BD.mirroredUILayout()
elseif ges.pos.x > Screen:getWidth()*2/3 then
show_prev_image = BD.mirroredUILayout()
show_next_image = not BD.mirroredUILayout()
end
if show_prev_image then
if self._images_list_cur > 1 then if self._images_list_cur > 1 then
self:switchToImageNum(self._images_list_cur - 1) self:switchToImageNum(self._images_list_cur - 1)
end end
elseif ges.pos.x > Screen:getWidth()*2/3 then elseif show_next_image then
if self._images_list_cur < self._images_list_nb then if self._images_list_cur < self._images_list_nb then
self:switchToImageNum(self._images_list_cur + 1) self:switchToImageNum(self._images_list_cur + 1)
end end

@ -19,6 +19,7 @@ Example:
]] ]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
@ -333,6 +334,7 @@ function KeyValuePage:init()
end end
-- return button -- return button
--- @todo: alternative icon if BD.mirroredUILayout()
self.page_return_arrow = Button:new{ self.page_return_arrow = Button:new{
icon = "resources/icons/appbar.arrow.left.up.png", icon = "resources/icons/appbar.arrow.left.up.png",
callback = function() self:onReturn() end, callback = function() self:onReturn() end,
@ -340,26 +342,34 @@ function KeyValuePage:init()
show_parent = self, show_parent = self,
} }
-- group for page info -- group for page info
local chevron_left = "resources/icons/appbar.chevron.left.png"
local chevron_right = "resources/icons/appbar.chevron.right.png"
local chevron_first = "resources/icons/appbar.chevron.first.png"
local chevron_last = "resources/icons/appbar.chevron.last.png"
if BD.mirroredUILayout() then
chevron_left, chevron_right = chevron_right, chevron_left
chevron_first, chevron_last = chevron_last, chevron_first
end
self.page_info_left_chev = Button:new{ self.page_info_left_chev = Button:new{
icon = "resources/icons/appbar.chevron.left.png", icon = chevron_left,
callback = function() self:prevPage() end, callback = function() self:prevPage() end,
bordersize = 0, bordersize = 0,
show_parent = self, show_parent = self,
} }
self.page_info_right_chev = Button:new{ self.page_info_right_chev = Button:new{
icon = "resources/icons/appbar.chevron.right.png", icon = chevron_right,
callback = function() self:nextPage() end, callback = function() self:nextPage() end,
bordersize = 0, bordersize = 0,
show_parent = self, show_parent = self,
} }
self.page_info_first_chev = Button:new{ self.page_info_first_chev = Button:new{
icon = "resources/icons/appbar.chevron.first.png", icon = chevron_first,
callback = function() self:goToPage(1) end, callback = function() self:goToPage(1) end,
bordersize = 0, bordersize = 0,
show_parent = self, show_parent = self,
} }
self.page_info_last_chev = Button:new{ self.page_info_last_chev = Button:new{
icon = "resources/icons/appbar.chevron.last.png", icon = chevron_last,
callback = function() self:goToPage(self.pages) end, callback = function() self:goToPage(self.pages) end,
bordersize = 0, bordersize = 0,
show_parent = self, show_parent = self,
@ -446,6 +456,7 @@ function KeyValuePage:init()
local content = OverlapGroup:new{ local content = OverlapGroup:new{
dimen = self.dimen:copy(), dimen = self.dimen:copy(),
allow_mirroring = false,
VerticalGroup:new{ VerticalGroup:new{
align = "left", align = "left",
self.title_bar, self.title_bar,
@ -557,16 +568,17 @@ function KeyValuePage:onPrevPage()
end end
function KeyValuePage:onSwipe(arg, ges_ev) function KeyValuePage:onSwipe(arg, ges_ev)
if ges_ev.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges_ev.direction)
if direction == "west" then
self:nextPage() self:nextPage()
return true return true
elseif ges_ev.direction == "east" then elseif direction == "east" then
self:prevPage() self:prevPage()
return true return true
elseif ges_ev.direction == "south" then elseif direction == "south" then
-- Allow easier closing with swipe down -- Allow easier closing with swipe down
self:onClose() self:onClose()
elseif ges_ev.direction == "north" then elseif direction == "north" then
-- no use for now -- no use for now
do end -- luacheck: ignore 541 do end -- luacheck: ignore 541
else -- diagonal swipe else -- diagonal swipe

@ -36,6 +36,7 @@ Note that ListView is created mainly to be used as a building block for other
widgets like @{ui.widget.networksetting|NetworkSetting}, so they can share the same pagination code. widgets like @{ui.widget.networksetting|NetworkSetting}, so they can share the same pagination code.
]] ]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local Device = require("device") local Device = require("device")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
@ -113,10 +114,11 @@ function ListView:prevPage()
end end
function ListView:onSwipe(arg, ges_ev) function ListView:onSwipe(arg, ges_ev)
if ges_ev.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges_ev.direction)
if direction == "west" then
self:nextPage() self:nextPage()
return true return true
elseif ges_ev.direction == "east" then elseif direction == "east" then
self:prevPage() self:prevPage()
return true return true
end end

@ -2,6 +2,7 @@
Widget that displays a shortcut icon for menu item. Widget that displays a shortcut icon for menu item.
--]] --]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
@ -33,7 +34,6 @@ local util = require("ffi/util")
local _ = require("gettext") local _ = require("gettext")
local Input = Device.input local Input = Device.input
local Screen = Device.screen local Screen = Device.screen
local getMenuText = require("util").getMenuText
local ItemShortCutIcon = WidgetContainer:new{ local ItemShortCutIcon = WidgetContainer:new{
dimen = Geom:new{ w = Screen:scaleBySize(22), h = Screen:scaleBySize(22) }, dimen = Geom:new{ w = Screen:scaleBySize(22), h = Screen:scaleBySize(22) },
@ -92,21 +92,30 @@ local MenuCloseButton = InputContainer:new{
} }
function MenuCloseButton:init() function MenuCloseButton:init()
self[1] = TextWidget:new{ local text_widget = TextWidget:new{
text = "×", text = "×",
face = Font:getFace("cfont", 30), -- this font size align nicely with title face = Font:getFace("cfont", 30), -- this font size align nicely with title
} }
-- The text box height is greater than its width, and we want this × to be
local text_size = self[1]:getSize() -- diagonally aligned with the top right corner (assuming padding_right=0,
-- The text box height is greater than its width, and we want this × to -- or padding_right = padding_top so the diagonal aligment is preserved).
-- be diagonally aligned with our top right border local text_size = text_widget:getSize()
local text_width_pad = (text_size.h - text_size.w) / 2 local text_width_pad = (text_size.h - text_size.w) / 2
-- We also add the provided padding_right
self[1] = FrameContainer:new{
bordersize = 0,
padding = 0,
padding_top = self.padding_top,
padding_bottom = self.padding_bottom,
padding_left = self.padding_left,
padding_right = self.padding_right + text_width_pad,
text_widget,
}
self.dimen = Geom:new{ self.dimen = Geom:new{
w = text_size.w + text_width_pad + self.padding_right, w = text_size.w + text_width_pad + self.padding_right,
h = text_size.h, h = text_size.h,
} }
self.ges_events.Close = { self.ges_events.Close = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
@ -401,6 +410,7 @@ function MenuItem:init()
table.insert(hgroup, HorizontalSpan:new{ width = Size.span.horizontal_default }) table.insert(hgroup, HorizontalSpan:new{ width = Size.span.horizontal_default })
end end
table.insert(hgroup, self._underline_container) table.insert(hgroup, self._underline_container)
table.insert(hgroup, HorizontalSpan:new{ width = Size.padding.fullscreen })
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
bordersize = 0, bordersize = 0,
@ -641,26 +651,34 @@ function Menu:init()
-- group for items -- group for items
self.item_group = VerticalGroup:new{} self.item_group = VerticalGroup:new{}
-- group for page info -- group for page info
local chevron_left = "resources/icons/appbar.chevron.left.png"
local chevron_right = "resources/icons/appbar.chevron.right.png"
local chevron_first = "resources/icons/appbar.chevron.first.png"
local chevron_last = "resources/icons/appbar.chevron.last.png"
if BD.mirroredUILayout() then
chevron_left, chevron_right = chevron_right, chevron_left
chevron_first, chevron_last = chevron_last, chevron_first
end
self.page_info_left_chev = Button:new{ self.page_info_left_chev = Button:new{
icon = "resources/icons/appbar.chevron.left.png", icon = chevron_left,
callback = function() self:onPrevPage() end, callback = function() self:onPrevPage() end,
bordersize = 0, bordersize = 0,
show_parent = self.show_parent, show_parent = self.show_parent,
} }
self.page_info_right_chev = Button:new{ self.page_info_right_chev = Button:new{
icon = "resources/icons/appbar.chevron.right.png", icon = chevron_right,
callback = function() self:onNextPage() end, callback = function() self:onNextPage() end,
bordersize = 0, bordersize = 0,
show_parent = self.show_parent, show_parent = self.show_parent,
} }
self.page_info_first_chev = Button:new{ self.page_info_first_chev = Button:new{
icon = "resources/icons/appbar.chevron.first.png", icon = chevron_first,
callback = function() self:onFirstPage() end, callback = function() self:onFirstPage() end,
bordersize = 0, bordersize = 0,
show_parent = self.show_parent, show_parent = self.show_parent,
} }
self.page_info_last_chev = Button:new{ self.page_info_last_chev = Button:new{
icon = "resources/icons/appbar.chevron.last.png", icon = chevron_last,
callback = function() self:onLastPage() end, callback = function() self:onLastPage() end,
bordersize = 0, bordersize = 0,
show_parent = self.show_parent, show_parent = self.show_parent,
@ -807,6 +825,10 @@ function Menu:init()
} }
end end
local content = OverlapGroup:new{ local content = OverlapGroup:new{
-- This unique allow_mirroring=false looks like it's enough
-- to have this complex Menu, and all widgets based on it,
-- be mirrored correctly with RTL languages
allow_mirroring = false,
dimen = self.dimen:copy(), dimen = self.dimen:copy(),
self.content_group, self.content_group,
page_return, page_return,
@ -979,7 +1001,7 @@ function Menu:updateItems(select_number)
show_parent = self.show_parent, show_parent = self.show_parent,
state = self.item_table[i].state, state = self.item_table[i].state,
state_size = self.state_size or {}, state_size = self.state_size or {},
text = getMenuText(self.item_table[i]), text = Menu.getMenuText(self.item_table[i]),
mandatory = self.item_table[i].mandatory, mandatory = self.item_table[i].mandatory,
bold = self.item_table.current == i or self.item_table[i].bold == true, bold = self.item_table.current == i or self.item_table[i].bold == true,
dim = self.item_table[i].dim, dim = self.item_table[i].dim,
@ -1237,11 +1259,12 @@ function Menu:onTapCloseAllMenus(arg, ges_ev)
end end
function Menu:onSwipe(arg, ges_ev) function Menu:onSwipe(arg, ges_ev)
if ges_ev.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges_ev.direction)
if direction == "west" then
self:onNextPage() self:onNextPage()
elseif ges_ev.direction == "east" then elseif direction == "east" then
self:onPrevPage() self:onPrevPage()
elseif ges_ev.direction == "south" then elseif direction == "south" then
if self.has_close_button and not self.no_title then if self.has_close_button and not self.no_title then
-- If there is a close button displayed (so, this Menu can be -- If there is a close button displayed (so, this Menu can be
-- closed), allow easier closing with swipe up/down -- closed), allow easier closing with swipe up/down
@ -1249,7 +1272,7 @@ function Menu:onSwipe(arg, ges_ev)
end end
-- If there is no close button, it's a top level Menu and swipe -- If there is no close button, it's a top level Menu and swipe
-- up/down may hide/show top menu -- up/down may hide/show top menu
elseif ges_ev.direction == "north" then elseif direction == "north" then
-- no use for now -- no use for now
do end -- luacheck: ignore 541 do end -- luacheck: ignore 541
else -- diagonal swipe else -- diagonal swipe
@ -1258,6 +1281,42 @@ function Menu:onSwipe(arg, ges_ev)
end end
end end
--- Adds > to touch menu items with a submenu
local arrow_left = "" -- U+25C2 BLACK LEFT-POINTING SMALL TRIANGLE
local arrow_right = "" -- U+25B8 BLACK RIGHT-POINTING SMALL TRIANGLE
local sub_item_format
-- Adjust arrow direction and position for menu with sub items
-- according to possible user choices
if BD.mirroredUILayout() then
if BD.rtlUIText() then -- normal case with RTL language
sub_item_format = "%s " .. BD.rtl(arrow_left)
else -- user reverted text direction, so LTR
sub_item_format = BD.ltr(arrow_left) .. " %s"
end
else
if BD.rtlUIText() then -- user reverted text direction, so RTL
sub_item_format = BD.rtl(arrow_right) .. " %s"
else -- normal case with LTR language
sub_item_format = "%s " .. BD.ltr(arrow_right)
end
end
function Menu.getMenuText(item)
local text
if item.text_func then
text = item.text_func()
else
text = item.text
end
if item.bidi_wrap_func then
text = item.bidi_wrap_func(text)
end
if item.sub_item_table ~= nil or item.sub_item_table_func then
text = string.format(sub_item_format, text)
end
return text
end
function Menu.itemTableFromTouchMenu(t) function Menu.itemTableFromTouchMenu(t)
local item_t = {} local item_t = {}
for k,v in pairs(t) do for k,v in pairs(t) do

@ -2,6 +2,7 @@
HTML widget with vertical scroll bar. HTML widget with vertical scroll bar.
--]] --]]
local BD = require("ui/bidi")
local Device = require("device") local Device = require("device")
local HtmlBoxWidget = require("ui/widget/htmlboxwidget") local HtmlBoxWidget = require("ui/widget/htmlboxwidget")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
@ -143,7 +144,7 @@ function ScrollHtmlWidget:onScrollText(arg, ges)
end end
function ScrollHtmlWidget:onTapScrollText(arg, ges) function ScrollHtmlWidget:onTapScrollText(arg, ges)
if ges.pos.x < Screen:getWidth()/2 then if BD.flipIfMirroredUILayout(ges.pos.x < Screen:getWidth()/2) then
if self.htmlbox_widget.page_number > 1 then if self.htmlbox_widget.page_number > 1 then
self:scrollText(-1) self:scrollText(-1)
return true return true

@ -2,6 +2,7 @@
Text widget with vertical scroll bar. Text widget with vertical scroll bar.
--]] --]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local Device = require("device") local Device = require("device")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
@ -161,6 +162,9 @@ function ScrollTextWidget:moveCursorToCharPos(charpos)
end end
function ScrollTextWidget:moveCursorToXY(x, y, no_overflow) function ScrollTextWidget:moveCursorToXY(x, y, no_overflow)
if BD.mirroredUILayout() then -- the scroll bar is on the left
x = x - self.scroll_bar_width - self.text_scroll_span
end
self.text_widget:moveCursorToXY(x, y, no_overflow) self.text_widget:moveCursorToXY(x, y, no_overflow)
self:updateScrollBar() self:updateScrollBar()
end end
@ -238,7 +242,7 @@ function ScrollTextWidget:onTapScrollText(arg, ges)
return false return false
end end
-- same tests as done in TextBoxWidget:scrollUp/Down -- same tests as done in TextBoxWidget:scrollUp/Down
if ges.pos.x < Screen:getWidth()/2 then if BD.flipIfMirroredUILayout(ges.pos.x < Screen:getWidth()/2) then
if self.text_widget.virtual_line_num > 1 then if self.text_widget.virtual_line_num > 1 then
self:scrollText(-1) self:scrollText(-1)
return true return true

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
@ -452,14 +453,15 @@ function SortWidget:onPrevPage()
end end
function SortWidget:onSwipe(arg, ges_ev) function SortWidget:onSwipe(arg, ges_ev)
if ges_ev.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges_ev.direction)
if direction == "west" then
self:onNextPage() self:onNextPage()
elseif ges_ev.direction == "east" then elseif direction == "east" then
self:onPrevPage() self:onPrevPage()
elseif ges_ev.direction == "south" then elseif direction == "south" then
-- Allow easier closing with swipe down -- Allow easier closing with swipe down
self:onClose() self:onClose()
elseif ges_ev.direction == "north" then elseif direction == "north" then
-- no use for now -- no use for now
do end -- luacheck: ignore 541 do end -- luacheck: ignore 541
else -- diagonal swipe else -- diagonal swipe

@ -8,6 +8,7 @@ Displays some text in a scrollable view.
} }
UIManager:show(textviewer) UIManager:show(textviewer)
]] ]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local ButtonTable = require("ui/widget/buttontable") local ButtonTable = require("ui/widget/buttontable")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
@ -256,10 +257,11 @@ end
function TextViewer:onSwipe(arg, ges) function TextViewer:onSwipe(arg, ges)
if ges.pos:intersectWith(self.textw.dimen) then if ges.pos:intersectWith(self.textw.dimen) then
if ges.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges.direction)
if direction == "west" then
self.scroll_text_w:scrollText(1) self.scroll_text_w:scrollText(1)
return true return true
elseif ges.direction == "east" then elseif direction == "east" then
self.scroll_text_w:scrollText(-1) self.scroll_text_w:scrollText(-1)
return true return true
else else

@ -5,6 +5,7 @@ Displays a button that toggles between states. Used in bottom configuration pane
local ToggleSwitch = require("ui/widget/toggleswitch") local ToggleSwitch = require("ui/widget/toggleswitch")
]] ]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device") local Device = require("device")
@ -173,6 +174,9 @@ end
function ToggleSwitch:calculatePosition(gev) function ToggleSwitch:calculatePosition(gev)
local x = (gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos local x = (gev.pos.x - self.dimen.x) / self.dimen.w * self.n_pos
if BD.mirroredUILayout() then
x = self.n_pos - x
end
local y = (gev.pos.y - self.dimen.y) / self.dimen.h * self.row_count local y = (gev.pos.y - self.dimen.y) / self.dimen.h * self.row_count
return math.max(1, math.ceil(x)) + math.min(self.row_count-1, math.floor(y)) * self.n_pos return math.max(1, math.ceil(x)) + math.min(self.row_count-1, math.floor(y)) * self.n_pos
end end

@ -1,6 +1,7 @@
--[[-- --[[--
TouchMenu widget for hierarchical menus. TouchMenu widget for hierarchical menus.
]] ]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
@ -26,9 +27,9 @@ local UIManager = require("ui/uimanager")
local UnderlineContainer = require("ui/widget/container/underlinecontainer") local UnderlineContainer = require("ui/widget/container/underlinecontainer")
local VerticalGroup = require("ui/widget/verticalgroup") local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan") local VerticalSpan = require("ui/widget/verticalspan")
local util = require("ffi/util") local getMenuText = require("ui/widget/menu").getMenuText
local _ = require("gettext") local _ = require("gettext")
local getMenuText = require("util").getMenuText local T = require("ffi/util").template
local Input = Device.input local Input = Device.input
local Screen = Device.screen local Screen = Device.screen
@ -247,6 +248,11 @@ function TouchMenuBar:init()
-- we have to use local variable here for closure callback -- we have to use local variable here for closure callback
local _start_seg = end_seg + icon_sep_width local _start_seg = end_seg + icon_sep_width
local _end_seg = _start_seg + self.icon_widgets[k]:getSize().w local _end_seg = _start_seg + self.icon_widgets[k]:getSize().w
end_seg = _end_seg -- for next loop _start_seg
if BD.mirroredUILayout() then
_start_seg, _end_seg = self.width - _end_seg, self.width - _start_seg
end
if k == 1 then if k == 1 then
self.bar_sep = LineWidget:new{ self.bar_sep = LineWidget:new{
@ -294,9 +300,15 @@ function TouchMenuBar:init()
-- if the active icon is the last icon then the empty bar segment has -- if the active icon is the last icon then the empty bar segment has
-- to move over to the right by the width of a separator and the stretch width -- to move over to the right by the width of a separator and the stretch width
if last_icon then if last_icon then
local _start_last_seg = icon_sep_width + stretch_width + _start_seg
local _end_last_seg = icon_sep_width + stretch_width + _end_seg
if BD.mirroredUILayout() then
_start_last_seg = _start_seg - icon_sep_width - stretch_width
_end_last_seg = _end_seg - icon_sep_width - stretch_width
end
self.bar_sep.empty_segments = { self.bar_sep.empty_segments = {
{ {
s = icon_sep_width + stretch_width + _start_seg, e = icon_sep_width + stretch_width + _end_seg s = _start_last_seg, e = _end_last_seg
} }
} }
sep.style = "solid" sep.style = "solid"
@ -327,8 +339,6 @@ function TouchMenuBar:init()
table.insert(self.icon_seps, icon_sep_duplicate) table.insert(self.icon_seps, icon_sep_duplicate)
table.insert(self.bar_icon_group, icon_sep_duplicate) table.insert(self.bar_icon_group, icon_sep_duplicate)
end end
end_seg = _end_seg
end end
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
@ -429,14 +439,19 @@ function TouchMenu:init()
align = "center", align = "center",
} }
-- group for page info -- group for page info
local chevron_left = "resources/icons/appbar.chevron.left.png"
local chevron_right = "resources/icons/appbar.chevron.right.png"
if BD.mirroredUILayout() then
chevron_left, chevron_right = chevron_right, chevron_left
end
self.page_info_left_chev = Button:new{ self.page_info_left_chev = Button:new{
icon = "resources/icons/appbar.chevron.left.png", icon = chevron_left,
callback = function() self:onPrevPage() end, callback = function() self:onPrevPage() end,
bordersize = 0, bordersize = 0,
show_parent = self.show_parent, show_parent = self.show_parent,
} }
self.page_info_right_chev = Button:new{ self.page_info_right_chev = Button:new{
icon = "resources/icons/appbar.chevron.right.png", icon = chevron_right,
callback = function() self:onNextPage() end, callback = function() self:onNextPage() end,
bordersize = 0, bordersize = 0,
show_parent = self.show_parent, show_parent = self.show_parent,
@ -592,7 +607,7 @@ function TouchMenu:updateItems()
table.insert(self.item_group, self.footer) table.insert(self.item_group, self.footer)
if self.page_num > 1 then if self.page_num > 1 then
-- @translators %1 is the current page. %2 is the total number of pages. In some languages a good translation might need to reverse this order, for instance: "Total %2, page %1". -- @translators %1 is the current page. %2 is the total number of pages. In some languages a good translation might need to reverse this order, for instance: "Total %2, page %1".
self.page_info_text:setText(util.template(_("Page %1 of %2"), self.page, self.page_num)) self.page_info_text:setText(T(_("Page %1 of %2"), self.page, self.page_num))
else else
self.page_info_text:setText("") self.page_info_text:setText("")
end end
@ -609,35 +624,35 @@ function TouchMenu:updateItems()
end end
local powerd = Device:getPowerDevice() local powerd = Device:getPowerDevice()
local batt_lvl = powerd:getCapacity() local batt_lvl = powerd:getCapacity()
time_info_txt = time_info_txt .. "" local batt_symbol
if powerd:isCharging() then if powerd:isCharging() then
time_info_txt = time_info_txt .. "" batt_symbol = ""
else else
if batt_lvl >= 100 then if batt_lvl >= 100 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 90 then elseif batt_lvl >= 90 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 80 then elseif batt_lvl >= 80 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 70 then elseif batt_lvl >= 70 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 60 then elseif batt_lvl >= 60 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 50 then elseif batt_lvl >= 50 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 40 then elseif batt_lvl >= 40 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 30 then elseif batt_lvl >= 30 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 20 then elseif batt_lvl >= 20 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
elseif batt_lvl >= 10 then elseif batt_lvl >= 10 then
time_info_txt = time_info_txt .. "" batt_symbol = ""
else else
time_info_txt = time_info_txt .. "" batt_symbol = ""
end end
end end
time_info_txt = time_info_txt .. batt_lvl .. "%" time_info_txt = BD.wrap(time_info_txt) .. " " .. BD.wrap("") .. BD.wrap(batt_symbol) .. BD.wrap(batt_lvl .. "%")
self.time_info:setText(time_info_txt) self.time_info:setText(time_info_txt)
-- recalculate dimen based on new layout -- recalculate dimen based on new layout
@ -721,11 +736,12 @@ function TouchMenu:onPrevPage()
end end
function TouchMenu:onSwipe(arg, ges_ev) function TouchMenu:onSwipe(arg, ges_ev)
if ges_ev.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges_ev.direction)
if direction == "west" then
self:onNextPage() self:onNextPage()
elseif ges_ev.direction == "east" then elseif direction == "east" then
self:onPrevPage() self:onPrevPage()
elseif ges_ev.direction == "north" then elseif direction == "north" then
self:closeMenu() self:closeMenu()
end end
end end

@ -163,6 +163,7 @@ function VirtualKey:init()
background = Blitbuffer.COLOR_WHITE, background = Blitbuffer.COLOR_WHITE,
radius = 0, radius = 0,
padding = 0, padding = 0,
allow_mirroring = false,
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = self.width - 2*self.bordersize, w = self.width - 2*self.bordersize,
@ -465,11 +466,11 @@ function VirtualKeyPopup:init()
} }
local v_key_padding = VerticalSpan:new{width = parent_key.keyboard.key_padding} local v_key_padding = VerticalSpan:new{width = parent_key.keyboard.key_padding}
local vertical_group = VerticalGroup:new{} local vertical_group = VerticalGroup:new{ allow_mirroring = false }
local horizontal_group_extra = HorizontalGroup:new{} local horizontal_group_extra = HorizontalGroup:new{ allow_mirroring = false }
local horizontal_group_top = HorizontalGroup:new{} local horizontal_group_top = HorizontalGroup:new{ allow_mirroring = false }
local horizontal_group_middle = HorizontalGroup:new{} local horizontal_group_middle = HorizontalGroup:new{ allow_mirroring = false }
local horizontal_group_bottom = HorizontalGroup:new{} local horizontal_group_bottom = HorizontalGroup:new{ allow_mirroring = false }
local function horizontalRow(chars, group) local function horizontalRow(chars, group)
local layout_horizontal = {} local layout_horizontal = {}
@ -565,6 +566,7 @@ function VirtualKeyPopup:init()
background = Blitbuffer.COLOR_WHITE, background = Blitbuffer.COLOR_WHITE,
radius = 0, radius = 0,
padding = parent_key.keyboard.padding, padding = parent_key.keyboard.padding,
allow_mirroring = false,
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = parent_key.width*num_columns + 2*Size.border.default + (num_columns)*parent_key.keyboard.key_padding, w = parent_key.width*num_columns + 2*Size.border.default + (num_columns)*parent_key.keyboard.key_padding,
@ -778,9 +780,9 @@ function VirtualKeyboard:addKeys()
local base_key_height = math.floor((self.height - (#self.KEYS + 1)*self.key_padding - 2*self.padding)/#self.KEYS) local base_key_height = math.floor((self.height - (#self.KEYS + 1)*self.key_padding - 2*self.padding)/#self.KEYS)
local h_key_padding = HorizontalSpan:new{width = self.key_padding} local h_key_padding = HorizontalSpan:new{width = self.key_padding}
local v_key_padding = VerticalSpan:new{width = self.key_padding} local v_key_padding = VerticalSpan:new{width = self.key_padding}
local vertical_group = VerticalGroup:new{} local vertical_group = VerticalGroup:new{ allow_mirroring = false }
for i = 1, #self.KEYS do for i = 1, #self.KEYS do
local horizontal_group = HorizontalGroup:new{} local horizontal_group = HorizontalGroup:new{ allow_mirroring = false }
local layout_horizontal = {} local layout_horizontal = {}
for j = 1, #self.KEYS[i] do for j = 1, #self.KEYS[i] do
local key local key
@ -828,6 +830,7 @@ function VirtualKeyboard:addKeys()
background = Blitbuffer.COLOR_WHITE, background = Blitbuffer.COLOR_WHITE,
radius = 0, radius = 0,
padding = self.padding, padding = self.padding,
allow_mirroring = false,
CenterContainer:new{ CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
w = self.width - 2*Size.border.default - 2*self.padding, w = self.width - 2*Size.border.default - 2*self.padding,

@ -694,20 +694,6 @@ function util.getFormattedSize(size)
return s return s
end end
--- Adds > to touch menu items with a submenu
function util.getMenuText(item)
local text
if item.text_func then
text = item.text_func()
else
text = item.text
end
if item.sub_item_table ~= nil or item.sub_item_table_func then
text = text .. ""
end
return text
end
--[[-- --[[--
Replaces invalid UTF-8 characters with a replacement string. Replaces invalid UTF-8 characters with a replacement string.

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device") local Device = require("device")
@ -31,6 +32,7 @@ local _ = require("gettext")
local N_ = _.ngettext local N_ = _.ngettext
local Screen = Device.screen local Screen = Device.screen
local T = require("ffi/util").template local T = require("ffi/util").template
local getMenuText = require("ui/widget/menu").getMenuText
local BookInfoManager = require("bookinfomanager") local BookInfoManager = require("bookinfomanager")
@ -339,7 +341,7 @@ function ListMenuItem:update()
local filename_without_suffix, filetype = util.splitFileNameSuffix(filename) local filename_without_suffix, filetype = util.splitFileNameSuffix(filename)
local fileinfo_str = filetype local fileinfo_str = filetype
if self.mandatory then if self.mandatory then
fileinfo_str = self.mandatory .. " " .. fileinfo_str fileinfo_str = self.mandatory .. " " .. BD.wrap(fileinfo_str)
end end
if bookinfo._no_provider then if bookinfo._no_provider then
-- for unspported files: don't show extension on the right, -- for unspported files: don't show extension on the right,
@ -452,7 +454,7 @@ function ListMenuItem:update()
end end
corner_mark = ImageWidget:new{ corner_mark = ImageWidget:new{
file = "resources/icons/dogear.png", file = "resources/icons/dogear.png",
rotation_angle = 270, rotation_angle = BD.mirroredUILayout() and 180 or 270,
width = corner_mark_size, width = corner_mark_size,
height = corner_mark_size, height = corner_mark_size,
} }
@ -695,7 +697,12 @@ function ListMenuItem:paintTo(bb, x, y)
if self.shortcut_icon then if self.shortcut_icon then
-- align it on bottom left corner of sub-widget -- align it on bottom left corner of sub-widget
local target = self[1][1][2] local target = self[1][1][2]
local ix = 0 local ix
if BD.mirroredUILayout() then
ix = target.dimen.w - self.shortcut_icon.dimen.w
else
ix = 0
end
local iy = target.dimen.h - self.shortcut_icon.dimen.h local iy = target.dimen.h - self.shortcut_icon.dimen.h
self.shortcut_icon:paintTo(bb, x+ix, y+iy) self.shortcut_icon:paintTo(bb, x+ix, y+iy)
end end
@ -703,7 +710,12 @@ function ListMenuItem:paintTo(bb, x, y)
-- to which we paint over a dogear if needed -- to which we paint over a dogear if needed
if corner_mark and self.do_hint_opened and self.been_opened then if corner_mark and self.do_hint_opened and self.been_opened then
-- align it on bottom right corner of widget -- align it on bottom right corner of widget
local ix = self.width - corner_mark:getSize().w local ix
if BD.mirroredUILayout() then
ix = 0
else
ix = self.width - corner_mark:getSize().w
end
local iy = self.height - corner_mark:getSize().h local iy = self.height - corner_mark:getSize().h
corner_mark:paintTo(bb, x+ix, y+iy) corner_mark:paintTo(bb, x+ix, y+iy)
end end
@ -716,10 +728,22 @@ function ListMenuItem:paintTo(bb, x, y)
if self.do_cover_image and target[1][1][1] then if self.do_cover_image and target[1][1][1] then
-- it has an image, align it on image's framecontainer's right border -- it has an image, align it on image's framecontainer's right border
target = target[1][1] target = target[1][1]
bb:paintBorder(target.dimen.x + target.dimen.w - 1, target.dimen.y, d_w, d_h, 1) local ix
if BD.mirroredUILayout() then
ix = target.dimen.x - d_w + 1
else
ix = target.dimen.x + target.dimen.w - 1
end
bb:paintBorder(ix, target.dimen.y, d_w, d_h, 1)
else else
-- no image, align it to the left border -- no image, align it to the left border
bb:paintBorder(x, y, d_w, d_h, 1) local ix
if BD.mirroredUILayout() then
ix = target.dimen.x + target.dimen.w - d_w
else
ix = x
end
bb:paintBorder(ix, y, d_w, d_h, 1)
end end
end end
end end
@ -884,7 +908,7 @@ function ListMenu:_updateItemsBuildUI()
height = self.item_height, height = self.item_height,
width = self.item_width, width = self.item_width,
entry = entry, entry = entry,
text = util.getMenuText(entry), text = getMenuText(entry),
show_parent = self.show_parent, show_parent = self.show_parent,
mandatory = entry.mandatory, mandatory = entry.mandatory,
dimen = self.item_dimen:new(), dimen = self.item_dimen:new(),

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
@ -28,7 +29,7 @@ local util = require("util")
local _ = require("gettext") local _ = require("gettext")
local Screen = Device.screen local Screen = Device.screen
local T = require("ffi/util").template local T = require("ffi/util").template
local getMenuText = require("util").getMenuText local getMenuText = require("ui/widget/menu").getMenuText
local BookInfoManager = require("bookinfomanager") local BookInfoManager = require("bookinfomanager")
@ -195,7 +196,7 @@ function FakeCover:init()
end end
if filename then if filename then
filename_wg = TextBoxWidget:new{ filename_wg = TextBoxWidget:new{
text = filename, text = BD.filename(filename),
face = Font:getFace("cfont", math.max(self.filename_font_max - sizedec, self.filename_font_min)), face = Font:getFace("cfont", math.max(self.filename_font_max - sizedec, self.filename_font_min)),
width = text_width, width = text_width,
alignment = "center", alignment = "center",
@ -607,7 +608,12 @@ function MosaicMenuItem:paintTo(bb, x, y)
if self.shortcut_icon then if self.shortcut_icon then
-- align it on bottom left corner of widget -- align it on bottom left corner of widget
local target = self local target = self
local ix = 0 local ix
if BD.mirroredUILayout() then
ix = target.dimen.w - self.shortcut_icon.dimen.w
else
ix = 0
end
local iy = target.dimen.h - self.shortcut_icon.dimen.h local iy = target.dimen.h - self.shortcut_icon.dimen.h
self.shortcut_icon:paintTo(bb, x+ix, y+iy) self.shortcut_icon:paintTo(bb, x+ix, y+iy)
end end
@ -616,7 +622,12 @@ function MosaicMenuItem:paintTo(bb, x, y)
if corner_mark and self.do_hint_opened and self.been_opened then if corner_mark and self.do_hint_opened and self.been_opened then
-- align it on bottom right corner of sub-widget -- align it on bottom right corner of sub-widget
local target = self[1][1][1] local target = self[1][1][1]
local ix = self.width - math.ceil((self.width - target.dimen.w)/2) - corner_mark:getSize().w local ix
if BD.mirroredUILayout() then
ix = math.floor((self.width - target.dimen.w)/2)
else
ix = self.width - math.ceil((self.width - target.dimen.w)/2) - corner_mark:getSize().w
end
local iy = self.height - math.ceil((self.height - target.dimen.h)/2) - corner_mark:getSize().h local iy = self.height - math.ceil((self.height - target.dimen.h)/2) - corner_mark:getSize().h
-- math.ceil() makes it looks better than math.floor() -- math.ceil() makes it looks better than math.floor()
corner_mark:paintTo(bb, x+ix, y+iy) corner_mark:paintTo(bb, x+ix, y+iy)
@ -629,15 +640,27 @@ function MosaicMenuItem:paintTo(bb, x, y)
local d_w = Screen:scaleBySize(3) local d_w = Screen:scaleBySize(3)
local d_h = math.ceil(target.dimen.h / 8) local d_h = math.ceil(target.dimen.h / 8)
-- Paint it directly relative to target.dimen.x/y which has been computed at this point -- Paint it directly relative to target.dimen.x/y which has been computed at this point
local ix = target.dimen.w - 1 local ix
local iy = 0 if BD.mirroredUILayout() then
bb:paintBorder(target.dimen.x+ix, target.dimen.y+iy, d_w, d_h, 1) ix = - d_w + 1
local x_overflow = target.dimen.x+ix+d_w - x - self.dimen.w
if x_overflow > 0 then
-- Set alternate dimen to be marked as dirty to include this description in refresh -- Set alternate dimen to be marked as dirty to include this description in refresh
self.refresh_dimen = self[1].dimen:copy() local x_overflow_left = x - target.dimen.x+ix -- positive if overflow
self.refresh_dimen.w = self.refresh_dimen.w + x_overflow if x_overflow_left > 0 then
self.refresh_dimen = self[1].dimen:copy()
self.refresh_dimen.x = self.refresh_dimen.x - x_overflow_left
self.refresh_dimen.w = self.refresh_dimen.w + x_overflow_left
end
else
ix = target.dimen.w - 1
-- Set alternate dimen to be marked as dirty to include this description in refresh
local x_overflow_right = target.dimen.x+ix+d_w - x - self.dimen.w
if x_overflow_right > 0 then
self.refresh_dimen = self[1].dimen:copy()
self.refresh_dimen.w = self.refresh_dimen.w + x_overflow_right
end
end end
local iy = 0
bb:paintBorder(target.dimen.x+ix, target.dimen.y+iy, d_w, d_h, 1)
end end
end end
@ -732,7 +755,7 @@ function MosaicMenu:_recalculateDimen()
end end
corner_mark = ImageWidget:new{ corner_mark = ImageWidget:new{
file = "resources/icons/dogear.png", file = "resources/icons/dogear.png",
rotation_angle = 270, rotation_angle = BD.mirroredUILayout() and 180 or 270,
width = corner_mark_size, width = corner_mark_size,
height = corner_mark_size, height = corner_mark_size,
} }

@ -1,3 +1,4 @@
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
@ -239,15 +240,20 @@ function DoubleKeyValuePage:init()
text_font_bold = false, text_font_bold = false,
} }
-- group for page info -- group for page info
local chevron_left = "resources/icons/appbar.chevron.left.png"
local chevron_right = "resources/icons/appbar.chevron.right.png"
if BD.mirroredUILayout() then
chevron_left, chevron_right = chevron_right, chevron_left
end
self.page_info_left_chev = Button:new{ self.page_info_left_chev = Button:new{
icon = "resources/icons/appbar.chevron.left.png", icon = chevron_left,
callback = function() self:prevPage() end, callback = function() self:prevPage() end,
bordersize = 0, bordersize = 0,
show_parent = self, show_parent = self,
} }
self.page_info_right_chev = Button:new{ self.page_info_right_chev = Button:new{
icon = "resources/icons/appbar.chevron.right.png", icon = chevron_right,
callback = function() self:_nextPage() end, callback = function() self:nextPage() end,
bordersize = 0, bordersize = 0,
show_parent = self, show_parent = self,
} }
@ -413,16 +419,17 @@ function DoubleKeyValuePage:onPrevPage()
end end
function DoubleKeyValuePage:onSwipe(arg, ges_ev) function DoubleKeyValuePage:onSwipe(arg, ges_ev)
if ges_ev.direction == "west" then local direction = BD.flipDirectionIfMirroredUILayout(ges_ev.direction)
if direction == "west" then
self:_nextPage() self:_nextPage()
return true return true
elseif ges_ev.direction == "east" then elseif direction == "east" then
self:prevPage() self:prevPage()
return true return true
elseif ges_ev.direction == "south" then elseif direction == "south" then
-- Allow easier closing with swipe down -- Allow easier closing with swipe down
self:onClose() self:onClose()
elseif ges_ev.direction == "north" then elseif direction == "north" then
-- no use for now -- no use for now
do end -- luacheck: ignore 541 do end -- luacheck: ignore 541
else -- diagonal swipe else -- diagonal swipe

Loading…
Cancel
Save