Updated demo and fixed bug in spacing
Python CI / test (push) Successful in 4m10s

This commit is contained in:
2025-11-08 18:51:33 +01:00
parent 284a6e3393
commit 1993b0bf48
7 changed files with 1161 additions and 183 deletions
+183 -62
View File
@@ -245,15 +245,27 @@ class EbookReader:
"""
Get the current page as a PIL Image.
If an overlay is currently open, returns the composited overlay image.
Otherwise returns the base reading page.
Args:
include_highlights: Whether to overlay highlights on the page
include_highlights: Whether to overlay highlights on the page (only applies to base page)
Returns:
PIL Image of the current page, or None if no book is loaded
PIL Image of the current page (or overlay), or None if no book is loaded
"""
if not self.manager:
return None
# If an overlay is open, return the cached composited overlay image
if self.is_overlay_open() and self.overlay_manager._cached_base_page:
# Return the last composited overlay image
# The overlay manager keeps this updated when settings change
return self.overlay_manager.composite_overlay(
self.overlay_manager._cached_base_page,
self.overlay_manager._cached_overlay_image
)
try:
page = self.manager.get_current_page()
img = page.render()
@@ -503,42 +515,30 @@ class EbookReader:
def set_line_spacing(self, spacing: int) -> Optional[Image.Image]:
"""
Set line spacing and re-render current page.
Set line spacing using pyWebLayout's native support.
Args:
spacing: Line spacing in pixels
Returns:
PIL Image of the re-rendered page
"""
if not self.manager:
return None
try:
# Update page style
self.page_style.line_spacing = max(0, spacing)
# Need to recreate the manager with new page style
current_pos = self.manager.current_position
current_font_scale = self.base_font_scale
self.manager.shutdown()
self.manager = EreaderLayoutManager(
blocks=self.blocks,
page_size=self.page_size,
document_id=self.document_id,
buffer_size=self.buffer_size,
page_style=self.page_style,
bookmarks_dir=self.bookmarks_dir
)
# Restore position
self.manager.current_position = current_pos
# Restore font scale using the method (not direct assignment)
if current_font_scale != 1.0:
self.manager.set_font_scale(current_font_scale)
# Calculate delta from current spacing
current_spacing = self.manager.page_style.line_spacing
target_spacing = max(0, spacing)
delta = target_spacing - current_spacing
# Use pyWebLayout's built-in methods to adjust spacing
if delta > 0:
self.manager.increase_line_spacing(abs(delta))
elif delta < 0:
self.manager.decrease_line_spacing(abs(delta))
# Get re-rendered page
page = self.manager.get_current_page()
return page.render()
except Exception as e:
@@ -547,48 +547,68 @@ class EbookReader:
def set_inter_block_spacing(self, spacing: int) -> Optional[Image.Image]:
"""
Set spacing between blocks (paragraphs, headings, etc.) and re-render.
Set inter-block spacing using pyWebLayout's native support.
Args:
spacing: Inter-block spacing in pixels
Returns:
PIL Image of the re-rendered page
"""
if not self.manager:
return None
try:
# Update page style
self.page_style.inter_block_spacing = max(0, spacing)
# Need to recreate the manager with new page style
current_pos = self.manager.current_position
current_font_scale = self.base_font_scale
self.manager.shutdown()
self.manager = EreaderLayoutManager(
blocks=self.blocks,
page_size=self.page_size,
document_id=self.document_id,
buffer_size=self.buffer_size,
page_style=self.page_style,
bookmarks_dir=self.bookmarks_dir
)
# Restore position
self.manager.current_position = current_pos
# Restore font scale using the method (not direct assignment)
if current_font_scale != 1.0:
self.manager.set_font_scale(current_font_scale)
# Calculate delta from current spacing
current_spacing = self.manager.page_style.inter_block_spacing
target_spacing = max(0, spacing)
delta = target_spacing - current_spacing
# Use pyWebLayout's built-in methods to adjust spacing
if delta > 0:
self.manager.increase_inter_block_spacing(abs(delta))
elif delta < 0:
self.manager.decrease_inter_block_spacing(abs(delta))
# Get re-rendered page
page = self.manager.get_current_page()
return page.render()
except Exception as e:
print(f"Error setting inter-block spacing: {e}")
return None
def set_word_spacing(self, spacing: int) -> Optional[Image.Image]:
"""
Set word spacing using pyWebLayout's native support.
Args:
spacing: Word spacing in pixels
Returns:
PIL Image of the re-rendered page
"""
if not self.manager:
return None
try:
# Calculate delta from current spacing
current_spacing = self.manager.page_style.word_spacing
target_spacing = max(0, spacing)
delta = target_spacing - current_spacing
# Use pyWebLayout's built-in methods to adjust spacing
if delta > 0:
self.manager.increase_word_spacing(abs(delta))
elif delta < 0:
self.manager.decrease_word_spacing(abs(delta))
# Get re-rendered page
page = self.manager.get_current_page()
return page.render()
except Exception as e:
print(f"Error setting word spacing: {e}")
return None
def get_position_info(self) -> Dict[str, Any]:
"""
Get detailed information about the current position.
@@ -725,6 +745,9 @@ class EbookReader:
elif event.gesture == GestureType.SWIPE_UP:
# Swipe up from bottom opens TOC overlay
return self._handle_swipe_up(event.y)
elif event.gesture == GestureType.SWIPE_DOWN:
# Swipe down from top opens settings overlay
return self._handle_swipe_down(event.y)
elif event.gesture == GestureType.PINCH_IN:
return self._handle_zoom_out()
elif event.gesture == GestureType.PINCH_OUT:
@@ -915,8 +938,26 @@ class EbookReader:
return GestureResponse(ActionType.NONE, {})
def _handle_swipe_down(self, y: int) -> GestureResponse:
"""Handle swipe down gesture - opens settings overlay if from top of screen"""
# Check if swipe started from top 20% of screen
top_threshold = self.page_size[1] * 0.2
if y <= top_threshold:
# Open settings overlay
overlay_image = self.open_settings_overlay()
if overlay_image:
return GestureResponse(ActionType.OVERLAY_OPENED, {
"overlay_type": "settings",
"font_scale": self.base_font_scale,
"line_spacing": self.page_style.line_spacing,
"inter_block_spacing": self.page_style.inter_block_spacing
})
return GestureResponse(ActionType.NONE, {})
def _handle_overlay_tap(self, x: int, y: int) -> GestureResponse:
"""Handle tap when overlay is open - select chapter or close overlay"""
"""Handle tap when overlay is open - select chapter, adjust settings, or close overlay"""
# For TOC overlay, use pyWebLayout link query to detect chapter clicks
if self.current_overlay_state == OverlayState.TOC:
# Query the overlay to see what was tapped
@@ -961,6 +1002,74 @@ class EbookReader:
self.close_overlay()
return GestureResponse(ActionType.OVERLAY_CLOSED, {})
# For settings overlay, handle setting adjustments
elif self.current_overlay_state == OverlayState.SETTINGS:
# Query the overlay to see what was tapped
query_result = self.overlay_manager.query_overlay_pixel(x, y)
# If query failed (tap outside overlay), close it
if not query_result:
self.close_overlay()
return GestureResponse(ActionType.OVERLAY_CLOSED, {})
# Check if tapped on a settings control link
if query_result.get("is_interactive") and query_result.get("link_target"):
link_target = query_result["link_target"]
# Parse "setting:action" format
if link_target.startswith("setting:"):
action = link_target.split(":", 1)[1]
# Apply the setting change
if action == "font_increase":
self.increase_font_size()
elif action == "font_decrease":
self.decrease_font_size()
elif action == "line_spacing_increase":
new_spacing = self.page_style.line_spacing + 2
self.set_line_spacing(new_spacing)
elif action == "line_spacing_decrease":
new_spacing = max(0, self.page_style.line_spacing - 2)
self.set_line_spacing(new_spacing)
elif action == "block_spacing_increase":
new_spacing = self.page_style.inter_block_spacing + 3
self.set_inter_block_spacing(new_spacing)
elif action == "block_spacing_decrease":
new_spacing = max(0, self.page_style.inter_block_spacing - 3)
self.set_inter_block_spacing(new_spacing)
elif action == "word_spacing_increase":
new_spacing = self.page_style.word_spacing + 2
self.set_word_spacing(new_spacing)
elif action == "word_spacing_decrease":
new_spacing = max(0, self.page_style.word_spacing - 2)
self.set_word_spacing(new_spacing)
# Re-render the base page with new settings applied
# Must get directly from manager, not get_current_page() which returns overlay
page = self.manager.get_current_page()
updated_page = page.render()
# Refresh the settings overlay with updated values and page
self.overlay_manager.refresh_settings_overlay(
updated_base_page=updated_page,
font_scale=self.base_font_scale,
line_spacing=self.page_style.line_spacing,
inter_block_spacing=self.page_style.inter_block_spacing,
word_spacing=self.page_style.word_spacing
)
return GestureResponse(ActionType.SETTING_CHANGED, {
"action": action,
"font_scale": self.base_font_scale,
"line_spacing": self.page_style.line_spacing,
"inter_block_spacing": self.page_style.inter_block_spacing,
"word_spacing": self.page_style.word_spacing
})
# Not a setting control, close overlay
self.close_overlay()
return GestureResponse(ActionType.OVERLAY_CLOSED, {})
# For other overlays, just close on any tap for now
self.close_overlay()
return GestureResponse(ActionType.OVERLAY_CLOSED, {})
@@ -1208,7 +1317,7 @@ class EbookReader:
def open_settings_overlay(self) -> Optional[Image.Image]:
"""
Open the settings overlay.
Open the settings overlay with current settings values.
Returns:
Composited image with settings overlay on top of current page, or None if no book loaded
@@ -1221,8 +1330,20 @@ class EbookReader:
if not base_page:
return None
# Get current settings
font_scale = self.base_font_scale
line_spacing = self.page_style.line_spacing
inter_block_spacing = self.page_style.inter_block_spacing
word_spacing = self.page_style.word_spacing
# Open overlay and get composited image
result = self.overlay_manager.open_settings_overlay(base_page)
result = self.overlay_manager.open_settings_overlay(
base_page,
font_scale=font_scale,
line_spacing=line_spacing,
inter_block_spacing=inter_block_spacing,
word_spacing=word_spacing
)
self.current_overlay_state = OverlayState.SETTINGS
return result