Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pretty_gpx/common/drawing/utils/drawing_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ def __call__(self, paper_size: PaperSize) -> float:
scale = paper_size.diag_mm/PAPER_SIZES['A4'].diag_mm
return mm_to_point(self.__val_mm)*scale

def __mul__(self, other: float) -> 'A4Float':
"""Multiply the A4Float by a scalar."""
return A4Float(mm=self.__val_mm * other)

def __truediv__(self, other: float) -> 'A4Float':
"""Divide the A4Float by a scalar."""
return A4Float(mm=self.__val_mm / other)


class MetersFloat:
"""Scales a meter measurement to points based on paper size and GPX bounds."""
Expand Down
24 changes: 0 additions & 24 deletions pretty_gpx/common/drawing/utils/fonts.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#!/usr/bin/python3
"""Fonts."""
import os
import textwrap
from enum import Enum
from pathlib import Path

from matplotlib.font_manager import FontProperties

Expand All @@ -23,25 +21,3 @@ class CustomFont(Enum):
def font_name(self) -> str:
"""Get the font name."""
return self.value.get_name()

def get_css_header(self) -> str | None:
"""Get the CSS header for the font."""
font_path = self.value.get_file()
if font_path is None or not isinstance(font_path, str):
return None

font_path = Path(font_path).name
if font_path.lower().endswith('.otf'):
font_format = 'opentype'
elif font_path.lower().endswith('.ttf'):
font_format = 'truetype'
else:
raise ValueError("Unsupported font format. Please provide a .otf or .ttf file.")

header = f'''
@font-face {{
font-family: '{self.font_name}';
src: url('/fonts/{font_path}') format('{font_format}');
}}
'''
return textwrap.dedent(header)
4 changes: 2 additions & 2 deletions pretty_gpx/ui/pages/city/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ def update_drawer_params(self) -> None:
self.drawer.params.profile_fill_color = theme.track_color
self.drawer.params.profile_font_color = theme.background_color
self.drawer.params.centered_title_font_color = theme.point_color

self.drawer.params.centered_title_fontproperties = self.font.value.value
self.drawer.params.centered_title_fontproperties = self.font.font.value
self.drawer.params.centered_title_font_size = self.font._current_fontsize

