mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
* Switching between HTML/text dicts (Fix #7209) * Updating the scrollbar and scroll state properly when switching dicts * Highlights in SortWidget * Highlights in the Dictionary Download page * Minor simplification of the tail end of the update process in ImageViewer
This commit is contained in:
@@ -234,7 +234,7 @@ function DictQuickLookup:init()
|
||||
-- below the title: lookup word and definition
|
||||
local content_padding_h = Size.padding.large
|
||||
local content_padding_v = Size.padding.large -- added via VerticalSpan
|
||||
local content_width = inner_width - 2*content_padding_h
|
||||
self.content_width = inner_width - 2*content_padding_h
|
||||
|
||||
-- Spans between components
|
||||
local top_to_word_span = VerticalSpan:new{ width = content_padding_v }
|
||||
@@ -305,13 +305,13 @@ function DictQuickLookup:init()
|
||||
text = self.displayword,
|
||||
face = Font:getFace(word_font_face, word_font_size),
|
||||
bold = true,
|
||||
max_width = content_width - math.max(lookup_edit_button_w, lookup_word_nb_w),
|
||||
max_width = self.content_width - math.max(lookup_edit_button_w, lookup_word_nb_w),
|
||||
padding = 0, -- to be aligned with lookup_word_nb
|
||||
}
|
||||
-- Group these 3 widgets
|
||||
local lookup_word = OverlapGroup:new{
|
||||
dimen = {
|
||||
w = content_width,
|
||||
w = self.content_width,
|
||||
h = lookup_height,
|
||||
},
|
||||
self.lookup_word_text,
|
||||
@@ -570,7 +570,7 @@ function DictQuickLookup:init()
|
||||
local test_widget = ScrollTextWidget:new{
|
||||
text = "z",
|
||||
face = self.content_face,
|
||||
width = content_width,
|
||||
width = self.content_width,
|
||||
height = self.definition_height,
|
||||
}
|
||||
self.definition_line_height = test_widget:getLineHeight()
|
||||
@@ -623,33 +623,8 @@ function DictQuickLookup:init()
|
||||
end
|
||||
end
|
||||
|
||||
if self.is_html then
|
||||
self.text_widget = ScrollHtmlWidget:new{
|
||||
html_body = self.definition,
|
||||
css = self:getHtmlDictionaryCss(),
|
||||
default_font_size = Screen:scaleBySize(self.dict_font_size),
|
||||
width = content_width,
|
||||
height = self.definition_height,
|
||||
dialog = self,
|
||||
html_link_tapped_callback = function(link)
|
||||
self.html_dictionary_link_tapped_callback(self.dictionary, link)
|
||||
end,
|
||||
}
|
||||
else
|
||||
self.text_widget = ScrollTextWidget:new{
|
||||
text = self.definition,
|
||||
face = self.content_face,
|
||||
width = content_width,
|
||||
height = self.definition_height,
|
||||
dialog = self,
|
||||
justified = G_reader_settings:nilOrTrue("dict_justify"), -- allow for disabling justification
|
||||
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,
|
||||
}
|
||||
end
|
||||
-- Instantiate self.text_widget
|
||||
self:_instantiateScrollWidget()
|
||||
|
||||
-- word definition
|
||||
self.definition_widget = FrameContainer:new{
|
||||
@@ -768,6 +743,39 @@ function DictQuickLookup:getHtmlDictionaryCss()
|
||||
return css
|
||||
end
|
||||
|
||||
-- Used in init & update to instantiate the Scroll*Widget that self.text_widget points to
|
||||
function DictQuickLookup:_instantiateScrollWidget()
|
||||
if self.is_html then
|
||||
self.shw_widget = ScrollHtmlWidget:new{
|
||||
html_body = self.definition,
|
||||
css = self:getHtmlDictionaryCss(),
|
||||
default_font_size = Screen:scaleBySize(self.dict_font_size),
|
||||
width = self.content_width,
|
||||
height = self.definition_height,
|
||||
dialog = self,
|
||||
html_link_tapped_callback = function(link)
|
||||
self.html_dictionary_link_tapped_callback(self.dictionary, link)
|
||||
end,
|
||||
}
|
||||
self.text_widget = self.shw_widget
|
||||
else
|
||||
self.stw_widget = ScrollTextWidget:new{
|
||||
text = self.definition,
|
||||
face = self.content_face,
|
||||
width = self.content_width,
|
||||
height = self.definition_height,
|
||||
dialog = self,
|
||||
justified = G_reader_settings:nilOrTrue("dict_justify"), -- allow for disabling justification
|
||||
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,
|
||||
}
|
||||
self.text_widget = self.stw_widget
|
||||
end
|
||||
end
|
||||
|
||||
function DictQuickLookup:update()
|
||||
-- self[1] is a WidgetContainer, its free method will call free on each of its child widget with a free method.
|
||||
-- Here, that's the definitions' TextBoxWidget & HtmlBoxWidget,
|
||||
@@ -794,17 +802,35 @@ function DictQuickLookup:update()
|
||||
end
|
||||
|
||||
-- Update main text widgets
|
||||
if self.is_html then
|
||||
if self.is_html and self.shw_widget then
|
||||
-- Re-use our ScrollHtmlWidget (self.shw_widget)
|
||||
-- NOTE: The recursive free via our WidgetContainer (self[1]) above already released the previous MµPDF document instance ;)
|
||||
self.text_widget.htmlbox_widget:setContent(self.definition, self:getHtmlDictionaryCss(), Screen:scaleBySize(self.dict_font_size))
|
||||
else
|
||||
-- Scroll back to top
|
||||
self.text_widget:resetScroll()
|
||||
elseif not self.is_html and self.stw_widget then
|
||||
-- Re-use our ScrollTextWidget (self.stw_widget)
|
||||
self.text_widget.text_widget.text = self.definition
|
||||
-- NOTE: The recursive free via our WidgetContainer (self[1]) above already free'd us ;)
|
||||
self.text_widget.text_widget:init()
|
||||
-- Scroll back to top
|
||||
self.text_widget:resetScroll()
|
||||
else
|
||||
-- We jumped from HTML to Text (or vice-versa), we need a new widget instance
|
||||
self:_instantiateScrollWidget()
|
||||
-- Update *all* the references to self.text_widget
|
||||
self.definition_widget[1] = self.text_widget
|
||||
-- Destroy the previous "opposite type" widget
|
||||
if self.is_html then
|
||||
self.stw_widget = nil
|
||||
else
|
||||
self.shw_widget = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset alpha to avoid stacking transparency on top of the previous content.
|
||||
-- NOTE: This doesn't take care of the Scroll*Widget, which will preserve alpha on scroll,
|
||||
-- leading to increasingly opaque and muddy text as half-tarnsparent stuff gets stacked on top of each other...
|
||||
-- leading to increasingly opaque and muddy text as half-transparent stuff gets stacked on top of each other...
|
||||
self.movable.alpha = nil
|
||||
|
||||
UIManager:setDirty(self, function()
|
||||
|
||||
@@ -347,48 +347,8 @@ function ImageViewer:init()
|
||||
self.img_container_h = self.img_container_h - self.progress_container:getSize().h
|
||||
end
|
||||
|
||||
-- If no buttons and no title are shown, use the full screen
|
||||
local max_image_h = self.img_container_h
|
||||
local max_image_w = self.width
|
||||
-- Otherwise, add paddings around image
|
||||
if self.buttons_visible or self.with_title_bar then
|
||||
max_image_h = self.img_container_h - self.image_padding*2
|
||||
max_image_w = self.width - self.image_padding*2
|
||||
end
|
||||
|
||||
local rotation_angle = 0
|
||||
if self.rotated then
|
||||
-- in portrait mode, rotate according to this global setting so we are
|
||||
-- like in landscape mode
|
||||
local rotate_clockwise = DLANDSCAPE_CLOCKWISE_ROTATION
|
||||
if Screen:getWidth() > Screen:getHeight() then
|
||||
-- in landscape mode, counter-rotate landscape rotation so we are
|
||||
-- back like in portrait mode
|
||||
rotate_clockwise = not rotate_clockwise
|
||||
end
|
||||
rotation_angle = rotate_clockwise and 90 or 270
|
||||
end
|
||||
|
||||
self._image_wg = ImageWidget:new{
|
||||
file = self.file,
|
||||
image = self.image,
|
||||
image_disposable = false, -- we may re-use self.image
|
||||
alpha = true, -- we might be showing images with an alpha channel (e.g., from Wikipedia)
|
||||
width = max_image_w,
|
||||
height = max_image_h,
|
||||
rotation_angle = rotation_angle,
|
||||
scale_factor = self.scale_factor,
|
||||
center_x_ratio = self._center_x_ratio,
|
||||
center_y_ratio = self._center_y_ratio,
|
||||
}
|
||||
|
||||
self.image_container = CenterContainer:new{
|
||||
dimen = Geom:new{
|
||||
w = self.width,
|
||||
h = self.img_container_h,
|
||||
},
|
||||
self._image_wg,
|
||||
}
|
||||
-- Instantiate self._image_wg & self.image_container
|
||||
self:_new_image_wg()
|
||||
|
||||
local frame_elements = VerticalGroup:new{ align = "left" }
|
||||
if self.with_title_bar then
|
||||
@@ -440,6 +400,53 @@ function ImageViewer:_clean_image_wg()
|
||||
end
|
||||
end
|
||||
|
||||
-- Used in init & update to instantiate a new ImageWidget & its container
|
||||
function ImageViewer:_new_image_wg()
|
||||
-- If no buttons and no title are shown, use the full screen
|
||||
local max_image_h = self.img_container_h
|
||||
local max_image_w = self.width
|
||||
-- Otherwise, add paddings around image
|
||||
if self.buttons_visible or self.with_title_bar then
|
||||
max_image_h = self.img_container_h - self.image_padding*2
|
||||
max_image_w = self.width - self.image_padding*2
|
||||
end
|
||||
|
||||
local rotation_angle = 0
|
||||
if self.rotated then
|
||||
-- in portrait mode, rotate according to this global setting so we are
|
||||
-- like in landscape mode
|
||||
-- NOTE: This is the sole user of this legacy global left!
|
||||
local rotate_clockwise = DLANDSCAPE_CLOCKWISE_ROTATION
|
||||
if Screen:getWidth() > Screen:getHeight() then
|
||||
-- in landscape mode, counter-rotate landscape rotation so we are
|
||||
-- back like in portrait mode
|
||||
rotate_clockwise = not rotate_clockwise
|
||||
end
|
||||
rotation_angle = rotate_clockwise and 90 or 270
|
||||
end
|
||||
|
||||
self._image_wg = ImageWidget:new{
|
||||
file = self.file,
|
||||
image = self.image,
|
||||
image_disposable = false, -- we may re-use self.image
|
||||
alpha = true, -- we might be showing images with an alpha channel (e.g., from Wikipedia)
|
||||
width = max_image_w,
|
||||
height = max_image_h,
|
||||
rotation_angle = rotation_angle,
|
||||
scale_factor = self.scale_factor,
|
||||
center_x_ratio = self._center_x_ratio,
|
||||
center_y_ratio = self._center_y_ratio,
|
||||
}
|
||||
|
||||
self.image_container = CenterContainer:new{
|
||||
dimen = Geom:new{
|
||||
w = self.width,
|
||||
h = self.img_container_h,
|
||||
},
|
||||
self._image_wg,
|
||||
}
|
||||
end
|
||||
|
||||
function ImageViewer:update()
|
||||
-- Free our ImageWidget, which is the only thing we'll replace (e.g., leave the TextBoxWidgets alone).
|
||||
self:_clean_image_wg()
|
||||
@@ -503,48 +510,7 @@ function ImageViewer:update()
|
||||
end
|
||||
|
||||
-- Update the image widget itself
|
||||
-- If no buttons and no title are shown, use the full screen
|
||||
local max_image_h = self.img_container_h
|
||||
local max_image_w = self.width
|
||||
-- Otherwise, add paddings around image
|
||||
if self.buttons_visible or self.with_title_bar then
|
||||
max_image_h = self.img_container_h - self.image_padding*2
|
||||
max_image_w = self.width - self.image_padding*2
|
||||
end
|
||||
|
||||
local rotation_angle = 0
|
||||
if self.rotated then
|
||||
-- in portrait mode, rotate according to this global setting so we are
|
||||
-- like in landscape mode
|
||||
local rotate_clockwise = DLANDSCAPE_CLOCKWISE_ROTATION
|
||||
if Screen:getWidth() > Screen:getHeight() then
|
||||
-- in landscape mode, counter-rotate landscape rotation so we are
|
||||
-- back like in portrait mode
|
||||
rotate_clockwise = not rotate_clockwise
|
||||
end
|
||||
rotation_angle = rotate_clockwise and 90 or 270
|
||||
end
|
||||
|
||||
self._image_wg = ImageWidget:new{
|
||||
file = self.file,
|
||||
image = self.image,
|
||||
image_disposable = false, -- we may re-use self.image
|
||||
alpha = true, -- we might be showing images with an alpha channel (e.g., from Wikipedia)
|
||||
width = max_image_w,
|
||||
height = max_image_h,
|
||||
rotation_angle = rotation_angle,
|
||||
scale_factor = self.scale_factor,
|
||||
center_x_ratio = self._center_x_ratio,
|
||||
center_y_ratio = self._center_y_ratio,
|
||||
}
|
||||
|
||||
self.image_container = CenterContainer:new{
|
||||
dimen = Geom:new{
|
||||
w = self.width,
|
||||
h = self.img_container_h,
|
||||
},
|
||||
self._image_wg,
|
||||
}
|
||||
self:_new_image_wg()
|
||||
|
||||
-- Update the final layout
|
||||
local frame_elements = VerticalGroup:new{ align = "left" }
|
||||
@@ -560,22 +526,8 @@ function ImageViewer:update()
|
||||
table.insert(frame_elements, self.button_container)
|
||||
end
|
||||
|
||||
self.main_frame = FrameContainer:new{
|
||||
radius = not self.fullscreen and 8 or nil,
|
||||
padding = 0,
|
||||
margin = 0,
|
||||
background = Blitbuffer.COLOR_WHITE,
|
||||
frame_elements,
|
||||
}
|
||||
self[1] = WidgetContainer:new{
|
||||
align = self.align,
|
||||
dimen = self.region,
|
||||
FrameContainer:new{
|
||||
bordersize = 0,
|
||||
padding = Size.padding.default,
|
||||
self.main_frame,
|
||||
}
|
||||
}
|
||||
self.main_frame.radius = not self.fullscreen and 8 or nil
|
||||
self.main_frame[1] = frame_elements
|
||||
|
||||
self.dithered = true
|
||||
UIManager:setDirty(self, function()
|
||||
|
||||
@@ -296,12 +296,22 @@ function KeyValueItem:onTap()
|
||||
-- Has to be scheduled *after* the dict delays for the lookup history pages...
|
||||
UIManager:scheduleIn(0.75, function()
|
||||
self[1].invert = false
|
||||
-- Skip the repaint if we've ended up below something, which is likely.
|
||||
if UIManager:getTopWidget() ~= self.show_parent then
|
||||
-- If we've ended up below something, things get trickier.
|
||||
local top_widget = UIManager:getTopWidget()
|
||||
if top_widget ~= self.show_parent then
|
||||
-- It's generally tricky to get accurate dimensions out of whatever was painted above us,
|
||||
-- so cheat by comparing against the previous refresh region...
|
||||
if self[1].dimen:intersectWith(UIManager:getPreviousRefreshRegion()) then
|
||||
-- If that something is a modal (e.g., dictionary D/L), repaint the whole stack
|
||||
if top_widget.modal then
|
||||
UIManager:setDirty(self.show_parent, function()
|
||||
return "ui", self[1].dimen
|
||||
end)
|
||||
return true
|
||||
else
|
||||
-- Otherwise, skip the repaint
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
UIManager:widgetInvert(self[1], self[1].dimen.x, self[1].dimen.y)
|
||||
|
||||
@@ -50,7 +50,7 @@ function ScrollHtmlWidget:init()
|
||||
end
|
||||
}
|
||||
|
||||
self.v_scroll_bar:set((self.htmlbox_widget.page_number-1) / self.htmlbox_widget.page_count, self.htmlbox_widget.page_number / self.htmlbox_widget.page_count)
|
||||
self:_updateScrollBar()
|
||||
|
||||
local horizontal_group = HorizontalGroup:new{}
|
||||
table.insert(horizontal_group, self.htmlbox_widget)
|
||||
@@ -85,10 +85,25 @@ function ScrollHtmlWidget:init()
|
||||
end
|
||||
end
|
||||
|
||||
-- Not to be confused with ScrollTextWidget's updateScrollBar, which has user-visible effects.
|
||||
-- This simply updates the scroll bar's internal state according to the current page & page count.
|
||||
function ScrollHtmlWidget:_updateScrollBar()
|
||||
self.v_scroll_bar:set((self.htmlbox_widget.page_number-1) / self.htmlbox_widget.page_count, self.htmlbox_widget.page_number / self.htmlbox_widget.page_count)
|
||||
end
|
||||
|
||||
function ScrollHtmlWidget:getSinglePageHeight()
|
||||
return self.htmlbox_widget:getSinglePageHeight()
|
||||
end
|
||||
|
||||
-- Reset the scrolling *state* to the top of the document, but don't actually re-render/refresh anything.
|
||||
-- (Useful when replacing a Scroll*Widget during an update call, c.f., DictQuickLookup).
|
||||
function ScrollHtmlWidget:resetScroll()
|
||||
self.htmlbox_widget.page_number = 1
|
||||
self:_updateScrollBar()
|
||||
|
||||
self.v_scroll_bar.enable = self.htmlbox_widget.page_count > 1
|
||||
end
|
||||
|
||||
function ScrollHtmlWidget:scrollToRatio(ratio)
|
||||
ratio = math.max(0, math.min(1, ratio)) -- ensure ratio is between 0 and 1 (100%)
|
||||
local page_num = 1 + math.floor((self.htmlbox_widget.page_count) * ratio)
|
||||
@@ -99,9 +114,11 @@ function ScrollHtmlWidget:scrollToRatio(ratio)
|
||||
return
|
||||
end
|
||||
self.htmlbox_widget.page_number = page_num
|
||||
self.v_scroll_bar:set((page_num-1) / self.htmlbox_widget.page_count, page_num / self.htmlbox_widget.page_count)
|
||||
self:_updateScrollBar()
|
||||
|
||||
self.htmlbox_widget:freeBb()
|
||||
self.htmlbox_widget:_render()
|
||||
|
||||
UIManager:setDirty(self.dialog, function()
|
||||
return "partial", self.dimen
|
||||
end)
|
||||
@@ -125,8 +142,7 @@ function ScrollHtmlWidget:scrollText(direction)
|
||||
|
||||
self.htmlbox_widget.page_number = self.htmlbox_widget.page_number - 1
|
||||
end
|
||||
|
||||
self.v_scroll_bar:set((self.htmlbox_widget.page_number-1) / self.htmlbox_widget.page_count, self.htmlbox_widget.page_number / self.htmlbox_widget.page_count)
|
||||
self:_updateScrollBar()
|
||||
|
||||
self.htmlbox_widget:freeBb()
|
||||
self.htmlbox_widget:_render()
|
||||
|
||||
@@ -159,6 +159,17 @@ function ScrollTextWidget:updateScrollBar(is_partial)
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset the scrolling *state* to the top of the document, but don't actually re-render/refresh anything.
|
||||
-- (Useful when replacing a Scroll*Widget during an update call, c.f., DictQuickLookup).
|
||||
function ScrollTextWidget:resetScroll()
|
||||
local low, high = self.text_widget:getVisibleHeightRatios()
|
||||
self.v_scroll_bar:set(low, high)
|
||||
|
||||
local visible_line_count = self.text_widget:getVisLineCount()
|
||||
local total_line_count = self.text_widget:getAllLineCount()
|
||||
self.v_scroll_bar.enable = visible_line_count < total_line_count
|
||||
end
|
||||
|
||||
function ScrollTextWidget:moveCursorToCharPos(charpos)
|
||||
self.text_widget:moveCursorToCharPos(charpos)
|
||||
self:updateScrollBar()
|
||||
|
||||
@@ -228,6 +228,7 @@ function SortWidget:init()
|
||||
bordersize = 0,
|
||||
padding = 0,
|
||||
radius = 0,
|
||||
show_parent = self,
|
||||
}
|
||||
self.footer_right = Button:new{
|
||||
text = footer_right_text,
|
||||
@@ -237,6 +238,7 @@ function SortWidget:init()
|
||||
bordersize = 0,
|
||||
padding = 0,
|
||||
radius = 0,
|
||||
show_parent = self,
|
||||
}
|
||||
self.footer_first_up = Button:new{
|
||||
text = footer_first_up_text,
|
||||
@@ -252,6 +254,7 @@ function SortWidget:init()
|
||||
bordersize = 0,
|
||||
padding = 0,
|
||||
radius = 0,
|
||||
show_parent = self,
|
||||
}
|
||||
self.footer_last_down = Button:new{
|
||||
text = footer_last_down_text,
|
||||
@@ -267,6 +270,7 @@ function SortWidget:init()
|
||||
bordersize = 0,
|
||||
padding = 0,
|
||||
radius = 0,
|
||||
show_parent = self,
|
||||
}
|
||||
self.footer_cancel = Button:new{
|
||||
text = "✘",
|
||||
@@ -276,6 +280,7 @@ function SortWidget:init()
|
||||
text_font_size = 28,
|
||||
padding = 0,
|
||||
radius = 0,
|
||||
show_parent = self,
|
||||
}
|
||||
|
||||
self.footer_ok = Button:new{
|
||||
@@ -286,6 +291,7 @@ function SortWidget:init()
|
||||
padding = 0,
|
||||
radius = 0,
|
||||
text_font_size = 28,
|
||||
show_parent = self,
|
||||
}
|
||||
|
||||
self.footer_page = Button:new{
|
||||
@@ -308,6 +314,7 @@ function SortWidget:init()
|
||||
text_font_face = "pgfont",
|
||||
text_font_bold = false,
|
||||
width = self.width_widget * 22 / 100,
|
||||
show_parent = self,
|
||||
}
|
||||
local button_vertical_line = LineWidget:new{
|
||||
dimen = Geom:new{ w = Size.line.thick, h = math.floor(self.item_height * 1.25) },
|
||||
|
||||
Reference in New Issue
Block a user