diff --git a/frontend/apps/filemanager/filemanager.lua b/frontend/apps/filemanager/filemanager.lua index 14decd52373d..666e9ea2485a 100644 --- a/frontend/apps/filemanager/filemanager.lua +++ b/frontend/apps/filemanager/filemanager.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Button = require("ui/widget/button") local ButtonDialogTitle = require("ui/widget/buttondialogtitle") @@ -109,6 +110,7 @@ function FileManager:init() self.path_text = TextWidget:new{ face = Font:getFace("xx_smallinfofont"), text = filemanagerutil.abbreviate(self.root_path), + para_direction_rtl = false, -- force LTR max_width = Screen:getWidth() - 2*Size.padding.small, truncate_left = true, } @@ -456,9 +458,10 @@ function FileManager:onShowPlusMenu() end function FileManager:onSwipeFM(ges) - if ges.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges.direction) + if direction == "west" then self.file_chooser:onNextPage() - elseif ges.direction == "east" then + elseif direction == "east" then self.file_chooser:onPrevPage() end return true diff --git a/frontend/apps/filemanager/filemanagermenu.lua b/frontend/apps/filemanager/filemanagermenu.lua index 5ccbad44448d..bb325171d559 100644 --- a/frontend/apps/filemanager/filemanagermenu.lua +++ b/frontend/apps/filemanager/filemanagermenu.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local CenterContainer = require("ui/widget/container/centercontainer") local CloudStorage = require("apps/cloudstorage/cloudstorage") local ConfirmBox = require("ui/widget/confirmbox") @@ -399,6 +400,37 @@ function FileManagerMenu:setUpdateItemTable() }) end, }) + table.insert(self.menu_items.developer_options.sub_item_table, { + text = "UI layout mirroring and text direction", + sub_item_table = { + { + text = _("Reverse language natural UI layout mirroring"), + checked_func = function() + return G_reader_settings:isTrue("reverse_lang_ui_layout_mirroring") + end, + callback = function() + G_reader_settings:flipNilOrFalse("reverse_lang_ui_layout_mirroring") + local InfoMessage = require("ui/widget/infomessage") + UIManager:show(InfoMessage:new{ + text = _("This will take effect on next restart."), + }) + end + }, + { + text = _("Reverse language natural UI text direction"), + checked_func = function() + return G_reader_settings:isTrue("reverse_lang_ui_text_direction") + end, + callback = function() + G_reader_settings:flipNilOrFalse("reverse_lang_ui_text_direction") + local InfoMessage = require("ui/widget/infomessage") + UIManager:show(InfoMessage:new{ + text = _("This will take effect on next restart."), + }) + end + } + } + }) self.menu_items.cloud_storage = { text = _("Cloud storage"), @@ -580,10 +612,10 @@ function FileManagerMenu:_getTabIndexFromLocation(ges) return last_tab_index -- if the start position is far right 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 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 else return last_tab_index diff --git a/frontend/apps/reader/modules/readerbookmark.lua b/frontend/apps/reader/modules/readerbookmark.lua index e9c4f6443da0..5b0c76c13700 100644 --- a/frontend/apps/reader/modules/readerbookmark.lua +++ b/frontend/apps/reader/modules/readerbookmark.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local CenterContainer = require("ui/widget/container/centercontainer") local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") @@ -231,7 +232,7 @@ function ReaderBookmark:onShowBookmark() w = Screen:getWidth(), h = Screen:getHeight(), }, - direction = "east" + direction = BD.mirrorDirectionIfMirroredUILayout("east") } } } diff --git a/frontend/apps/reader/modules/readerdogear.lua b/frontend/apps/reader/modules/readerdogear.lua index 51c562242f9e..5d3898e6d539 100644 --- a/frontend/apps/reader/modules/readerdogear.lua +++ b/frontend/apps/reader/modules/readerdogear.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Device = require("device") local Geom = require("ui/geometry") 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}, ImageWidget:new{ file = "resources/icons/dogear.png", + rotation_angle = BD.mirroredUILayout() and 90 or 0, width = self.dogear_size, height = self.dogear_size, } diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index f19b2ad03d4a..8d40b8d7e206 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") local CenterContainer = require("ui/widget/container/centercontainer") @@ -21,6 +22,7 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer") local util = require("util") local T = require("ffi/util").template local _ = require("gettext") +local C_ = _.pgettext local Screen = Device.screen local MODE = { @@ -41,26 +43,45 @@ local symbol_prefix = { letters = { time = nil, pages_left = "=>", - battery = "B:", - percentage = "R:", - book_time_to_read = "TB:", - chapter_time_to_read = "TC:", - frontlight = "L:", - mem_usage = "M:", - wifi_status = "W:", + battery = C_("FooterLetterPrefix", "B:"), + percentage = C_("FooterLetterPrefix", "R:"), + book_time_to_read = C_("FooterLetterPrefix", "TB:"), + chapter_time_to_read = C_("FooterLetterPrefix", "TC:"), + frontlight = C_("FooterLetterPrefix", "L:"), + mem_usage = C_("FooterLetterPrefix", "M:"), + wifi_status = C_("FooterLetterPrefix", "W:"), }, icons = { time = "⌚", - pages_left = "⇒", + pages_left = BD.mirroredUILayout() and "⇐" or "⇒", battery = "", - percentage = "⤠", + percentage = BD.mirroredUILayout() and "⤟" or "⤠", book_time_to_read = "⏳", - chapter_time_to_read = "⤻", + chapter_time_to_read = BD.mirroredUILayout() and "⥖" or "⤻", frontlight = "☼", mem_usage = "", 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_THIN_DEFAULT_HEIGHT = 3 @@ -115,9 +136,9 @@ local footerTextGeneratorMap = { prefix = "" end end - return prefix .. batt_lvl .. "%" + return BD.wrap(prefix) .. batt_lvl .. "%" else - return prefix .. " " .. (powerd:isCharging() and "+" or "") .. batt_lvl .. "%" + return BD.wrap(prefix) .. " " .. (powerd:isCharging() and "+" or "") .. batt_lvl .. "%" end end, time = function(footer) @@ -1230,10 +1251,13 @@ function ReaderFooter:genAllFooterText() elseif self.settings.items_separator == "bullet" then separator = " • " 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 - table.insert(info, gen(self)) + table.insert(info, BD.wrap(gen(self))) end - return table.concat(info, separator) + return table.concat(info, BD.wrap(separator)) end function ReaderFooter:setTocMarkers(reset) diff --git a/frontend/apps/reader/modules/readergesture.lua b/frontend/apps/reader/modules/readergesture.lua index 8fffabe7f311..2cccf378d097 100644 --- a/frontend/apps/reader/modules/readergesture.lua +++ b/frontend/apps/reader/modules/readergesture.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local ConfirmBox = require("ui/widget/confirmbox") local DataStorage = require("datastorage") local Device = require("device") @@ -225,6 +226,44 @@ function ReaderGesture:init() } local gm = G_reader_settings:readSetting(self.ges_mode) 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:initGesture() 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 document_module.inverse_reading_order = not document_module.inverse_reading_order 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{ - 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, }) return true diff --git a/frontend/apps/reader/modules/readerhighlight.lua b/frontend/apps/reader/modules/readerhighlight.lua index 407ceb369596..a12946a7745a 100644 --- a/frontend/apps/reader/modules/readerhighlight.lua +++ b/frontend/apps/reader/modules/readerhighlight.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local ButtonDialog = require("ui/widget/buttondialog") local Device = require("device") local Event = require("ui/event") @@ -350,9 +351,19 @@ function ReaderHighlight:onShowHighlightDialog(page, index) } 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, { { - text = "◁⇱", + text = start_prev, callback = function() self:updateHighlight(page, index, 0, -1, false) end, @@ -362,7 +373,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index) end }, { - text = "⇱▷", + text = start_next, callback = function() self:updateHighlight(page, index, 0, 1, false) end, @@ -372,7 +383,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index) end }, { - text = "◁⇲", + text = end_prev, callback = function() self:updateHighlight(page, index, 1, -1, false) end, @@ -381,7 +392,7 @@ function ReaderHighlight:onShowHighlightDialog(page, index) end }, { - text = "⇲▷", + text = end_next, callback = function() self:updateHighlight(page, index, 1, 1, false) end, @@ -577,6 +588,14 @@ function ReaderHighlight:onHoldPan(_, ges) and self.holdpan_pos.x < 1/8*Screen:getWidth() local is_in_bottom_right_corner = self.holdpan_pos.y > 7/8*Screen:getHeight() 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 self.was_in_some_corner then -- Do nothing, wait for the user to move his finger out of that corner @@ -781,6 +800,8 @@ function ReaderHighlight:viewSelectionHTML(debug_view) text = css_text or _("Failed getting CSS content"), text_face = Font:getFace("smallinfont"), justified = false, + para_direction_rtl = false, + auto_para_direction = false, buttons_table = { {{ text = _("Prettify"), @@ -792,6 +813,8 @@ function ReaderHighlight:viewSelectionHTML(debug_view) text = prettifyCss(css_text), text_face = Font:getFace("smallinfont"), justified = false, + para_direction_rtl = false, + auto_para_direction = false, }) end, }}, @@ -838,6 +861,8 @@ function ReaderHighlight:viewSelectionHTML(debug_view) text = html, text_face = Font:getFace("smallinfont"), justified = false, + para_direction_rtl = false, + auto_para_direction = false, buttons_table = buttons_table, } UIManager:show(textviewer) diff --git a/frontend/apps/reader/modules/readerlink.lua b/frontend/apps/reader/modules/readerlink.lua index 5b76acb3dbfc..64d4faa8dd6a 100644 --- a/frontend/apps/reader/modules/readerlink.lua +++ b/frontend/apps/reader/modules/readerlink.lua @@ -2,6 +2,7 @@ ReaderLink is an abstraction for document-specific link interfaces. ]] +local BD = require("ui/bidi") local ButtonDialogTitle = require("ui/widget/buttondialogtitle") local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") @@ -689,7 +690,8 @@ function ReaderLink:onGoBackLink(show_notification_if_empty) end function ReaderLink:onSwipe(arg, ges) - if ges.direction == "east" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges.direction) + if direction == "east" then if isSwipeToGoBackEnabled() then if #self.location_stack > 0 then -- Remember if location stack is going to be empty, so we @@ -709,7 +711,7 @@ function ReaderLink:onSwipe(arg, ges) return true end end - elseif ges.direction == "west" then + elseif direction == "west" then local ret = false if isSwipeToFollowNearestLinkEnabled() then ret = self:onGoToPageLink(ges, isSwipeIgnoreExternalLinksEnabled(), isFootnoteLinkInPopupEnabled()) @@ -1062,12 +1064,13 @@ function ReaderLink:showAsFootnotePopup(link, neglect_current_location) -- then just ignore the whole stylesheet, including our own declarations -- we add at start) -- - -- flags = 0x0000 to get the simplest/purest HTML without CSS + -- flags = 0x0001 to get the simplest/purest HTML without CSS, and dir= + -- and lang= attributes grabbed from parent nodes local html if extStartXP and extEndXP then - html = self.ui.document:getHTMLFromXPointers(extStartXP, extEndXP, 0x0000) + html = self.ui.document:getHTMLFromXPointers(extStartXP, extEndXP, 0x0001) else - html = self.ui.document:getHTMLFromXPointer(target_xpointer, 0x0000, true) + html = self.ui.document:getHTMLFromXPointer(target_xpointer, 0x0001, true) -- from_final_parent = true to get a possibly more complete footnote end if not html then diff --git a/frontend/apps/reader/modules/readermenu.lua b/frontend/apps/reader/modules/readermenu.lua index fe304f921cbf..697c37dac703 100644 --- a/frontend/apps/reader/modules/readermenu.lua +++ b/frontend/apps/reader/modules/readermenu.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local CenterContainer = require("ui/widget/container/centercontainer") local ConfirmBox = require("ui/widget/confirmbox") local Device = require("device") @@ -373,10 +374,10 @@ function ReaderMenu:_getTabIndexFromLocation(ges) return self.last_tab_index -- if the start position is far right 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 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 else return self.last_tab_index diff --git a/frontend/apps/reader/modules/readerpaging.lua b/frontend/apps/reader/modules/readerpaging.lua index 5b03c9263b46..9f6f34e5bcc7 100644 --- a/frontend/apps/reader/modules/readerpaging.lua +++ b/frontend/apps/reader/modules/readerpaging.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Device = require("device") local Event = require("ui/event") local Geom = require("ui/geometry") @@ -111,7 +112,11 @@ function ReaderPaging:setupTapTouchZones() ratio_w = DTAP_ZONE_BACKWARD.w, ratio_h = DTAP_ZONE_BACKWARD.h, } + local do_mirror = BD.mirroredUILayout() 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 backward_zone.ratio_x = 1 - backward_zone.ratio_x - backward_zone.ratio_w end @@ -373,39 +378,42 @@ function ReaderPaging:pageFlipping(flipping_page, flipping_ges) local steps = #self.flip_steps local stp_proportion = flipping_ges.distance / Screen:getWidth() local abs_proportion = flipping_ges.distance / Screen:getHeight() - if flipping_ges.direction == "east" then + local direction = BD.mirrorDirectionIfMirroredUILayout(flipping_ges.direction) + if direction == "east" then 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)]) - elseif flipping_ges.direction == "south" then + elseif direction == "south" then 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)) end UIManager:setDirty(self.view.dialog, "partial") end function ReaderPaging:bookmarkFlipping(flipping_page, flipping_ges) - if flipping_ges.direction == "east" then + local direction = BD.mirrorDirectionIfMirroredUILayout(flipping_ges.direction) + if direction == "east" then 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)) end UIManager:setDirty(self.view.dialog, "partial") end function ReaderPaging:onSwipe(_, ges) + local direction = BD.mirrorDirectionIfMirroredUILayout(ges.direction) if self.bookmark_flipping_mode then self:bookmarkFlipping(self.current_page, ges) elseif self.page_flipping_mode and self.original_page then self:_gotoPage(self.original_page) - elseif ges.direction == "west" then + elseif direction == "west" then if self.inverse_reading_order then self:onGotoViewRel(-1) else self:onGotoViewRel(1) end - elseif ges.direction == "east" then + elseif direction == "east" then if self.inverse_reading_order then self:onGotoViewRel(1) else diff --git a/frontend/apps/reader/modules/readerrolling.lua b/frontend/apps/reader/modules/readerrolling.lua index 66cb24c36626..2d29ddf930e6 100644 --- a/frontend/apps/reader/modules/readerrolling.lua +++ b/frontend/apps/reader/modules/readerrolling.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local ConfirmBox = require("ui/widget/confirmbox") 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, } + local do_mirror = BD.mirroredUILayout() 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 backward_zone.ratio_x = 1 - backward_zone.ratio_x - backward_zone.ratio_w @@ -464,13 +469,14 @@ function ReaderRolling:getLastPercent() end function ReaderRolling:onSwipe(_, ges) - if ges.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges.direction) + if direction == "west" then if self.inverse_reading_order then self:onGotoViewRel(-1) else self:onGotoViewRel(1) end - elseif ges.direction == "east" then + elseif direction == "east" then if self.inverse_reading_order then self:onGotoViewRel(1) else @@ -597,20 +603,36 @@ function ReaderRolling:onGotoXPointer(xp, marker_xp) -- 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)) - if self.ui.document:getVisiblePageCount() > 1 and screen_x > Screen:getWidth() / 2 then - -- On right page in 2-pages mode - -- We could show the marker on the right of the page with: - -- screen_x = Screen:getWidth() - marker_w - -- But it's best to show it on the left of text, so in - -- the middle margin, so it still shows just left of a - -- footnote number. - -- 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 - else - screen_x = 0 + if self.ui.document:getVisiblePageCount() > 1 then -- 2-pages mode + if screen_x < Screen:getWidth() / 2 then -- On left page + if BD.mirroredUILayout() then + -- In the middle margin, on the right of text + -- Same trick as below, assuming page2_x is equal to page 1 right x + 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 + screen_x = screen_x - marker_w + else + screen_x = 0 -- In left page left margin + end + else -- On right page + 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 self.mark_func = function() diff --git a/frontend/apps/reader/modules/readersearch.lua b/frontend/apps/reader/modules/readersearch.lua index e2986121e593..dcb640b2a5d6 100644 --- a/frontend/apps/reader/modules/readersearch.lua +++ b/frontend/apps/reader/modules/readersearch.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local ButtonDialog = require("ui/widget/buttondialog") local InputContainer = require("ui/widget/container/inputcontainer") local UIManager = require("ui/uimanager") @@ -28,6 +29,11 @@ function ReaderSearch:addToMainMenu(menu_items) end function ReaderSearch:onShowFulltextSearchInput() + local backward_text = "◁" + local forward_text = "▷" + if BD.mirroredUILayout() then + backward_text, forward_text = forward_text, backward_text + end self:onInput{ title = _("Enter text to search for"), type = "text", @@ -40,14 +46,14 @@ function ReaderSearch:onShowFulltextSearchInput() end, }, { - text = "◁", + text = backward_text, callback = function() self:onShowSearchDialog(self.input_dialog:getInputText(), 1) self:closeInputDialog() end, }, { - text = "▷", + text = forward_text, is_enter_default = true, callback = function() self:onShowSearchDialog(self.input_dialog:getInputText(), 0) @@ -138,24 +144,33 @@ function ReaderSearch:onShowSearchDialog(text, direction) 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{ -- alpha = 0.7, buttons = { { { - text = "▕◁", + text = from_start_text, callback = do_search(self.searchFromStart, text), }, { - text = "◁", + text = backward_text, callback = do_search(self.searchNext, text, 1), }, { - text = "▷", + text = forward_text, callback = do_search(self.searchNext, text, 0), }, { - text = "▷▏", + text = from_end_text, callback = do_search(self.searchFromEnd, text), }, } diff --git a/frontend/apps/reader/modules/readerstyletweak.lua b/frontend/apps/reader/modules/readerstyletweak.lua index f69df86bf103..e9eb9fae2bec 100644 --- a/frontend/apps/reader/modules/readerstyletweak.lua +++ b/frontend/apps/reader/modules/readerstyletweak.lua @@ -97,6 +97,7 @@ function TweakInfoWidget:init() text = css, face = Font:getFace("infont", 16), width = self.width - 2*Size.padding.large, + para_direction_rtl = false, -- LTR } }) if self.is_global_default then diff --git a/frontend/apps/reader/modules/readertoc.lua b/frontend/apps/reader/modules/readertoc.lua index b2493beba1d5..d7b3a4c1e727 100644 --- a/frontend/apps/reader/modules/readertoc.lua +++ b/frontend/apps/reader/modules/readertoc.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Button = require("ui/widget/button") local CenterContainer = require("ui/widget/container/centercontainer") local ConfirmBox = require("ui/widget/confirmbox") @@ -285,6 +286,7 @@ function ReaderToc:onShowToc() -- update collapsible state self.expand_button = Button:new{ icon = "resources/icons/appbar.control.expand.png", + icon_rotation_angle = BD.mirroredUILayout() and 180 or 0, width = Screen:scaleBySize(30), bordersize = 0, show_parent = self, @@ -338,7 +340,7 @@ function ReaderToc:onShowToc() w = Screen:getWidth(), h = Screen:getHeight(), }, - direction = "west" + direction = BD.mirrorDirectionIfMirroredUILayout("west") } } } @@ -352,7 +354,15 @@ function ReaderToc:onShowToc() function toc_menu:onMenuSelect(item, pos) -- 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 - 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() else toc_menu:close_callback() diff --git a/frontend/apps/reader/modules/readerwikipedia.lua b/frontend/apps/reader/modules/readerwikipedia.lua index bbddd6ed8402..889a606acec6 100644 --- a/frontend/apps/reader/modules/readerwikipedia.lua +++ b/frontend/apps/reader/modules/readerwikipedia.lua @@ -488,6 +488,7 @@ function ReaderWikipedia:lookupWikipedia(word, box, get_fullpage, forced_lang) definition = definition, is_fullpage = get_fullpage, lang = lang, + rtl_lang = Wikipedia:isWikipediaLanguageRTL(lang), images = page.images, } table.insert(results, result) diff --git a/frontend/apps/reader/skimtowidget.lua b/frontend/apps/reader/skimtowidget.lua index 710dc7d435b2..a2538ef28777 100644 --- a/frontend/apps/reader/skimtowidget.lua +++ b/frontend/apps/reader/skimtowidget.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Button = require("ui/widget/button") local CenterContainer = require("ui/widget/container/centercontainer") @@ -91,7 +92,7 @@ function SkimToWidget:init() text = dialog_title, face = self.title_face, bold = true, - width = self.screen_width * 0.95, + max_width = self.screen_width * 0.95, }, } @@ -122,6 +123,7 @@ function SkimToWidget:init() h = skimto_title:getSize().h }, skimto_title, + allow_mirroring = true, CloseButton:new{ window = self, padding_top = Size.margin.title, }, } local button_minus = Button:new{ @@ -198,8 +200,16 @@ function SkimToWidget:init() 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{ - text = '▷│', + text = chapter_next_text, bordersize = self.button_bordersize, margin = self.button_margin, radius = 0, @@ -218,7 +228,7 @@ function SkimToWidget:init() } local button_chapter_prev = Button:new{ - text = "│◁", + text = chapter_prev_text, bordersize = self.button_bordersize, margin = self.button_margin, radius = 0, @@ -237,7 +247,7 @@ function SkimToWidget:init() } local button_bookmark_next = Button:new{ - text = "☆▷", + text = bookmark_next_text, bordersize = self.button_bordersize, margin = self.button_margin, radius = 0, @@ -265,7 +275,7 @@ function SkimToWidget:init() } local button_bookmark_prev = Button:new{ - text = "◁☆", + text = bookmark_prev_text, bordersize = self.button_bordersize, margin = self.button_margin, radius = 0, @@ -420,9 +430,10 @@ end function SkimToWidget:onTapProgress(arg, ges_ev) if ges_ev.pos:intersectWith(self.progress_bar.dimen) then - local width = self.progress_bar.dimen.w - local pos = ges_ev.pos.x - self.progress_bar.dimen.x - local perc = pos / width + local perc = self.progress_bar:getPercentageFromPosition(ges_ev.pos) + if not perc then + return true + end local page = Math.round(perc * self.page_count) self:addOriginToLocationStack() self.ui:handleEvent(Event:new("GotoPage", page )) diff --git a/frontend/ui/widget/button.lua b/frontend/ui/widget/button.lua index ac39d54d9003..33d704353eff 100644 --- a/frontend/ui/widget/button.lua +++ b/frontend/ui/widget/button.lua @@ -33,6 +33,7 @@ local Button = InputContainer:new{ text = nil, -- mandatory text_func = nil, icon = nil, + icon_rotation_angle = 0, preselect = false, callback = nil, enabled = true, @@ -74,6 +75,7 @@ function Button:init() else self.label_widget = ImageWidget:new{ file = self.icon, + rotation_angle = self.icon_rotation_angle, dim = not self.enabled, scale_for_dpi = true, } diff --git a/frontend/ui/widget/checkmark.lua b/frontend/ui/widget/checkmark.lua index dd836e3c6cb6..de25076efcef 100644 --- a/frontend/ui/widget/checkmark.lua +++ b/frontend/ui/widget/checkmark.lua @@ -14,6 +14,7 @@ Example: ]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Font = require("ui/font") local InputContainer = require("ui/widget/container/inputcontainer") @@ -27,30 +28,41 @@ local CheckMark = InputContainer:new{ face = Font:getFace("smallinfofont"), width = 0, height = 0, + _mirroredUI = BD.mirroredUILayout(), } 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). + -- XXX remove if we end up reversing both mirroring and rtl + local para_direction_rtl = self._mirroredUI local checked_widget = TextWidget:new{ text = " ✓", -- preceded by thin space for better alignment face = self.face, + para_direction_rtl = para_direction_rtl, } local unchecked_widget = TextWidget:new{ text = "▢ ", face = self.face, + para_direction_rtl = para_direction_rtl, } local disabled_checked_widget = TextWidget:new{ text = " ✓", -- preceded by thin space for better alignment face = self.face, fgcolor = Blitbuffer.COLOR_DARK_GRAY, + para_direction_rtl = para_direction_rtl, } local disabled_unchecked_widget = TextWidget:new{ text = "▢ ", face = self.face, fgcolor = Blitbuffer.COLOR_DARK_GRAY, + para_direction_rtl = para_direction_rtl, } local empty_widget = TextWidget:new{ text = "", face = self.face, + para_direction_rtl = para_direction_rtl, } local widget if self.checkable then diff --git a/frontend/ui/widget/closebutton.lua b/frontend/ui/widget/closebutton.lua index 19b4c419e867..49887432a4fa 100644 --- a/frontend/ui/widget/closebutton.lua +++ b/frontend/ui/widget/closebutton.lua @@ -34,11 +34,11 @@ function CloseButton:init() 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() - -- 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 - -- We also add the provided padding_right self[1] = FrameContainer:new{ bordersize = 0, diff --git a/frontend/ui/widget/container/framecontainer.lua b/frontend/ui/widget/container/framecontainer.lua index 5e5a2ce1f531..cc5ecad1db54 100644 --- a/frontend/ui/widget/container/framecontainer.lua +++ b/frontend/ui/widget/container/framecontainer.lua @@ -18,6 +18,7 @@ Example: --]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Geom = require("ui/geometry") local Size = require("ui/size") @@ -38,6 +39,8 @@ local FrameContainer = WidgetContainer:new{ width = nil, height = nil, invert = false, + allow_mirroring = true, + _mirroredUI = BD.mirroredUILayout(), } function FrameContainer:getSize() @@ -46,6 +49,9 @@ function FrameContainer:getSize() self._padding_right = self.padding_right or self.padding self._padding_bottom = self.padding_bottom or self.padding self._padding_left = self.padding_left or self.padding + if self._mirroredUI and self.allow_mirroring then + self._padding_left, self._padding_right = self._padding_right, self._padding_left + end return Geom:new{ w = content_size.w + ( self.margin + self.bordersize ) * 2 + self._padding_left + self._padding_right, h = content_size.h + ( self.margin + self.bordersize ) * 2 + self._padding_top + self._padding_bottom @@ -62,6 +68,11 @@ function FrameContainer:paintTo(bb, x, y) local container_width = self.width or my_size.w local container_height = self.height or my_size.h + local shift_x = 0 + if self._mirroredUI and self.allow_mirroring then + shift_x = container_width - my_size.w + end + --- @todo get rid of margin here? 13.03 2013 (houqp) if self.background then bb:paintRoundedRect(x, y, @@ -83,7 +94,7 @@ function FrameContainer:paintTo(bb, x, y) end if self[1] then self[1]:paintTo(bb, - x + self.margin + self.bordersize + self._padding_left, + x + self.margin + self.bordersize + self._padding_left + shift_x, y + self.margin + self.bordersize + self._padding_top) end if self.invert then diff --git a/frontend/ui/widget/container/leftcontainer.lua b/frontend/ui/widget/container/leftcontainer.lua index d1f1837e4a09..635e2e517a70 100644 --- a/frontend/ui/widget/container/leftcontainer.lua +++ b/frontend/ui/widget/container/leftcontainer.lua @@ -2,9 +2,13 @@ LeftContainer aligns its content (1 widget) at the left of its own dimensions --]] +local BD = require("ui/bidi") local WidgetContainer = require("ui/widget/container/widgetcontainer") -local LeftContainer = WidgetContainer:new() +local LeftContainer = WidgetContainer:new{ + allow_mirroring = true, + _mirroredUI = BD.mirroredUILayout(), +} function LeftContainer:paintTo(bb, x, y) local contentSize = self[1]:getSize() @@ -13,6 +17,9 @@ function LeftContainer:paintTo(bb, x, y) -- throw error? paint to scrap buffer and blit partially? -- for now, we ignore this -- end + if self._mirroredUI and self.allow_mirroring then + x = x + (self.dimen.w - contentSize.w) -- as in RightContainer + end self[1]:paintTo(bb, x , y + math.floor((self.dimen.h - contentSize.h)/2)) end diff --git a/frontend/ui/widget/container/rightcontainer.lua b/frontend/ui/widget/container/rightcontainer.lua index 605c0d9e341e..f86dc221a0e4 100644 --- a/frontend/ui/widget/container/rightcontainer.lua +++ b/frontend/ui/widget/container/rightcontainer.lua @@ -2,9 +2,13 @@ RightContainer aligns its content (1 widget) at the right of its own dimensions --]] +local BD = require("ui/bidi") local WidgetContainer = require("ui/widget/container/widgetcontainer") -local RightContainer = WidgetContainer:new() +local RightContainer = WidgetContainer:new{ + allow_mirroring = true, + _mirroredUI = BD.mirroredUILayout(), +} function RightContainer:paintTo(bb, x, y) local contentSize = self[1]:getSize() @@ -13,8 +17,12 @@ function RightContainer:paintTo(bb, x, y) -- throw error? paint to scrap buffer and blit partially? -- for now, we ignore this -- end + if not self._mirroredUI or not self.allow_mirroring then + x = x + (self.dimen.w - contentSize.w) + -- else: keep x, as in LeftContainer + end self[1]:paintTo(bb, - x + (self.dimen.w - contentSize.w), + x, y + math.floor((self.dimen.h - contentSize.h)/2)) end diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 0312d5c7b5cf..89cb59c5e1bf 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Button = require("ui/widget/button") local ButtonTable = require("ui/widget/buttontable") @@ -309,6 +310,9 @@ function DictQuickLookup:update() dialog = self, -- allow for disabling justification justified = G_reader_settings:nilOrTrue("dict_justify"), + lang = self.lang and self.lang:lower(), -- only available on wikipedia results + para_direction_rtl = self.rtl_lang, -- only available on wikipedia results + auto_para_direction = not self.is_wiki, -- only for dict results (we don't know their lang) image_alt_face = self.image_alt_face, images = self.images, } @@ -404,10 +408,15 @@ function DictQuickLookup:update() }, } 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 = { { { - text = "◁◁", + text = prev_dict_text, enabled = self:isPrevDictAvaiable(), callback = function() self:changeToPrevDict() @@ -426,7 +435,7 @@ function DictQuickLookup:update() end, }, { - text = "▷▷", + text = next_dict_text, enabled = self:isNextDictAvaiable(), callback = function() self:changeToNextDict() @@ -456,7 +465,10 @@ function DictQuickLookup:update() { -- if more than one language, enable it and display "current lang > next 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, callback = function() if self.is_wiki then @@ -656,6 +668,7 @@ function DictQuickLookup:changeDictionary(index) self.is_html = self.results[index].is_html self.css = self.results[index].css self.lang = self.results[index].lang + self.rtl_lang = self.results[index].rtl_lang self.images = self.results[index].images if self.images and #self.images > 0 then -- We'll be giving some images to textboxwidget that will @@ -731,7 +744,7 @@ function DictQuickLookup:onTapCloseDict(arg, ges_ev) -- processed for scrolling definition by ScrollTextWidget, which -- will pop it up for us here when it can't scroll anymore). -- 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 self:changeToPrevDict() if self.dict_index ~= prev_index then @@ -785,9 +798,10 @@ function DictQuickLookup:onSwipe(arg, ges) if ges.pos:intersectWith(self.definition_widget.dimen) then -- if we want changeDict to still work with swipe outside window : -- or not ges.pos:intersectWith(self.dict_frame.dimen) then - if ges.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges.direction) + if direction == "west" then self:changeToNextDict() - elseif ges.direction == "east" then + elseif direction == "east" then self:changeToPrevDict() else if self.refresh_callback then self.refresh_callback() end diff --git a/frontend/ui/widget/filechooser.lua b/frontend/ui/widget/filechooser.lua index 59c6dc2adc4e..b3343ba9c8d8 100644 --- a/frontend/ui/widget/filechooser.lua +++ b/frontend/ui/widget/filechooser.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Device = require("device") local DocSettings = require("docsettings") local DocumentRegistry = require("document/documentregistry") @@ -121,7 +122,7 @@ end function FileChooser:genItemTableFromPath(path) local dirs = {} local files = {} - local up_folder_arrow = "⬆ ../" + local up_folder_arrow = BD.mirroredUILayout() and BD.ltr("../ ⬆") or "⬆ ../" self.list(path, dirs, files) @@ -213,15 +214,18 @@ function FileChooser:genItemTableFromPath(path) local num_items = #sub_dirs + #dir_files local istr = ffiUtil.template(N_("1 item", "%1 items", num_items), num_items) local text + local bidi_wrap_func if dir.name == ".." then text = up_folder_arrow elseif dir.name == "." then -- possible with show_current_dir_for_hold text = _("Long-press to select current directory") else text = dir.name.."/" + bidi_wrap_func = BD.directory end table.insert(item_table, { text = text, + bidi_wrap_func = bidi_wrap_func, mandatory = istr, path = subdir_path, is_go_up = dir.name == ".." @@ -239,6 +243,7 @@ function FileChooser:genItemTableFromPath(path) local sstr = getFriendlySize(file_size) local file_item = { text = file.name, + bidi_wrap_func = BD.filename, mandatory = sstr, 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 UIManager:show(ConfirmBox:new{ text = T(_("Always open '%2' with %1?"), - provider.provider_name, filename_pure), + provider.provider_name, BD.filename(filename_pure)), ok_text = _("Always"), ok_callback = function() DocumentRegistry:setProvider(file, provider, false) @@ -485,7 +490,7 @@ function FileChooser:showSetProviderButtons(file, filemanager_instance, reader_u end 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, buttons = buttons, } diff --git a/frontend/ui/widget/footnotewidget.lua b/frontend/ui/widget/footnotewidget.lua index 00086d0b8e4f..ccb543cfa62e 100644 --- a/frontend/ui/widget/footnotewidget.lua +++ b/frontend/ui/widget/footnotewidget.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") 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 -- shown, we let MuPDF handle our left margin. 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) if self.css then -- add any provided css css = css .. "\n" .. self.css @@ -327,14 +332,15 @@ function FootnoteWidget:onTapClose(arg, ges) end function FootnoteWidget:onSwipeFollow(arg, ges) - if ges.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges.direction) + if direction == "west" then if self.follow_callback then if self.close_callback then self.close_callback(self.height) end return self.follow_callback() end - elseif ges.direction == "south" or ges.direction == "east" then + elseif direction == "south" or direction == "east" then UIManager:close(self) -- We can close with swipe down. If footnote is scrollable, -- this event will be eaten by ScrollHtmlWidget, and it will @@ -345,7 +351,7 @@ function FootnoteWidget:onSwipeFollow(arg, ges) self.close_callback(self.height) end return true - elseif ges.direction == "north" then + elseif direction == "north" then -- no use for now do end -- luacheck: ignore 541 else -- diagonal swipe diff --git a/frontend/ui/widget/frontlightwidget.lua b/frontend/ui/widget/frontlightwidget.lua index 43cdb8c6ff03..3fe9864f7a5e 100644 --- a/frontend/ui/widget/frontlightwidget.lua +++ b/frontend/ui/widget/frontlightwidget.lua @@ -651,9 +651,10 @@ function FrontLightWidget:onTapProgress(arg, ges_ev) -- Unschedule any pending updates. UIManager:unschedule(self.update) - local width = self.fl_group.dimen.w - local pos = ges_ev.pos.x - self.fl_group.dimen.x - local perc = pos / width + local perc = self.fl_group:getPercentageFromPosition(ges_ev.pos) + if not perc then + return true + end local num = Math.round(perc * self.fl_max) -- Always set the frontlight intensity. diff --git a/frontend/ui/widget/horizontalgroup.lua b/frontend/ui/widget/horizontalgroup.lua index 907428fb0246..23931a4a2b64 100644 --- a/frontend/ui/widget/horizontalgroup.lua +++ b/frontend/ui/widget/horizontalgroup.lua @@ -2,10 +2,14 @@ A layout widget that puts objects besides each other. --]] +local BD = require("ui/bidi") local WidgetContainer = require("ui/widget/container/widgetcontainer") +local util = require("util") local HorizontalGroup = WidgetContainer:new{ align = "center", + allow_mirroring = true, + _mirroredUI = BD.mirroredUILayout(), _size = nil, } @@ -13,6 +17,9 @@ function HorizontalGroup:getSize() if not self._size then self._size = { w = 0, h = 0 } self._offsets = { } + if self._mirroredUI and self.allow_mirroring then + util.arrayReverse(self) + end for i, widget in ipairs(self) do local w_size = widget:getSize() self._offsets[i] = { @@ -24,6 +31,9 @@ function HorizontalGroup:getSize() self._size.h = w_size.h end end + if self._mirroredUI and self.allow_mirroring then + util.arrayReverse(self) + end end return self._size end @@ -31,6 +41,9 @@ end function HorizontalGroup:paintTo(bb, x, y) local size = self:getSize() + if self._mirroredUI and self.allow_mirroring then + util.arrayReverse(self) + end for i, widget in ipairs(self) do if self.align == "center" then widget:paintTo(bb, @@ -45,6 +58,9 @@ function HorizontalGroup:paintTo(bb, x, y) self.align) end end + if self._mirroredUI and self.allow_mirroring then + util.arrayReverse(self) + end end function HorizontalGroup:clear() diff --git a/frontend/ui/widget/imageviewer.lua b/frontend/ui/widget/imageviewer.lua index 3eb24842c52b..c41ff7bf5938 100644 --- a/frontend/ui/widget/imageviewer.lua +++ b/frontend/ui/widget/imageviewer.lua @@ -2,6 +2,7 @@ ImageViewer displays an image with some simple manipulation options. ]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local ButtonTable = require("ui/widget/buttontable") local CenterContainer = require("ui/widget/container/centercontainer") @@ -459,11 +460,19 @@ function ImageViewer:onTap(_, ges) end 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 + local show_prev_image, show_next_image 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 self:switchToImageNum(self._images_list_cur - 1) 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 self:switchToImageNum(self._images_list_cur + 1) end diff --git a/frontend/ui/widget/inputdialog.lua b/frontend/ui/widget/inputdialog.lua index 5cbf3e970c7f..4f7dccf85443 100644 --- a/frontend/ui/widget/inputdialog.lua +++ b/frontend/ui/widget/inputdialog.lua @@ -181,6 +181,14 @@ local InputDialog = InputContainer:new{ button_padding = Size.padding.default, border_size = Size.border.window, + -- See TextBoxWidget for details about these options + alignment = "left", + justified = false, + lang = nil, + para_direction_rtl = nil, + auto_para_direction = false, + alignment_strict = false, + -- for internal use _text_modified = false, -- previous known modified status _top_line_num = nil, @@ -307,6 +315,9 @@ function InputDialog:init() width = self.text_width, padding = self.input_padding, margin = self.input_margin, + lang = self.lang, -- these might influence height + para_direction_rtl = self.para_direction_rtl, + auto_para_direction = self.auto_para_direction, } local text_height = input_widget:getTextHeight() local line_height = input_widget:getLineHeight() @@ -351,6 +362,12 @@ function InputDialog:init() text = self.input, hint = self.input_hint, face = self.input_face, + alignment = self.alignment, + justified = self.justified, + lang = self.lang, + para_direction_rtl = self.para_direction_rtl, + auto_para_direction = self.auto_para_direction, + alignment_strict = self.alignment_strict, width = self.text_width, height = self.text_height or nil, padding = self.input_padding, diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index cec05ac2ebd7..4f36a22e9d81 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -42,6 +42,14 @@ local InputText = InputContainer:new{ margin = Size.margin.default, bordersize = Size.border.inputtext, + -- See TextBoxWidget for details about these options + alignment = "left", + justified = false, + lang = nil, + para_direction_rtl = nil, + auto_para_direction = false, + alignment_strict = false, + -- for internal use text_widget = nil, -- Text Widget for cursor movement, possibly a ScrollTextWidget charlist = nil, -- table of individual chars from input string @@ -305,6 +313,9 @@ function InputText:initTextBox(text, char_added) charlist = show_charlist, face = self.face, width = text_width, + lang = self.lang, -- these might influence height + para_direction_rtl = self.para_direction_rtl, + auto_para_direction = self.auto_para_direction, } self.height = text_widget:getTextHeight() self.scroll = true @@ -319,6 +330,12 @@ function InputText:initTextBox(text, char_added) editable = self.focused, face = self.face, fgcolor = fgcolor, + alignment = self.alignment, + justified = self.justified, + lang = self.lang, + para_direction_rtl = self.para_direction_rtl, + auto_para_direction = self.auto_para_direction, + alignment_strict = self.alignment_strict, width = self.width, height = self.height, dialog = self.parent, @@ -334,6 +351,12 @@ function InputText:initTextBox(text, char_added) editable = self.focused, face = self.face, fgcolor = fgcolor, + alignment = self.alignment, + justified = self.justified, + lang = self.lang, + para_direction_rtl = self.para_direction_rtl, + auto_para_direction = self.auto_para_direction, + alignment_strict = self.alignment_strict, width = self.width, height = self.height, dialog = self.parent, diff --git a/frontend/ui/widget/keyvaluepage.lua b/frontend/ui/widget/keyvaluepage.lua index d023e6c7c6aa..b55b6efe1850 100644 --- a/frontend/ui/widget/keyvaluepage.lua +++ b/frontend/ui/widget/keyvaluepage.lua @@ -19,6 +19,7 @@ Example: ]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") local Button = require("ui/widget/button") @@ -334,6 +335,7 @@ function KeyValuePage:init() end -- return button + --- @todo: alternative icon if BD.mirroredUILayout() self.page_return_arrow = Button:new{ icon = "resources/icons/appbar.arrow.left.up.png", callback = function() self:onReturn() end, @@ -341,26 +343,34 @@ function KeyValuePage:init() show_parent = self, } -- 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{ - icon = "resources/icons/appbar.chevron.left.png", + icon = chevron_left, callback = function() self:prevPage() end, bordersize = 0, show_parent = self, } self.page_info_right_chev = Button:new{ - icon = "resources/icons/appbar.chevron.right.png", + icon = chevron_right, callback = function() self:nextPage() end, bordersize = 0, show_parent = self, } self.page_info_first_chev = Button:new{ - icon = "resources/icons/appbar.chevron.first.png", + icon = chevron_first, callback = function() self:goToPage(1) end, bordersize = 0, show_parent = self, } self.page_info_last_chev = Button:new{ - icon = "resources/icons/appbar.chevron.last.png", + icon = chevron_last, callback = function() self:goToPage(self.pages) end, bordersize = 0, show_parent = self, @@ -447,6 +457,7 @@ function KeyValuePage:init() local content = OverlapGroup:new{ dimen = self.dimen:copy(), + allow_mirroring = false, VerticalGroup:new{ align = "left", self.title_bar, @@ -558,16 +569,17 @@ function KeyValuePage:onPrevPage() end function KeyValuePage:onSwipe(arg, ges_ev) - if ges_ev.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges_ev.direction) + if direction == "west" then self:nextPage() return true - elseif ges_ev.direction == "east" then + elseif direction == "east" then self:prevPage() return true - elseif ges_ev.direction == "south" then + elseif direction == "south" then -- Allow easier closing with swipe down self:onClose() - elseif ges_ev.direction == "north" then + elseif direction == "north" then -- no use for now do end -- luacheck: ignore 541 else -- diagonal swipe diff --git a/frontend/ui/widget/listview.lua b/frontend/ui/widget/listview.lua index a77b20846f6e..551d7d86d289 100644 --- a/frontend/ui/widget/listview.lua +++ b/frontend/ui/widget/listview.lua @@ -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. ]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Device = require("device") local FrameContainer = require("ui/widget/container/framecontainer") @@ -113,10 +114,11 @@ function ListView:prevPage() end function ListView:onSwipe(arg, ges_ev) - if ges_ev.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges_ev.direction) + if direction == "west" then self:nextPage() return true - elseif ges_ev.direction == "east" then + elseif direction == "east" then self:prevPage() return true end diff --git a/frontend/ui/widget/menu.lua b/frontend/ui/widget/menu.lua index 618a6bdffed9..367aa91cf9a9 100644 --- a/frontend/ui/widget/menu.lua +++ b/frontend/ui/widget/menu.lua @@ -2,6 +2,7 @@ Widget that displays a shortcut icon for menu item. --]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") local Button = require("ui/widget/button") @@ -33,7 +34,6 @@ local util = require("ffi/util") local _ = require("gettext") local Input = Device.input local Screen = Device.screen -local getMenuText = require("util").getMenuText local ItemShortCutIcon = WidgetContainer:new{ dimen = Geom:new{ w = Screen:scaleBySize(22), h = Screen:scaleBySize(22) }, @@ -92,21 +92,30 @@ local MenuCloseButton = InputContainer:new{ } function MenuCloseButton:init() - self[1] = TextWidget:new{ + local text_widget = TextWidget:new{ text = "×", face = Font:getFace("cfont", 30), -- this font size align nicely with title } - - local text_size = self[1]:getSize() - -- The text box height is greater than its width, and we want this × to - -- be diagonally aligned with our top right border + -- 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_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{ w = text_size.w + text_width_pad + self.padding_right, h = text_size.h, } - self.ges_events.Close = { GestureRange:new{ ges = "tap", @@ -401,6 +410,7 @@ function MenuItem:init() table.insert(hgroup, HorizontalSpan:new{ width = Size.span.horizontal_default }) end table.insert(hgroup, self._underline_container) + table.insert(hgroup, HorizontalSpan:new{ width = Size.padding.fullscreen }) self[1] = FrameContainer:new{ bordersize = 0, @@ -611,6 +621,7 @@ function Menu:init() self.path_text = TextWidget:new{ face = Font:getFace("xx_smallinfofont"), text = self.path, + para_direction_rtl = false, -- force LTR max_width = self.dimen.w - 2*Size.padding.small, truncate_left = true, } @@ -640,26 +651,34 @@ function Menu:init() -- group for items self.item_group = VerticalGroup:new{} -- 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{ - icon = "resources/icons/appbar.chevron.left.png", + icon = chevron_left, callback = function() self:onPrevPage() end, bordersize = 0, show_parent = self.show_parent, } self.page_info_right_chev = Button:new{ - icon = "resources/icons/appbar.chevron.right.png", + icon = chevron_right, callback = function() self:onNextPage() end, bordersize = 0, show_parent = self.show_parent, } self.page_info_first_chev = Button:new{ - icon = "resources/icons/appbar.chevron.first.png", + icon = chevron_first, callback = function() self:onFirstPage() end, bordersize = 0, show_parent = self.show_parent, } self.page_info_last_chev = Button:new{ - icon = "resources/icons/appbar.chevron.last.png", + icon = chevron_last, callback = function() self:onLastPage() end, bordersize = 0, show_parent = self.show_parent, @@ -806,6 +825,10 @@ function Menu:init() } end 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(), self.content_group, page_return, @@ -978,7 +1001,7 @@ function Menu:updateItems(select_number) show_parent = self.show_parent, state = self.item_table[i].state, 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, bold = self.item_table.current == i or self.item_table[i].bold == true, dim = self.item_table[i].dim, @@ -1236,11 +1259,12 @@ function Menu:onTapCloseAllMenus(arg, ges_ev) end function Menu:onSwipe(arg, ges_ev) - if ges_ev.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges_ev.direction) + if direction == "west" then self:onNextPage() - elseif ges_ev.direction == "east" then + elseif direction == "east" then self:onPrevPage() - elseif ges_ev.direction == "south" then + elseif direction == "south" then if self.has_close_button and not self.no_title then -- If there is a close button displayed (so, this Menu can be -- closed), allow easier closing with swipe up/down @@ -1248,7 +1272,7 @@ function Menu:onSwipe(arg, ges_ev) end -- If there is no close button, it's a top level Menu and swipe -- up/down may hide/show top menu - elseif ges_ev.direction == "north" then + elseif direction == "north" then -- no use for now do end -- luacheck: ignore 541 else -- diagonal swipe @@ -1257,6 +1281,42 @@ function Menu:onSwipe(arg, ges_ev) 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) local item_t = {} for k,v in pairs(t) do diff --git a/frontend/ui/widget/multiinputdialog.lua b/frontend/ui/widget/multiinputdialog.lua index f83ea761dc46..af3a1f775ed3 100644 --- a/frontend/ui/widget/multiinputdialog.lua +++ b/frontend/ui/widget/multiinputdialog.lua @@ -17,8 +17,6 @@ local Screen = Device.screen local input_field, input_description local MultiInputDialog = InputDialog:extend{ - field = {}, - field_hint = {}, fields = {}, description_padding = Size.padding.default, description_margin = Size.margin.small, @@ -50,6 +48,13 @@ function MultiInputDialog:init() parent = self, padding = field.padding or nil, margin = field.margin or nil, + -- Allow these to be specified per field if needed + alignment = field.alignment or self.alignment, + justified = field.justified or self.justified, + lang = field.lang or self.lang, + para_direction_rtl = field.para_direction_rtl or self.para_direction_rtl, + auto_para_direction = field.auto_para_direction or self.auto_para_direction, + alignment_strict = field.alignment_strict or self.alignment_strict, } if Device:hasDPad() then -- little hack to piggyback on the layout of the button_table to handle the new InputText diff --git a/frontend/ui/widget/overlapgroup.lua b/frontend/ui/widget/overlapgroup.lua index b5076693bdc3..ec94357d2ec4 100644 --- a/frontend/ui/widget/overlapgroup.lua +++ b/frontend/ui/widget/overlapgroup.lua @@ -2,9 +2,19 @@ A layout widget that puts objects above each other. --]] +local BD = require("ui/bidi") local WidgetContainer = require("ui/widget/container/widgetcontainer") local OverlapGroup = WidgetContainer:new{ + -- Note: we default to allow_mirroring = true. + -- When using LeftContainer, RightContainer or HorizontalGroup + -- in an OverlapGroup, either OverlapGroup, or all the others, + -- need to have allow_mirroring=false (otherwise, some upper + -- mirroring would cancel a lower one...). + -- It's usually safer to set it to false on the OverlapGroup, + -- but some thinking is needed when many of them are nested. + allow_mirroring = true, + _mirroredUI = BD.mirroredUILayout(), _size = nil, } @@ -45,10 +55,19 @@ function OverlapGroup:paintTo(bb, x, y) local size = self:getSize() for i, wget in ipairs(self) do + local overlap_align = wget.overlap_align + if self._mirroredUI and self.allow_mirroring then + if overlap_align == "right" then + overlap_align = "left" + elseif overlap_align ~= "center" then + overlap_align = "right" + end + -- see if something to do with wget.overlap_offset + end local wget_size = wget:getSize() - if wget.overlap_align == "right" then + if overlap_align == "right" then wget:paintTo(bb, x+size.w-wget_size.w, y) - elseif wget.overlap_align == "center" then + elseif overlap_align == "center" then wget:paintTo(bb, x+math.floor((size.w-wget_size.w)/2), y) elseif wget.overlap_offset then wget:paintTo(bb, x+wget.overlap_offset[1], y+wget.overlap_offset[2]) diff --git a/frontend/ui/widget/progresswidget.lua b/frontend/ui/widget/progresswidget.lua index 8d8aa007d341..d747f92ff80b 100644 --- a/frontend/ui/widget/progresswidget.lua +++ b/frontend/ui/widget/progresswidget.lua @@ -27,6 +27,7 @@ Example: ]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Geom = require("ui/geometry") local Widget = require("ui/widget/widget") @@ -46,6 +47,9 @@ local ProgressWidget = Widget:new{ ticks = nil, tick_width = Screen:scaleBySize(3), last = nil, + fill_from_right = false, + allow_mirroring = true, + _mirroredUI = BD.mirroredUILayout(), } function ProgressWidget:getSize() @@ -69,17 +73,30 @@ function ProgressWidget:paintTo(bb, x, y) self.bordersize, self.bordercolor, self.radius) -- paint percentage infill if self.percentage >= 0 and self.percentage <= 1 then - bb:paintRect(x+self.margin_h, math.ceil(y+self.margin_v+self.bordersize), - math.ceil((my_size.w-2*self.margin_h)*self.percentage), - my_size.h-2*(self.margin_v+self.bordersize), self.rectcolor) + if self.fill_from_right or (self._mirroredUI and not self.fill_from_right) then + bb:paintRect(x+self.margin_h + math.ceil((my_size.w-2*self.margin_h)*(1-self.percentage)), + math.ceil(y+self.margin_v+self.bordersize), + math.ceil((my_size.w-2*self.margin_h)*self.percentage), + my_size.h-2*(self.margin_v+self.bordersize), + self.rectcolor) + else + bb:paintRect(x+self.margin_h, + math.ceil(y+self.margin_v+self.bordersize), + math.ceil((my_size.w-2*self.margin_h)*self.percentage), + my_size.h-2*(self.margin_v+self.bordersize), self.rectcolor) + end end if self.ticks and self.last and self.last > 0 then local bar_width = (my_size.w-2*self.margin_h) local y_pos = y + self.margin_v + self.bordersize local bar_height = my_size.h-2*(self.margin_v+self.bordersize) for i=1, #self.ticks do + local tick_x = bar_width*(self.ticks[i]/self.last) + if self._mirroredUI then + tick_x = bar_width - tick_x + end bb:paintRect( - x + bar_width*(self.ticks[i]/self.last) + self.margin_h, + x + self.margin_h + tick_x, y_pos, self.tick_width, bar_height, @@ -92,6 +109,21 @@ function ProgressWidget:setPercentage(percentage) self.percentage = percentage end +function ProgressWidget:getPercentageFromPosition(pos) + if not pos or not pos.x then + return nil + end + local width = self.dimen.w - 2*self.margin_h + local x = pos.x - self.dimen.x - self.margin_h + if x < 0 or x > width then + return nil + end + if BD.mirroredUILayout() then + x = width - x + end + return x / width +end + function ProgressWidget:updateStyle(thick, height) if thick then if height then diff --git a/frontend/ui/widget/scrollhtmlwidget.lua b/frontend/ui/widget/scrollhtmlwidget.lua index 9588de5348a9..1cd2869f6c25 100644 --- a/frontend/ui/widget/scrollhtmlwidget.lua +++ b/frontend/ui/widget/scrollhtmlwidget.lua @@ -2,6 +2,7 @@ HTML widget with vertical scroll bar. --]] +local BD = require("ui/bidi") local Device = require("device") local HtmlBoxWidget = require("ui/widget/htmlboxwidget") local Geom = require("ui/geometry") @@ -143,7 +144,7 @@ function ScrollHtmlWidget:onScrollText(arg, ges) end 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 self:scrollText(-1) return true diff --git a/frontend/ui/widget/scrolltextwidget.lua b/frontend/ui/widget/scrolltextwidget.lua index c729ebd2b765..04cd7fdedc39 100644 --- a/frontend/ui/widget/scrolltextwidget.lua +++ b/frontend/ui/widget/scrolltextwidget.lua @@ -2,6 +2,7 @@ Text widget with vertical scroll bar. --]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Device = require("device") local Geom = require("ui/geometry") @@ -22,7 +23,6 @@ local ScrollTextWidget = InputContainer:new{ charpos = nil, top_line_num = nil, editable = false, - justified = false, scroll_callback = nil, -- called with (low, high) when view is scrolled scroll_by_pan = false, -- allow scrolling by lines with Pan face = nil, @@ -33,6 +33,13 @@ local ScrollTextWidget = InputContainer:new{ text_scroll_span = Screen:scaleBySize(12), dialog = nil, images = nil, + -- See TextBoxWidget for details about these options + alignment = "left", + justified = false, + lang = nil, + para_direction_rtl = nil, + auto_para_direction = false, + alignment_strict = false, } function ScrollTextWidget:init() @@ -43,13 +50,18 @@ function ScrollTextWidget:init() top_line_num = self.top_line_num, dialog = self.dialog, editable = self.editable, - justified = self.justified, face = self.face, image_alt_face = self.image_alt_face, fgcolor = self.fgcolor, width = self.width - self.scroll_bar_width - self.text_scroll_span, height = self.height, images = self.images, + alignment = self.alignment, + justified = self.justified, + lang = self.lang, + para_direction_rtl = self.para_direction_rtl, + auto_para_direction = self.auto_para_direction, + alignment_strict = self.alignment_strict, } local visible_line_count = self.text_widget:getVisLineCount() local total_line_count = self.text_widget:getAllLineCount() @@ -227,7 +239,7 @@ function ScrollTextWidget:onTapScrollText(arg, ges) return false end -- 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 self:scrollText(-1) return true diff --git a/frontend/ui/widget/sortwidget.lua b/frontend/ui/widget/sortwidget.lua index bf4e7315f2d6..1a3c36fad296 100644 --- a/frontend/ui/widget/sortwidget.lua +++ b/frontend/ui/widget/sortwidget.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") local Button = require("ui/widget/button") @@ -452,14 +453,15 @@ function SortWidget:onPrevPage() end function SortWidget:onSwipe(arg, ges_ev) - if ges_ev.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges_ev.direction) + if direction == "west" then self:onNextPage() - elseif ges_ev.direction == "east" then + elseif direction == "east" then self:onPrevPage() - elseif ges_ev.direction == "south" then + elseif direction == "south" then -- Allow easier closing with swipe down self:onClose() - elseif ges_ev.direction == "north" then + elseif direction == "north" then -- no use for now do end -- luacheck: ignore 541 else -- diagonal swipe diff --git a/frontend/ui/widget/textviewer.lua b/frontend/ui/widget/textviewer.lua index ab5462810a7c..b2984bf30408 100644 --- a/frontend/ui/widget/textviewer.lua +++ b/frontend/ui/widget/textviewer.lua @@ -8,6 +8,7 @@ Displays some text in a scrollable view. } UIManager:show(textviewer) ]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local ButtonTable = require("ui/widget/buttontable") local CenterContainer = require("ui/widget/container/centercontainer") @@ -37,7 +38,18 @@ local TextViewer = InputContainer:new{ width = nil, height = nil, buttons_table = nil, + -- See TextBoxWidget for details about these options + -- We default to justified and auto_para_direction to adapt + -- to any kind of text we are given (book descriptions, + -- bookmarks' text, translation results...). + -- When used to display more technical text (HTML, CSS, + -- application logs...), it's best to reset them to false. + alignment = "left", justified = true, + lang = nil, + para_direction_rtl = nil, + auto_para_direction = true, + alignment_strict = false, title_face = Font:getFace("x_smalltfont"), text_face = Font:getFace("x_smallinfofont"), @@ -157,7 +169,12 @@ function TextViewer:init() width = self.width - 2*self.text_padding - 2*self.text_margin, height = textw_height - 2*self.text_padding -2*self.text_margin, dialog = self, + alignment = self.alignment, justified = self.justified, + lang = self.lang, + para_direction_rtl = self.para_direction_rtl, + auto_para_direction = self.auto_para_direction, + alignment_strict = self.alignment_strict, } self.textw = FrameContainer:new{ padding = self.text_padding, @@ -240,10 +257,11 @@ end function TextViewer:onSwipe(arg, ges) if ges.pos:intersectWith(self.textw.dimen) then - if ges.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges.direction) + if direction == "west" then self.scroll_text_w:scrollText(1) return true - elseif ges.direction == "east" then + elseif direction == "east" then self.scroll_text_w:scrollText(-1) return true else diff --git a/frontend/ui/widget/toggleswitch.lua b/frontend/ui/widget/toggleswitch.lua index bbdedeefe9db..f5fb8534090b 100644 --- a/frontend/ui/widget/toggleswitch.lua +++ b/frontend/ui/widget/toggleswitch.lua @@ -5,6 +5,7 @@ Displays a button that toggles between states. Used in bottom configuration pane local ToggleSwitch = require("ui/widget/toggleswitch") ]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local CenterContainer = require("ui/widget/container/centercontainer") local Device = require("device") @@ -173,6 +174,9 @@ end function ToggleSwitch:calculatePosition(gev) 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 return math.max(1, math.ceil(x)) + math.min(self.row_count-1, math.floor(y)) * self.n_pos end diff --git a/frontend/ui/widget/touchmenu.lua b/frontend/ui/widget/touchmenu.lua index cc0a23bdf762..eab9e0dfc3e4 100644 --- a/frontend/ui/widget/touchmenu.lua +++ b/frontend/ui/widget/touchmenu.lua @@ -1,6 +1,7 @@ --[[-- TouchMenu widget for hierarchical menus. ]] +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local Button = require("ui/widget/button") local CenterContainer = require("ui/widget/container/centercontainer") @@ -26,9 +27,9 @@ local UIManager = require("ui/uimanager") local UnderlineContainer = require("ui/widget/container/underlinecontainer") local VerticalGroup = require("ui/widget/verticalgroup") local VerticalSpan = require("ui/widget/verticalspan") -local util = require("ffi/util") +local getMenuText = require("ui/widget/menu").getMenuText local _ = require("gettext") -local getMenuText = require("util").getMenuText +local T = require("ffi/util").template local Input = Device.input local Screen = Device.screen @@ -247,6 +248,11 @@ function TouchMenuBar:init() -- we have to use local variable here for closure callback local _start_seg = end_seg + icon_sep_width 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 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 -- to move over to the right by the width of a separator and the stretch width 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 = { { - 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" @@ -327,8 +339,6 @@ function TouchMenuBar:init() table.insert(self.icon_seps, icon_sep_duplicate) table.insert(self.bar_icon_group, icon_sep_duplicate) end - - end_seg = _end_seg end self[1] = FrameContainer:new{ @@ -429,14 +439,19 @@ function TouchMenu:init() align = "center", } -- 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{ - icon = "resources/icons/appbar.chevron.left.png", + icon = chevron_left, callback = function() self:onPrevPage() end, bordersize = 0, show_parent = self.show_parent, } self.page_info_right_chev = Button:new{ - icon = "resources/icons/appbar.chevron.right.png", + icon = chevron_right, callback = function() self:onNextPage() end, bordersize = 0, show_parent = self.show_parent, @@ -592,7 +607,7 @@ function TouchMenu:updateItems() table.insert(self.item_group, self.footer) 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". - 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 self.page_info_text:setText("") end @@ -609,35 +624,35 @@ function TouchMenu:updateItems() end local powerd = Device:getPowerDevice() local batt_lvl = powerd:getCapacity() - time_info_txt = time_info_txt .. " ⌁" + local batt_symbol if powerd:isCharging() then - time_info_txt = time_info_txt .. "" + batt_symbol = "" else if batt_lvl >= 100 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 90 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 80 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 70 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 60 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 50 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 40 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 30 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 20 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" elseif batt_lvl >= 10 then - time_info_txt = time_info_txt .. "" + batt_symbol = "" else - time_info_txt = time_info_txt .. "" + batt_symbol = "" 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) -- recalculate dimen based on new layout @@ -721,11 +736,12 @@ function TouchMenu:onPrevPage() end function TouchMenu:onSwipe(arg, ges_ev) - if ges_ev.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges_ev.direction) + if direction == "west" then self:onNextPage() - elseif ges_ev.direction == "east" then + elseif direction == "east" then self:onPrevPage() - elseif ges_ev.direction == "north" then + elseif direction == "north" then self:closeMenu() end end diff --git a/frontend/ui/widget/verticalgroup.lua b/frontend/ui/widget/verticalgroup.lua index db0c5f1d4bd2..d34419cc5fee 100644 --- a/frontend/ui/widget/verticalgroup.lua +++ b/frontend/ui/widget/verticalgroup.lua @@ -2,12 +2,15 @@ A layout widget that puts objects under each other. --]] +local BD = require("ui/bidi") local WidgetContainer = require("ui/widget/container/widgetcontainer") local VerticalGroup = WidgetContainer:new{ align = "center", + allow_mirroring = true, + _mirroredUI = BD.mirroredUILayout(), _size = nil, - _offsets = {} + _offsets = {}, } function VerticalGroup:getSize() @@ -31,15 +34,23 @@ end function VerticalGroup:paintTo(bb, x, y) local size = self:getSize() + local align = self.align + if self._mirroredUI and self.allow_mirroring then + if align == "left" then + align = "right" + elseif align == "right" then + align = "left" + end + end for i, widget in ipairs(self) do - if self.align == "center" then + if align == "center" then widget:paintTo(bb, x + math.floor((size.w - self._offsets[i].x) / 2), y + self._offsets[i].y) - elseif self.align == "left" then + elseif align == "left" then widget:paintTo(bb, x, y + self._offsets[i].y) - elseif self.align == "right" then + elseif align == "right" then widget:paintTo(bb, x + size.w - self._offsets[i].x, y + self._offsets[i].y) diff --git a/frontend/ui/widget/virtualkeyboard.lua b/frontend/ui/widget/virtualkeyboard.lua index 50a7d8e5414e..0188f0d20b22 100644 --- a/frontend/ui/widget/virtualkeyboard.lua +++ b/frontend/ui/widget/virtualkeyboard.lua @@ -163,6 +163,7 @@ function VirtualKey:init() background = Blitbuffer.COLOR_WHITE, radius = 0, padding = 0, + allow_mirroring = false, CenterContainer:new{ dimen = Geom:new{ 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 vertical_group = VerticalGroup:new{} - local horizontal_group_extra = HorizontalGroup:new{} - local horizontal_group_top = HorizontalGroup:new{} - local horizontal_group_middle = HorizontalGroup:new{} - local horizontal_group_bottom = HorizontalGroup:new{} + local vertical_group = VerticalGroup:new{ allow_mirroring = false } + local horizontal_group_extra = HorizontalGroup:new{ allow_mirroring = false } + local horizontal_group_top = HorizontalGroup:new{ allow_mirroring = false } + local horizontal_group_middle = HorizontalGroup:new{ allow_mirroring = false } + local horizontal_group_bottom = HorizontalGroup:new{ allow_mirroring = false } local function horizontalRow(chars, group) local layout_horizontal = {} @@ -565,6 +566,7 @@ function VirtualKeyPopup:init() background = Blitbuffer.COLOR_WHITE, radius = 0, padding = parent_key.keyboard.padding, + allow_mirroring = false, CenterContainer:new{ dimen = Geom:new{ 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 h_key_padding = HorizontalSpan: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 - local horizontal_group = HorizontalGroup:new{} + local horizontal_group = HorizontalGroup:new{ allow_mirroring = false } local layout_horizontal = {} for j = 1, #self.KEYS[i] do local key @@ -828,6 +830,7 @@ function VirtualKeyboard:addKeys() background = Blitbuffer.COLOR_WHITE, radius = 0, padding = self.padding, + allow_mirroring = false, CenterContainer:new{ dimen = Geom:new{ w = self.width - 2*Size.border.default - 2*self.padding, diff --git a/frontend/ui/wikipedia.lua b/frontend/ui/wikipedia.lua index 81347638524a..58e7c57f68f4 100644 --- a/frontend/ui/wikipedia.lua +++ b/frontend/ui/wikipedia.lua @@ -681,6 +681,13 @@ local rtl_wiki_code = { ks = "Kashmiri", } +function Wikipedia:isWikipediaLanguageRTL(lang) + if lang and rtl_wiki_code[lang:lower()] then + return true + end + return false +end + -- Create an epub file (with possibly images) function Wikipedia:createEpub(epub_path, page, lang, with_images) -- Use Trapper to display progress and ask questions through the UI. @@ -1298,7 +1305,7 @@ table { local see_online_version = T(_("See %1 for up-to-date content"), online_version_htmllink) -- Set dir= attribute on the HTML tag for RTL languages local html_dir = "" - if rtl_wiki_code[lang:lower()] then + if self:isWikipediaLanguageRTL(lang) then html_dir = ' dir="rtl"' end epub:add("OEBPS/content.html", string.format([[ diff --git a/frontend/util.lua b/frontend/util.lua index 03529bdf113f..17c874679286 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -280,6 +280,17 @@ function util.arrayAppend(t1, t2) end end +-- Reverse array elements in-place in table t +---- @param t Lua table +function util.arrayReverse(t) + local i, j = 1, #t + while i < j do + t[i], t[j] = t[j], t[i] + i = i + 1 + j = j - 1 + end +end + -- Merge t2 into t1, overwriting existing elements if they already exist -- Probably not safe with nested tables (c.f., https://stackoverflow.com/q/1283388) ---- @param t1 Lua table @@ -683,20 +694,6 @@ function util.getFormattedSize(size) return s 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. diff --git a/plugins/coverbrowser.koplugin/listmenu.lua b/plugins/coverbrowser.koplugin/listmenu.lua index ff3ba2a5da4a..c4c3ece1ef86 100644 --- a/plugins/coverbrowser.koplugin/listmenu.lua +++ b/plugins/coverbrowser.koplugin/listmenu.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local CenterContainer = require("ui/widget/container/centercontainer") local Device = require("device") @@ -31,6 +32,7 @@ local _ = require("gettext") local N_ = _.ngettext local Screen = Device.screen local T = require("ffi/util").template +local getMenuText = require("ui/widget/menu").getMenuText local BookInfoManager = require("bookinfomanager") @@ -338,7 +340,7 @@ function ListMenuItem:update() local filename_without_suffix, filetype = util.splitFileNameSuffix(filename) local fileinfo_str = filetype if self.mandatory then - fileinfo_str = self.mandatory .. " " .. fileinfo_str + fileinfo_str = self.mandatory .. " " .. BD.wrap(fileinfo_str) end if bookinfo._no_provider then -- for unspported files: don't show extension on the right, @@ -451,7 +453,7 @@ function ListMenuItem:update() end corner_mark = ImageWidget:new{ file = "resources/icons/dogear.png", - rotation_angle = 270, + rotation_angle = BD.mirroredUILayout() and 180 or 270, width = corner_mark_size, height = corner_mark_size, } @@ -694,7 +696,12 @@ function ListMenuItem:paintTo(bb, x, y) if self.shortcut_icon then -- align it on bottom left corner of sub-widget 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 self.shortcut_icon:paintTo(bb, x+ix, y+iy) end @@ -702,7 +709,12 @@ function ListMenuItem:paintTo(bb, x, y) -- to which we paint over a dogear if needed if corner_mark and self.do_hint_opened and self.been_opened then -- 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 corner_mark:paintTo(bb, x+ix, y+iy) end @@ -715,10 +727,22 @@ function ListMenuItem:paintTo(bb, x, y) if self.do_cover_image and target[1][1][1] then -- it has an image, align it on image's framecontainer's right border 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 -- 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 @@ -883,7 +907,7 @@ function ListMenu:_updateItemsBuildUI() height = self.item_height, width = self.item_width, entry = entry, - text = util.getMenuText(entry), + text = getMenuText(entry), show_parent = self.show_parent, mandatory = entry.mandatory, dimen = self.item_dimen:new(), diff --git a/plugins/coverbrowser.koplugin/mosaicmenu.lua b/plugins/coverbrowser.koplugin/mosaicmenu.lua index 9de307c248f5..701f407c04de 100644 --- a/plugins/coverbrowser.koplugin/mosaicmenu.lua +++ b/plugins/coverbrowser.koplugin/mosaicmenu.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") local CenterContainer = require("ui/widget/container/centercontainer") @@ -28,7 +29,7 @@ local util = require("util") local _ = require("gettext") local Screen = Device.screen local T = require("ffi/util").template -local getMenuText = require("util").getMenuText +local getMenuText = require("ui/widget/menu").getMenuText local BookInfoManager = require("bookinfomanager") @@ -195,7 +196,7 @@ function FakeCover:init() end if filename then 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)), width = text_width, alignment = "center", @@ -607,7 +608,12 @@ function MosaicMenuItem:paintTo(bb, x, y) if self.shortcut_icon then -- align it on bottom left corner of widget 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 self.shortcut_icon:paintTo(bb, x+ix, y+iy) end @@ -616,7 +622,12 @@ function MosaicMenuItem:paintTo(bb, x, y) if corner_mark and self.do_hint_opened and self.been_opened then -- align it on bottom right corner of sub-widget 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 -- math.ceil() makes it looks better than math.floor() 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_h = math.ceil(target.dimen.h / 8) -- Paint it directly relative to target.dimen.x/y which has been computed at this point - local ix = target.dimen.w - 1 - local iy = 0 - bb:paintBorder(target.dimen.x+ix, target.dimen.y+iy, d_w, d_h, 1) - local x_overflow = target.dimen.x+ix+d_w - x - self.dimen.w - if x_overflow > 0 then + local ix + if BD.mirroredUILayout() then + ix = - d_w + 1 -- Set alternate dimen to be marked as dirty to include this description in refresh - self.refresh_dimen = self[1].dimen:copy() - self.refresh_dimen.w = self.refresh_dimen.w + x_overflow + local x_overflow_left = x - target.dimen.x+ix -- positive if 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 + local iy = 0 + bb:paintBorder(target.dimen.x+ix, target.dimen.y+iy, d_w, d_h, 1) end end @@ -732,7 +755,7 @@ function MosaicMenu:_recalculateDimen() end corner_mark = ImageWidget:new{ file = "resources/icons/dogear.png", - rotation_angle = 270, + rotation_angle = BD.mirroredUILayout() and 180 or 270, width = corner_mark_size, height = corner_mark_size, } diff --git a/plugins/docsettingtweak.koplugin/main.lua b/plugins/docsettingtweak.koplugin/main.lua index 7a6c27209210..d8f8ab53ff2d 100644 --- a/plugins/docsettingtweak.koplugin/main.lua +++ b/plugins/docsettingtweak.koplugin/main.lua @@ -51,6 +51,7 @@ function DocSettingTweak:editDirectoryDefaults() title = T(_("Directory Defaults: %1"),directory_defaults_path), input = defaults, input_type = "string", + para_direction_rtl = false, -- force LTR fullscreen = true, condensed = true, allow_newline = true, diff --git a/plugins/goodreads.koplugin/doublekeyvaluepage.lua b/plugins/goodreads.koplugin/doublekeyvaluepage.lua index bebe29ec68d1..fb59390e68e0 100644 --- a/plugins/goodreads.koplugin/doublekeyvaluepage.lua +++ b/plugins/goodreads.koplugin/doublekeyvaluepage.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local BottomContainer = require("ui/widget/container/bottomcontainer") local Button = require("ui/widget/button") @@ -239,15 +240,20 @@ function DoubleKeyValuePage:init() text_font_bold = false, } -- 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{ - icon = "resources/icons/appbar.chevron.left.png", + icon = chevron_left, callback = function() self:prevPage() end, bordersize = 0, show_parent = self, } self.page_info_right_chev = Button:new{ - icon = "resources/icons/appbar.chevron.right.png", - callback = function() self:_nextPage() end, + icon = chevron_right, + callback = function() self:nextPage() end, bordersize = 0, show_parent = self, } @@ -413,16 +419,17 @@ function DoubleKeyValuePage:onPrevPage() end function DoubleKeyValuePage:onSwipe(arg, ges_ev) - if ges_ev.direction == "west" then + local direction = BD.mirrorDirectionIfMirroredUILayout(ges_ev.direction) + if direction == "west" then self:_nextPage() return true - elseif ges_ev.direction == "east" then + elseif direction == "east" then self:prevPage() return true - elseif ges_ev.direction == "south" then + elseif direction == "south" then -- Allow easier closing with swipe down self:onClose() - elseif ges_ev.direction == "north" then + elseif direction == "north" then -- no use for now do end -- luacheck: ignore 541 else -- diagonal swipe diff --git a/plugins/newsdownloader.koplugin/main.lua b/plugins/newsdownloader.koplugin/main.lua index 32d4c799116c..40f6672d93e7 100644 --- a/plugins/newsdownloader.koplugin/main.lua +++ b/plugins/newsdownloader.koplugin/main.lua @@ -411,6 +411,7 @@ function NewsDownloader:changeFeedConfig() title = T(_("Config: %1"),feed_config_path), input = config, input_type = "string", + para_direction_rtl = false, -- force LTR fullscreen = true, condensed = true, allow_newline = true, diff --git a/plugins/terminal.koplugin/main.lua b/plugins/terminal.koplugin/main.lua index 62a3e4fe792c..59dc826545ce 100644 --- a/plugins/terminal.koplugin/main.lua +++ b/plugins/terminal.koplugin/main.lua @@ -23,6 +23,7 @@ function Terminal:start() self.input = InputDialog:new{ title = _("Enter a command and press \"Execute\""), input = self.command, + para_direction_rtl = false, -- force LTR text_height = Screen:getHeight() * 0.4, input_type = "string", buttons = {{{ diff --git a/plugins/texteditor.koplugin/main.lua b/plugins/texteditor.koplugin/main.lua index e7afdff7fee7..1349c90abe9b 100644 --- a/plugins/texteditor.koplugin/main.lua +++ b/plugins/texteditor.koplugin/main.lua @@ -1,3 +1,4 @@ +local BD = require("ui/bidi") local ConfirmBox = require("ui/widget/confirmbox") local DataStorage = require("datastorage") local Font = require("ui/font") @@ -54,6 +55,8 @@ function TextEditor:loadSettings() if self.settings:readSetting("monospace_font") then self.monospace_font = self.settings:readSetting("monospace_font") end + self.auto_para_direction = self.settings:readSetting("auto_para_direction") or true + self.force_ltr_para_direction = self.settings:readSetting("force_ltr_para_direction") or false end function TextEditor:onFlushSettings() @@ -63,6 +66,8 @@ function TextEditor:onFlushSettings() self.settings:saveSetting("last_path", self.last_path) self.settings:saveSetting("font_face", self.font_face) self.settings:saveSetting("font_size", self.font_size) + self.settings:saveSetting("auto_para_direction", self.auto_para_direction) + self.settings:saveSetting("force_ltr_para_direction", self.force_ltr_para_direction) self.settings:flush() end end @@ -114,6 +119,32 @@ function TextEditor:getSubMenuItems() self.font_face = self.monospace_font end end, + separator = true, + }, + { + text = _("Auto paragraph direction"), + help_text = _([[ +Detect the direction of each paragraph in the text: align to the right paragraphs in languages such as Arabic and Hebrew…, while keeping other paragraphs aligned to the left. +If disabled, paragraphs align according to KOReader's language default direction.]]), + checked_func = function() + return self.auto_para_direction + end, + callback = function() + self.auto_para_direction = not self.auto_para_direction + end, + }, + { + text = _("Force paragraph direction LTR"), + help_text = _([[ +Force all text to be displayed Left-To-Right (LTR) and aligned to the left. +Enable this if you are mostly editing code, HTML, CSS…]]), + enabled_func = BD.rtlUIText, -- only useful for RTL users editing code + checked_func = function() + return BD.rtlUIText() and self.force_ltr_para_direction + end, + callback = function() + self.force_ltr_para_direction = not self.force_ltr_para_direction + end, }, }, separator = true, @@ -405,10 +436,16 @@ function TextEditor:editFile(file_path, readonly) local filename_without_suffix, filetype = util.splitFileNameSuffix(filename) -- luacheck: no unused local is_lua = filetype:lower() == "lua" local input + local para_direction_rtl = nil -- use UI language direction + if self.force_ltr_para_direction then + para_direction_rtl = false -- force LTR + end input = InputDialog:new{ title = filename, input = self:readFileContent(file_path), input_face = Font:getFace(self.font_face, self.font_size), + para_direction_rtl = para_direction_rtl, + auto_para_direction = self.auto_para_direction, fullscreen = true, condensed = true, allow_newline = true,