for cat in [ScatterPointCategory.CITY_BRIDGE,
ScatterPointCategory.CITY_POI_DEFAULT,
Expand Down
2 changes: 2 additions & 0 deletions pretty_gpx/ui/pages/mountain/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def update_drawer_params(self) -> None:
self.drawer.params.profile_fill_color = theme.track_color
self.drawer.params.profile_font_color = theme.background_color
self.drawer.params.centered_title_font_color = theme.peak_color
self.drawer.params.centered_title_fontproperties = self.font.font.value
self.drawer.params.centered_title_font_size = self.font._current_fontsize

for cat in [ScatterPointCategory.MOUNTAIN_PASS,
ScatterPointCategory.START,
Expand Down
2 changes: 2 additions & 0 deletions pretty_gpx/ui/pages/multi_mountain/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ def update_drawer_params(self) -> None:
self.drawer.params.profile_fill_color = theme.track_color
self.drawer.params.profile_font_color = theme.background_color
self.drawer.params.centered_title_font_color = theme.peak_color
self.drawer.params.centered_title_fontproperties = self.font.font.value
self.drawer.params.centered_title_font_size = self.font._current_fontsize

for cat in [ScatterPointCategory.MOUNTAIN_HUT,
ScatterPointCategory.START,
Expand Down
64 changes: 64 additions & 0 deletions pretty_gpx/ui/pages/template/ui_font_and_size_select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/python3
"""Ui Fonts Menu, to select a font from a list and also select a font size."""
from collections.abc import Awaitable
from collections.abc import Callable

from nicegui import ui

from pretty_gpx.common.drawing.utils.drawing_figure import A4Float
from pretty_gpx.common.drawing.utils.fonts import CustomFont
from pretty_gpx.ui.pages.template.ui_font_select import UiFontSelect


class UiFontAndSizeSelect:
"""NiceGui menu to select a font in a list and also select a font size."""

def __init__(self, *,
label: str,
fonts: tuple[CustomFont, ...],
on_change: Callable[[], Awaitable[None]],
start_fontsize: A4Float,
start_font: CustomFont | None = None,
fontsize_geometric_step: float = 1.2) -> None:
"""Create a UiFontAndSizeSelect."""
with ui.row().classes('items-center gap-2'):
font_select = UiFontSelect(label=label,
fonts=fonts,
on_change=on_change,
start_font=start_font)

def on_click_minus() -> Callable[[], Awaitable[None]]:
"""On click minus handler."""
async def handler() -> None:
self._current_fontsize /= fontsize_geometric_step
await on_change()
return handler

def on_click_plus() -> Callable[[], Awaitable[None]]:
"""On click plus handler."""
async def handler() -> None:
self._current_fontsize *= fontsize_geometric_step
await on_change()
return handler

with ui.button(icon='remove', on_click=on_click_minus()
).props('dense round').classes('bg-white text-black border border-black'):
ui.tooltip('Decrease font size')
with ui.button(icon='add', on_click=on_click_plus()
).props('dense round').classes('bg-white text-black border border-black'):
ui.tooltip('Increase font size')

###

self._font_select = font_select
self._current_fontsize = start_fontsize

@property
def font(self) -> CustomFont:
"""Return the selected font."""
return self._font_select.font

@property
def fontsize(self) -> A4Float:
"""Return the selected fontsize."""
return self._current_fontsize
95 changes: 95 additions & 0 deletions pretty_gpx/ui/pages/template/ui_font_select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/python3
"""Ui Fonts Menu, to select a font from a list."""
import os
import textwrap
from collections.abc import Awaitable
from collections.abc import Callable

from nicegui import app
from nicegui import ui

from pretty_gpx.common.drawing.utils.fonts import CustomFont
from pretty_gpx.common.utils.paths import FONTS_DIR

app.add_static_files('/fonts', os.path.abspath(FONTS_DIR))


class UiFontSelect:
"""NiceGui menu to select a font in a list."""

def __init__(self,
*,
label: str,
fonts: tuple[CustomFont, ...],
on_change: Callable[[], Awaitable[None]],
start_font: CustomFont | None = None) -> None:
"""Create a UiFontsMenu."""
if start_font is None:
current_idx = 0
else:
current_idx = fonts.index(start_font) # Can raise ValueError if not found

def on_click_idx(idx: int) -> Callable[[], Awaitable[None]]:
"""On click handler."""
async def handler() -> None:
self.change_current_idx(idx)
await on_change()
return handler

with ui.dropdown_button(label, icon="font_download", auto_close=True) as main_button:
main_button.classes("bg-white text-black normal-case")

for font in fonts:
font_css_header = get_css_header(font)
if font_css_header is not None:
ui.add_css(font_css_header)

items = [ui.item(font.font_name, on_click=on_click_idx(idx))
.style(f'font-family:"{font.font_name}";')
for idx, font in enumerate(fonts)]

###

self.main_button = main_button
self.fonts = fonts
self.items = items
self.current_idx = current_idx

self.change_current_idx(current_idx)

def change_current_idx(self, new_idx: int) -> None:
"""Change the current index."""
if new_idx < 0 or new_idx >= len(self.fonts):
raise IndexError(f"Index {new_idx} out of bounds for fonts list of length {len(self.fonts)}.")
self.items[self.current_idx].classes(replace="bg-white text-black")
self.items[new_idx].classes(replace="bg-primary text-white")
self.current_idx = new_idx
self.main_button.style(f'font-family: "{self.font.font_name}";')

@property
def font(self) -> CustomFont:
"""Return the selected font."""
return self.fonts[self.current_idx]


def get_css_header(font: CustomFont) -> str | None:
"""Get the CSS header for the font."""
font_path = font.value.get_file()
if font_path is None or not isinstance(font_path, str):
return None

font_path = os.path.basename(font_path)
if font_path.lower().endswith('.otf'):
font_format = 'opentype'
elif font_path.lower().endswith('.ttf'):
font_format = 'truetype'
else:
raise ValueError("Unsupported font format. Please provide a .otf or .ttf file.")

header = f'''
@font-face {{
font-family: '{font.font_name}';
src: url('/fonts/{font_path}') format('{font_format}');
}}
'''
return textwrap.dedent(header)
66 changes: 0 additions & 66 deletions pretty_gpx/ui/pages/template/ui_fonts_menu.py

This file was deleted.

20 changes: 11 additions & 9 deletions pretty_gpx/ui/pages/template/ui_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
from pretty_gpx.common.drawing.utils.color_theme import LightTheme
from pretty_gpx.common.drawing.utils.drawer import DrawerMultiTrack
from pretty_gpx.common.drawing.utils.drawer import DrawerSingleTrack
from pretty_gpx.common.drawing.utils.drawing_figure import A4Float
from pretty_gpx.common.drawing.utils.fonts import CustomFont
from pretty_gpx.common.layout.paper_size import PAPER_SIZES
from pretty_gpx.common.layout.paper_size import PaperSize
from pretty_gpx.common.utils.logger import logger
from pretty_gpx.common.utils.profile import profile_parallel
from pretty_gpx.ui.pages.template.ui_fonts_menu import UiFontsMenu
from pretty_gpx.ui.pages.template.ui_font_and_size_select import UiFontAndSizeSelect
from pretty_gpx.ui.pages.template.ui_input import UiInputFloat
from pretty_gpx.ui.pages.template.ui_input import UiInputStr
from pretty_gpx.ui.pages.template.ui_plot import UiPlot
Expand Down Expand Up @@ -126,7 +127,7 @@ class UiManager(Generic[T], ABC):
paper_size: UiToggle[PaperSize]
title: UiInputStr
dist_km: UiInputFloat
font: UiFontsMenu
font: UiFontAndSizeSelect
dark_mode_switch: ui.switch
theme: UiToggle[DarkTheme] | UiToggle[LightTheme]

Expand Down Expand Up @@ -206,17 +207,18 @@ def __init__(self, drawer: T) -> None:
#

with self.subclass_column:
self.font = UiFontAndSizeSelect(label="Title's Font",
fonts=(CustomFont.LOBSTER,
CustomFont.MONOTON,
CustomFont.GOCHI_HAND,
CustomFont.EMILIO_20,
CustomFont.ALLERTA_STENCIL),
start_fontsize=A4Float(mm=20),
on_change=self.on_click_update)
self.title = UiInputStr.create(label='Title', value="Title", tooltip="Press Enter to update title",
on_enter=self.on_click_update)
self.dist_km = UiInputFloat.create(label='Distance (km)', value="", on_enter=self.on_click_update,
tooltip="Press Enter to override distance from GPX")
self.font = UiFontsMenu.create(fonts=(CustomFont.LOBSTER,
CustomFont.MONOTON,
CustomFont.GOCHI_HAND,
CustomFont.EMILIO_20,
CustomFont.ALLERTA_STENCIL),
on_change=self.on_click_update,
tooltip="Select the title's font")

#
# New fields will be added here by the subclass
Expand Down