Skip to content

Commit

Permalink
Better support for long text
Browse files Browse the repository at this point in the history
- Multi-line labels and scrolling
- Fixed 'quit' shortcut
- Improved README.md
  • Loading branch information
paddyesch committed Mar 7, 2023
1 parent f36747b commit a8fcba2
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 199 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ This is the GNOME application for Yuml Recipes. It opens *.yuml files by registe

## Roadmap

- Better support for long text with multi-line labels, max. width and vertical scrolling
- Support for *.yuml files packaged with their images as ZIP file keeping the *.yuml extension
- Export thumbnailer to give the zipped_recipe.yuml the thumbnail of its image, see [Flatpak thumbnailer feature request](https://github.com/flatpak/flatpak/issues/4923)

## Hints

Expand All @@ -45,16 +45,18 @@ This is the GNOME application for Yuml Recipes. It opens *.yuml files by registe

## Links

- [GTK 3 Python Reference](https://amolenaar.github.io/pgi-docgen/)
- [GTK 3 How Do I](https://wiki.gnome.org/HowDoI/Labels)
- [GTK 4 Widget Documentation](https://docs.gtk.org/gtk4/index.html)
- [GNOME Integration](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html?highlight=mime)
- [Flatpak PIP Generator](https://github.com/flatpak/flatpak-builder-tools/tree/master/pip)
- [Flatpak CI Docker Image](https://hub.docker.com/r/bilelmoussaoui/flatpak-github-actions)
- [GTK4 Widget Documentation](https://docs.gtk.org/gtk4/index.html)
- [GNOME Integration](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html?highlight=mime)

## License

GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007

GPL-3.0

Copyright (c) 2022 Patrick Eschenbach
Copyright (c) 2023 Patrick Eschenbach

6 changes: 5 additions & 1 deletion src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class YumlRecipesApplication(Adw.Application):
def __init__(self):
super().__init__(application_id='org.yumlrecipes.yumlrecipes',
flags=Gio.ApplicationFlags.HANDLES_OPEN)
self.create_action('quit', self.quit, ['<primary>q'])
self.create_action('quit', self.on_quit_action, ['<primary>q'])
self.create_action('about', self.on_about_action)
self.create_action('preferences', self.on_preferences_action)
self.init_css()
Expand Down Expand Up @@ -77,6 +77,7 @@ def open_recipe(self, win: YumlRecipesWindow, path: str):
win.show_ingredients(recipe.ingredients)
win.show_steps(recipe.steps)
win.show_variants(recipe.variants)
win.ensure_natural_height()

except yuml.YumlException as ex:
win.show_title(f"Couldn't load {path}: {str(ex)}")
Expand Down Expand Up @@ -106,6 +107,9 @@ def get_file_path(self, file: Gio.File) -> str:
path = path[len(schema):]
return path

def on_quit_action(self, action, param):
self.quit()

def on_about_action(self, widget, _):
"""Callback for the app.about action."""
about = Adw.AboutWindow(transient_for=self.props.active_window,
Expand Down
12 changes: 8 additions & 4 deletions src/window.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
}

.yuml-combo {
margin-left: 10px;
margin-left: 20px;
margin-right: 10px;
margin-top: 10px;
margin-bottom: 5px;
font-size: 15px;
}

Expand Down Expand Up @@ -46,8 +46,12 @@
color: white;
}

.yuml-list {
.yuml-grid {
margin: 10px;
font-size: 20px;
}

.yuml-right-entry {
margin-left: 20px;
margin-right: 20px;
}

147 changes: 75 additions & 72 deletions src/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,62 +23,62 @@
from typing import List


class ListModel(GObject.GObject):
def create_left_entry(text: str):
entry = Gtk.Label(label=text)
entry.set_halign(Gtk.Align.END)
entry.set_yalign(0.0)
entry.set_selectable(True)
return entry

def __init__(self, text: str, align: Gtk.Align):
super().__init__()
self.text = text
self.align = align

def create_right_entry(text: str):
entry = Gtk.Label(label=text)
entry.add_css_class('yuml-right-entry')
entry.set_justify(Gtk.Justification.FILL)
entry.set_halign(Gtk.Align.FILL)
entry.set_xalign(0.0)
entry.set_hexpand(True)
entry.set_wrap(True)
entry.set_selectable(True)
return entry


def attach_row(grid: Gtk.Grid, left: Gtk.Widget, right: Gtk.Widget, index: int):
grid.attach(left, 0, index, 1, 1)
grid.attach(right, 1, index, 1, 1)


def replace_row(grid: Gtk.Grid, left: Gtk.Widget, right: Gtk.Widget, index: int):
cur_left = grid.get_child_at(0, index)
cur_right = grid.get_child_at(1, index)
if cur_left: grid.remove(cur_left)
if cur_right: grid.remove(cur_right)
attach_row(grid, left, right, index)


@Gtk.Template(resource_path='/org/yumlrecipes/yumlrecipes/window.ui')
class YumlRecipesWindow(Adw.ApplicationWindow):
__gtype_name__ = 'YumlRecipesWindow'

scrolled_window = Gtk.Template.Child()
content = Gtk.Template.Child()
image_frame = Gtk.Template.Child()
image = Gtk.Template.Child()
title = Gtk.Template.Child()
serving_combobox = Gtk.Template.Child()
ingredient_frame = Gtk.Template.Child()
ingredient_listbox = Gtk.Template.Child()
ingredient_quantity_listbox = Gtk.Template.Child()
ingredient_grid = Gtk.Template.Child()
step_frame = Gtk.Template.Child()
step_listbox = Gtk.Template.Child()
step_index_listbox = Gtk.Template.Child()
step_grid = Gtk.Template.Child()
variant_frame = Gtk.Template.Child()
variant_listbox = Gtk.Template.Child()
variant_grid = Gtk.Template.Child()

def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_title('Yuml Recipes')
self.serving_combobox.connect('changed', self.__on_serving_changed)
self.initial_title = self.title.get_text()
self.ingredients = None

def create_entry(list_model: ListModel):
entry = Gtk.Label(label=list_model.text)
entry.set_halign(list_model.align)
return entry

self.ingredient_list_model = Gio.ListStore().new(ListModel)
self.ingredient_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.ingredient_listbox.bind_model(self.ingredient_list_model, create_entry)

self.ingredient_quantity_list_model = Gio.ListStore().new(ListModel)
self.ingredient_quantity_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.ingredient_quantity_listbox.bind_model(self.ingredient_quantity_list_model, create_entry)

self.step_list_model = Gio.ListStore().new(ListModel)
self.step_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.step_listbox.bind_model(self.step_list_model, create_entry)

self.step_index_list_model = Gio.ListStore().new(ListModel)
self.step_index_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.step_index_listbox.bind_model(self.step_index_list_model, create_entry)

self.variant_list_model = Gio.ListStore().new(ListModel)
self.variant_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.variant_listbox.bind_model(self.variant_list_model, create_entry)
self.ingredients = []

def show_title(self, title: str) -> None:
self.set_title(title)
Expand All @@ -91,51 +91,54 @@ def show_images(self, images: List[str]) -> None:
self.image.set_filename(images[0])

def show_servings(self, servings: List[yuml.Serving]) -> None:
if len(servings) == 0:
self.serving_combobox.set_visible(len(servings) > 0)
empty_text = True
for serving in servings:
empty_text = empty_text and not bool(serving.text)
self.serving_combobox.append_text(serving.text)
if empty_text:
self.serving_combobox.set_visible(False)
else:
empty_text = True
for serving in servings:
empty_text = empty_text and not bool(serving.text)
self.serving_combobox.append_text(serving.text)
if empty_text:
self.serving_combobox.set_visible(False)
elif len(servings) == 1:
self.serving_combobox.set_sensitive(False)

def __on_serving_changed(self, combobox):
elif len(servings) == 1:
self.serving_combobox.set_sensitive(False)

def __on_serving_changed(self, combobox: Gtk.ComboBoxText):
index = combobox.get_active()
if index is None and self.ingredients is None:
if index is None:
return
self.ingredient_quantity_list_model.remove_all()
for ingredient in self.ingredients:
quantity = ingredient.quantities[index]
self.ingredient_quantity_list_model.append(ListModel(quantity, Gtk.Align.START))
left = create_left_entry(ingredient.text)
right = create_right_entry(ingredient.quantities[index])
replace_row(self.ingredient_grid, left, right, ingredient.index + 1)

def show_ingredients(self, ingredients: List[yuml.Ingredient]) -> None:
self.ingredient_list_model.remove_all()
if len(ingredients) == 0:
self.ingredient_frame.set_visible(False)
else:
for ingredient in ingredients:
self.ingredient_list_model.append(ListModel(ingredient.text, Gtk.Align.END))
self.ingredient_frame.set_visible(len(ingredients) > 0)
self.ingredients = ingredients
self.serving_combobox.set_active(0)

def show_steps(self, steps: List[yuml.Step]) -> None:
self.step_list_model.remove_all()
self.step_index_list_model.remove_all()
if len(steps) == 0:
self.step_frame.set_visible(False)
else:
for step in steps:
self.step_list_model.append(ListModel(step.text, Gtk.Align.START))
self.step_index_list_model.append(ListModel(f"{step.index + 1}.", Gtk.Align.END))
self.step_frame.set_visible(len(steps) > 0)
for step in steps:
left = create_left_entry(f"{step.index + 1}.")
right = create_right_entry(step.text)
attach_row(self.step_grid, left, right, step.index)

def show_variants(self, variants: List[yuml.Variant]) -> None:
self.variant_list_model.remove_all()
if len(variants) == 0:
self.variant_frame.set_visible(False)
else:
for variant in variants:
self.variant_list_model.append(ListModel(f"- {variant.text}", Gtk.Align.START))
self.variant_frame.set_visible(len(variants) > 0)
for variant in variants:
left = create_left_entry(f"-")
right = create_right_entry(variant.text)
attach_row(self.variant_grid, left, right, variant.index)

def ensure_natural_height(self):
def tick(widget, frame_clock, user_data):
if self.count > 0:
max_content_height = self.scrolled_window.get_max_content_height()
allocated_content_height = self.content.get_allocated_height()
min_content_height = min(max_content_height, allocated_content_height)
self.scrolled_window.set_min_content_height(min_content_height)
else:
self.scrolled_window.add_tick_callback(tick, None)
self.count = self.count + 1
self.count = 0
self.scrolled_window.add_tick_callback(tick, None)

Loading

0 comments on commit a8fcba2

Please sign in to comment.