Skip to content

Commit

Permalink
Moved Adw.Windows to AdwDialogs, responsive UI for mobile devices
Browse files Browse the repository at this point in the history
  • Loading branch information
aleiepure committed Feb 9, 2025
1 parent a33115c commit 8d32b23
Show file tree
Hide file tree
Showing 20 changed files with 577 additions and 462 deletions.
13 changes: 5 additions & 8 deletions src/dialogs/add_manual_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@


@Gtk.Template(resource_path=shared.PREFIX + '/ui/dialogs/add_manual.ui')
class AddManualDialog(Adw.Window):
class AddManualDialog(Adw.Dialog):
"""
This class represents the window to manually add content to the db.
This class represents the dialog to manually add content to the db.
Properties:
edit_mode (bool): whether or not the window is in add/edit mode
Expand All @@ -39,7 +39,7 @@ class AddManualDialog(Adw.Window):
get_season(title: str, poster: str, episodes: list): returns the tuple with the provided data or an empty tuple
Signals:
edit-saved(SeriesModel or MovieMovel): emited when the user clicks the save button
edit-saved(SeriesModel or MovieModel): emited when the user clicks the save button
"""

__gtype_name__ = 'AddManualDialog'
Expand Down Expand Up @@ -74,12 +74,10 @@ class AddManualDialog(Adw.Window):
seasons: list = []

def __init__(self,
parent: Gtk.Window,
edit_mode: bool = False,
content: MovieModel | SeasonModel | None = None
):
super().__init__()
self.set_transient_for(parent)

self.edit_mode = edit_mode
self._content = content
Expand Down Expand Up @@ -293,7 +291,7 @@ def _on_season_add_btn_clicked(self, user_data: object | None) -> None:
dialog = EditSeasonDialog(self, title=_(
'Season {num}').format(num=len(self.seasons)+1))
dialog.connect('edit-saved', self._on_edit_saved)
dialog.present()
dialog.present(dialog.parent)

def _on_edit_saved(self, source: Gtk.Widget, title: str, poster_uri: str, episodes: List[tuple]) -> None:
"""
Expand Down Expand Up @@ -370,8 +368,7 @@ def _on_add_done(self,
activity: BackgroundActivity):
"""Callback to complete async activity"""

self.get_ancestor(Adw.Window).get_transient_for(
).activate_action('win.refresh', None)
self.get_ancestor(Adw.ApplicationWindow).activate_action('win.refresh', None)
activity.end()

def _save_movie(self, poster_uri: str) -> None:
Expand Down
7 changes: 3 additions & 4 deletions src/dialogs/add_tmdb_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@


@Gtk.Template(resource_path=shared.PREFIX + '/ui/dialogs/add_tmdb.ui')
class AddTMDBDialog(Adw.Window):
class AddTMDBDialog(Adw.Dialog):
"""
This class represents the window used to search for movies and tv-series on TMDB.
This class represents the dialog used to search for movies and tv-series on TMDB.
Properties:
None
Expand All @@ -32,9 +32,8 @@ class AddTMDBDialog(Adw.Window):
_stack = Gtk.Template.Child()
_model = Gtk.Template.Child()

def __init__(self, parent: Gtk.Window):
def __init__(self):
super().__init__()
self.set_transient_for(parent)

@Gtk.Template.Callback('_on_searchentry_search_changed')
def _on_searchentry_search_changed(self, user_data: object | None) -> None:
Expand Down
4 changes: 2 additions & 2 deletions src/dialogs/edit_season_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@


@Gtk.Template(resource_path=shared.PREFIX + '/ui/dialogs/edit_season.ui')
class EditSeasonDialog(Adw.Window):
class EditSeasonDialog(Adw.Dialog):
"""
This class represents the window to edit a season.
Expand Down Expand Up @@ -46,7 +46,7 @@ def __init__(self,
episodes: List[tuple] | None = None):

super().__init__()
self.set_transient_for(parent)
self.parent = parent

self._title = title
self._poster_uri = poster_uri
Expand Down
143 changes: 101 additions & 42 deletions src/pages/details_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class DetailsView(Adw.NavigationPage):
_additional_info_box = Gtk.Template.Child()
_flow_box = Gtk.Template.Child()
_loading_lbl = Gtk.Template.Child()

mobile = True

def __init__(self, content: MovieModel | SeriesModel):
super().__init__()
Expand All @@ -85,11 +87,49 @@ def __init__(self, content: MovieModel | SeriesModel):
else:
self.content = local.get_series_by_id(content.id)
logging.info(
f'Loading info [{"movie" if type(content) is MovieModel else "TV Serie"}] {self.content.title}')
f'Loading info [{"movie" if type(content) is MovieModel else "TV Serie"}] {self.content.title}') # type: ignore

self.set_title(self.content.title) # type: ignore
self._view_stack.set_visible_child_name('loading')

if shared.schema.get_int('win-width') >= 550:
self.mobile = False
else:
self.mobile = True

self._populate_data()

@Gtk.Template.Callback()
def _on_breakpoint_applied(self, breakpoint: Adw.Breakpoint) -> None:
"""
Callback for the "applied" signal.
Sets the orientation of the page based on the breakpoint.
Args:
breakpoint (Adw.Breakpoint): the breakpoint that was applied
Returns:
None
"""

self.mobile = True
self._build_seasons_group()

@Gtk.Template.Callback()
def _on_breakpoint_unapplied(self, breakpoint: Adw.Breakpoint) -> None:
"""
Callback for the "unapplied" signal.
Sets the orientation of the page based on the breakpoint.
Args:
breakpoint (Adw.Breakpoint): the breakpoint that was unapplied
Returns:
None
"""

self.mobile = False
self._build_seasons_group()

def _populate_data(self) -> None:
"""
Expand All @@ -108,8 +148,7 @@ def _populate_data(self) -> None:
if not Adw.StyleManager.get_default().get_high_contrast():
self._background_picture.set_file(Gio.File.new_for_uri(
self.content.backdrop_path)) # type: ignore
# type: ignore
with Image.open(self.content.backdrop_path[7:]) as image:
with Image.open(self.content.backdrop_path[7:]) as image: # type: ignore
stat = ImageStat.Stat(image.convert('L'))

luminance = [
Expand Down Expand Up @@ -206,6 +245,7 @@ def _build_seasons_group(self) -> None:
list_box.remove_all()

self._episode_rows = []

for season in self.content.seasons: # type: ignore
season_row = Adw.ExpanderRow(title=season.title,
subtitle=ngettext('{num} Episode'.format(num=season.episodes_number),
Expand All @@ -223,42 +263,50 @@ def _build_seasons_group(self) -> None:
season_row.add_prefix(poster)

button = Gtk.Button(valign=Gtk.Align.CENTER)
btn_content = Adw.ButtonContent()

if all(episode.watched for episode in season.episodes):
btn_content.set_label(_('Watched'))
btn_content.set_icon_name('check-plain')

if not self.mobile:
btn_content = Adw.ButtonContent()

if all(episode.watched for episode in season.episodes):
btn_content.set_label(_('Watched'))
btn_content.set_icon_name('check-plain')
else:
btn_content.set_label(_('Mark as Watched'))
btn_content.set_icon_name('watchlist')

button.set_child(btn_content)
season_row.add_suffix(button)
else:
btn_content.set_label(_('Mark as Watched'))
btn_content.set_icon_name('watchlist')

button.set_child(btn_content)
season_row.add_suffix(button)
if all(episode.watched for episode in season.episodes):
button.set_icon_name('check-plain')
else:
button.set_icon_name('watchlist')
season_row.add_suffix(button)

tmp = []
for episode in season.episodes:
episode_row = EpisodeRow(episode)
episode_row = EpisodeRow(episode, small_controls=self.mobile)
episode_row.connect(
'watched-clicked', self._on_episode_watch_clicked, (btn_content, season))
'watched-clicked', self._on_episode_watch_clicked, (button, season))
season_row.add_row(episode_row)
tmp.append(episode_row)

self._seasons_group.add(season_row)
self._episode_rows.append((season, tmp))

button.connect('clicked', self._on_season_watched_clicked,
(btn_content, season, self._episode_rows))
(button, season, self._episode_rows))

def _on_episode_watch_clicked(self,
source: Gtk.Widget,
data: Tuple[Adw.ButtonContent, SeasonModel]) -> None:
data: Tuple[Gtk.Button, SeasonModel]) -> None:
"""
Callback for "watched-clicked" signal.
Called after an episode is (un)marked as watched, checks and updates, if needed, the watched button for the corresponding season.
Args:
source (Gtk.Widget): caller widget
data(tuple[Adw.ButtonContent, SeasonModel]): tuple with the Adw.ButtonContent to change and the SeasonModel
data(tuple[Gtk.Button, SeasonModel]): tuple with the Gtk.Button to change and the SeasonModel
parent of the changed episode
Returns:
Expand All @@ -267,35 +315,41 @@ def _on_episode_watch_clicked(self,

self.content = local.get_series_by_id(self.content.id) # type: ignore

btn_content = data[0]
season_idx = 0
for idx, season in enumerate(self.content.seasons): # type: ignore
if season == data[1]:
season_idx = idx

if all(episode.watched for episode in self.content.seasons[season_idx].episodes):
btn_content.set_label(_('Watched'))
btn_content.set_icon_name('check-plain')

if not self.mobile:
btn_content = data[0]
if all(episode.watched for episode in self.content.seasons[season_idx].episodes): # type: ignore
btn_content.set_label(_('Watched'))
btn_content.set_icon_name('check-plain')
else:
btn_content.set_label(_('Mark as Watched'))
btn_content.set_icon_name('watchlist')
else:
btn_content.set_label(_('Mark as Watched'))
btn_content.set_icon_name('watchlist')
if all(episode.watched for episode in self.content.seasons[season_idx].episodes): # type: ignore
data[0].set_icon_name('check-plain')
else:
data[0].set_icon_name('watchlist')

# Update season status
local.mark_watched_series(self.content.id, all(
local.mark_watched_series(self.content.id, all( # type: ignore
episode.watched for season in self.content.seasons for episode in season.episodes)) # type: ignore
self.activate_action('win.refresh', None)

def _on_season_watched_clicked(self,
source: Gtk.Widget,
data: Tuple[Adw.ButtonContent, SeasonModel, List[Tuple[SeasonModel, List[EpisodeRow]]]]) -> None:
data: Tuple[Gtk.Button, SeasonModel, List[Tuple[SeasonModel, List[EpisodeRow]]]]) -> None:
"""
Callback for "clicked" signal.
Marks a whole season as (un)watched, making changes in the db and updating the ui.
Args:
source (Gtk.Widget): caller widget
data (Tuple[Adw.ButtonContent, SeasonModel, List[Tuple[SeasonModel, List[EpisodeRow]]]]): tuple with the
Adw.ButtonContent to change, the SeasonModel of the modified season, and a list of tuples of all
data (Tuple[Gtk.Button, SeasonModel, List[Tuple[SeasonModel, List[EpisodeRow]]]]): tuple with the
Gtk.Button to change, the SeasonModel of the modified season, and a list of tuples of all
SeasonModels and associated EpisodeRows.
Returns:
Expand All @@ -304,7 +358,6 @@ def _on_season_watched_clicked(self,

self.content = local.get_series_by_id(self.content.id) # type: ignore

btn_content = data[0]
season_idx = 0
for idx, season in enumerate(self.content.seasons): # type: ignore
if season == data[1]:
Expand All @@ -316,8 +369,7 @@ def _on_season_watched_clicked(self,
episode_rows = item[1]

# Make changes in db
# type: ignore
for episode in self.content.seasons[season_idx].episodes:
for episode in self.content.seasons[season_idx].episodes: # type: ignore
local.mark_watched_episode(episode.id, not all(
episode.watched for episode in self.content.seasons[season_idx].episodes)) # type: ignore

Expand All @@ -327,12 +379,20 @@ def _on_season_watched_clicked(self,
not all(episode.watched for episode in self.content.seasons[season_idx].episodes)) # type: ignore

# Update season expander
if not all(episode.watched for episode in self.content.seasons[season_idx].episodes):
btn_content.set_label(_('Watched'))
btn_content.set_icon_name('check-plain')
if not self.mobile:
btn_content = data[0]

if not all(episode.watched for episode in self.content.seasons[season_idx].episodes): # type: ignore
btn_content.set_label(_('Watched'))
btn_content.set_icon_name('check-plain')
else:
btn_content.set_label(_('Mark as Watched'))
btn_content.set_icon_name('watchlist')
else:
btn_content.set_label(_('Mark as Watched'))
btn_content.set_icon_name('watchlist')
if all(episode.watched for episode in self.content.seasons[season_idx].episodes): # type: ignore
data[0].set_icon_name('check-plain')
else:
data[0].set_icon_name('watchlist')

# Update season status
local.mark_watched_series(self.content.id, not all( # type: ignore
Expand Down Expand Up @@ -368,7 +428,7 @@ def _build_flow_box(self) -> None:
label.add_css_class('heading')
box.append(label)
# type: ignore
box.append(Gtk.Label(label=self.content.original_language.name))
box.append(Gtk.Label(label=self.content.original_language.name, lines=2, wrap=True))
self._flow_box.append(box)

if self.content.original_title: # type: ignore
Expand All @@ -377,7 +437,7 @@ def _build_flow_box(self) -> None:
label.add_css_class('heading')
box.append(label)
# type: ignore
box.append(Gtk.Label(label=self.content.original_title))
box.append(Gtk.Label(label=self.content.original_title, lines=2, wrap=True))
self._flow_box.append(box)

# Movie specific
Expand Down Expand Up @@ -488,10 +548,9 @@ def _on_edit_btn_clicked(self, user_data: object | None) -> None:
logging.info(
f'Editing [{"movie" if type(self.content) is MovieModel else "TV Serie"}] {self.content.title}')

dialog = AddManualDialog(self.get_ancestor(
Gtk.Window), True, self.content)
dialog = AddManualDialog(edit_mode=True, content=self.content)
dialog.connect('edit-saved', self._on_edit_saved)
dialog.present()
dialog.present(self)

def _on_edit_saved(self, source: Gtk.Widget, content: MovieModel | SeriesModel) -> None:
"""
Expand Down
Loading

0 comments on commit 8d32b23

Please sign in to comment.