Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cosmetic Changes for Pagination #268

Merged
merged 9 commits into from
Sep 18, 2023
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
100 changes: 100 additions & 0 deletions dtool_lookup_gui/views/config_details.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import asyncio
import logging
import os
import gi


gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Pango
from dtool_lookup_api.asynchronous import config
from ..utils.logging import _log_nested

# Set up logger for this module
logger = logging.getLogger(__name__)

@Gtk.Template(filename=f'{os.path.dirname(__file__)}/config_details.ui')
class ConfigDialog(Gtk.Window):
__gtype_name__ = 'ConfigDialog'
config_text_view = Gtk.Template.Child()

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# Create the "bold" tag for the buffer
buffer = self.config_text_view.get_buffer()
tag_bold = buffer.create_tag("bold", weight=Pango.Weight.BOLD)

async def _retrieve_config(self):
"""Asynchronously fetch server configuration and update the text view."""
server_config = await config()
config_info = self._format_server_config(server_config)
_log_nested(logger.info, config_info)
buffer = self.config_text_view.get_buffer()
buffer.set_text("") # Clearing the buffer
buffer.insert_at_cursor("\n".join(config_info))
self._apply_bold_to_buffer(buffer)

def _apply_bold_to_buffer(self, buffer):
"""Apply bold style where '**' are present."""
text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False)
start = 0

while True:
start_pos = text.find("**", start)
if start_pos == -1:
break
end_pos = text.find("**", start_pos + 2)
if end_pos == -1:
break

start_mark = buffer.create_mark(None, buffer.get_iter_at_offset(start_pos), True)
end_mark = buffer.create_mark(None, buffer.get_iter_at_offset(end_pos), True)

buffer.apply_tag_by_name("bold", buffer.get_iter_at_mark(start_mark), buffer.get_iter_at_mark(end_mark))
buffer.delete(buffer.get_iter_at_mark(end_mark), buffer.get_iter_at_offset(end_pos + 2))
buffer.delete(buffer.get_iter_at_mark(start_mark), buffer.get_iter_at_offset(start_pos + 2))

# Removing marks after their use
buffer.delete_mark(start_mark)
buffer.delete_mark(end_mark)

text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False)
start = start_pos + 1

def _format_server_config(self, server_config, indent=0):
"""Format the server configuration into a string representation for Markdown."""
markdown_lines = []
indentation = ' ' * indent # 4 spaces per indentation level

if not isinstance(server_config, dict):
return [f"{indentation}- {server_config}"]

for key, value in server_config.items():
# If value is a dictionary, recurse with increased indentation
if isinstance(value, dict):
markdown_lines.append(f"{indentation}**{key}**:")
markdown_lines.extend(self._format_server_config(value, indent + 1))
# If value is a list, handle it by enumerating its items
elif isinstance(value, list):
markdown_lines.append(f"{indentation}**{key}**:")
for item in value:
if isinstance(item, (dict, list)):
markdown_lines.extend(self._format_server_config(item, indent + 1))
else:
markdown_lines.append(f"{indentation}- {item}")
# For simple key-value pairs
else:
markdown_lines.append(f"{indentation}**{key}**: {value}")

return markdown_lines

@Gtk.Template.Callback()
def on_config_show(self, widget):
"""Callback executed when the window is shown; fetches server config."""
asyncio.create_task(self._retrieve_config())

@Gtk.Template.Callback()
def on_config_delete(self, widget, event):
"""Callback executed when window close event is triggered; hides the window instead of deleting."""
return self.hide_on_delete()

72 changes: 72 additions & 0 deletions dtool_lookup_gui/views/config_details.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<template class="ConfigDialog" parent="GtkWindow">
<property name="can-focus">False</property>
<property name="modal">True</property>
<property name="default-width">800</property>
<property name="default-height">600</property>
<signal name="delete-event" handler="on_config_delete" swapped="no"/>
<signal name="show" handler="on_config_show" swapped="no"/>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<child>
<object class="GtkTextView" id="config_text_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
<property name="cursor-visible">False</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="title" translatable="yes">Configurations</property>
<property name="show-close-button">True</property>
</object>
</child>
</template>
</interface>
4 changes: 2 additions & 2 deletions dtool_lookup_gui/views/login_window.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<property name="can-focus">False</property>
<property name="default-width">400</property>
<property name="default-height">200</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="modal">True</property> <!-- Added the modal property -->
<property name="window-position">center-on-parent</property> <!-- Ensures the window is centered -->
<signal name="delete-event" handler="on_delete" swapped="no"/>
<child>
<!-- n-columns=3 n-rows=3 -->
Expand Down
78 changes: 48 additions & 30 deletions dtool_lookup_gui/views/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
from .about_dialog import AboutDialog
from .settings_dialog import SettingsDialog
from .server_versions_dialog import ServerVersionsDialog
from .config_details import ConfigDialog
from .login_window import LoginWindow
from .log_window import LogWindow

Expand Down Expand Up @@ -154,6 +155,7 @@ class MainWindow(Gtk.ApplicationWindow):

settings_button = Gtk.Template.Child()
version_button = Gtk.Template.Child()
config_button = Gtk.Template.Child()

error_bar = Gtk.Template.Child()
error_label = Gtk.Template.Child()
Expand All @@ -169,7 +171,7 @@ class MainWindow(Gtk.ApplicationWindow):
main_statusbar = Gtk.Template.Child()
contents_per_page = Gtk.Template.Child()

test_login = Gtk.Template.Child()


def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -201,6 +203,7 @@ def __init__(self, *args, **kwargs):
self.log_window = LogWindow(application=self.application)
self.settings_dialog = SettingsDialog(application=self.application)
self.about_dialog = AboutDialog(application=self.application)
self.config_details = ConfigDialog(application=self.application)
self.server_versions_dialog = ServerVersionsDialog(application=self.application)

# window-scoped actions
Expand Down Expand Up @@ -258,8 +261,14 @@ def __init__(self, *args, **kwargs):

_logger.debug(f"Constructed main window for app '{self.application.get_application_id()}'")


# Initialize pagination parameters, hide page advancer button by default, set default items per page, and highlight the current page button.
self.pagination = {}
self.page_advancer_button.set_visible(False)
self.contents_per_page_value = 10

style_context = self.curr_page_button.get_style_context()
style_context.add_class('suggested-action')

# utility methods
def refresh(self):
Expand Down Expand Up @@ -376,7 +385,7 @@ async def _fetch_search_results(self, keyword, on_show=None, page_number=1, page
self.show_error(e)

async def retry():
await asyncio.sleep(1) # TODO: This is a dirty workaround for not having the login window pop up twice
await asyncio.sleep(0.5) # TODO: This is a dirty workaround for not having the login window pop up twice
await self._fetch_search_results(keyword, on_show, page_number, page_size, widget)

# What happens is that the LoginWindow evokes the renew-token action via Gtk framework.
Expand Down Expand Up @@ -575,8 +584,8 @@ def version_button_clicked(self, widget):
self.server_versions_dialog.show()

@Gtk.Template.Callback()
def on_test_login_clicked(self, widget):
self.login_window.show()
def config_button_clicked(self, widget):
self.config_details.show()

@Gtk.Template.Callback()
def on_logging_clicked(self, widget):
Expand Down Expand Up @@ -817,85 +826,93 @@ def on_error_bar_response(self, widget, response_id):

@Gtk.Template.Callback()
def on_first_page_button_clicked(self, widget):
# Navigate to the first page if results are not currently being fetched
if not self.fetching_results:
page_number = 1
self.pagination['page'] = page_number
self.disable_pagination_buttons()
asyncio.create_task(
self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))
self.curr_page_button.set_label(str(page_number))
self.page_advancer_button.set_label(str(page_number - 1))
self.next_page_advancer_button.set_label(str(page_number + 1))
asyncio.create_task(self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))
self.update_pagination_buttons(page_number, widget)

@Gtk.Template.Callback()
def on_nextoption_page_button_clicked(self, widget):
# Navigate to the next page if available
if not self.fetching_results and self.pagination['page'] < self.pagination['last_page']:
page_number = self.pagination['page'] + 1
self.update_pagination_buttons(page_number, widget)
self.disable_pagination_buttons()
asyncio.create_task(
self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))
asyncio.create_task(self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))

@Gtk.Template.Callback()
def on_last_page_button_clicked(self, widget):
# Navigate to the last page
page_number = self.pagination['last_page']
self.update_pagination_buttons(page_number, widget)
self.disable_pagination_buttons()
asyncio.create_task(
self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))
asyncio.create_task(self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))

@Gtk.Template.Callback()
def on_prev_page_button_clicked(self, widget):
# Navigate to the previous page if it exists
if not self.fetching_results and self.pagination['page'] > 1:
self.pagination['page'] -= 1
page_number = self.pagination['page']
page_number = self.pagination['page'] - 1
self.update_pagination_buttons(page_number, widget)
self.disable_pagination_buttons()
asyncio.create_task(
self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))
asyncio.create_task(self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))

@Gtk.Template.Callback()
def curr_page_button_clicked(self, widget):
# Highlight the current page button and fetch its results
page_number = self.pagination['page']
style_context = self.curr_page_button.get_style_context()
style_context.add_class('suggested-action')
self.update_pagination_buttons(page_number, widget)
self.disable_pagination_buttons()
asyncio.create_task(
self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))
asyncio.create_task(self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))

@Gtk.Template.Callback()
def page_advancer_button_clicked(self, widget):
# Navigate to the previous page using the advancer button
page_number = self.pagination['page']
if not self.fetching_results and page_number > 1:
page_number -= 1
self.update_pagination_buttons(page_number, widget)
self.disable_pagination_buttons()
asyncio.create_task(
self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))
asyncio.create_task(self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))

@Gtk.Template.Callback()
def next_page_advancer_button_clicked(self, widget):
# Navigate to the next page using the advancer button
page_number = self.pagination['page']
if not self.fetching_results and page_number < self.pagination['last_page']:
page_number += 1
self.update_pagination_buttons(page_number, widget)
self.disable_pagination_buttons()
asyncio.create_task(
self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))
asyncio.create_task(self._fetch_search_results(keyword=None, on_show=None, page_number=page_number,
page_size=int(self.contents_per_page_value), widget=widget))

def update_pagination_buttons(self, page_number, widget):
# Update labels and visibility of pagination buttons based on current page
self.pagination['page'] = page_number
self.curr_page_button.set_label(str(page_number))
self.page_advancer_button.set_label(str(page_number - 1))
self.next_page_advancer_button.set_label(str(page_number + 1))

# Hide next_page_advancer_button if current page is the last page or beyond
if page_number >= self.pagination['last_page']:
self.next_page_advancer_button.set_visible(False)
else:
self.next_page_advancer_button.set_visible(True)
self.page_advancer_button.set_visible(page_number != 1)

def disable_pagination_buttons(self):
# Disable all pagination buttons (typically while fetching results)
self.fetching_results = True
self.first_page_button.set_sensitive(False)
self.nextoption_page_button.set_sensitive(False)
Expand All @@ -906,6 +923,7 @@ def disable_pagination_buttons(self):
self.next_page_advancer_button.set_sensitive(False)

def enable_pagination_buttons(self):
# Enable all pagination buttons (typically after fetching results)
self.fetching_results = False
self.first_page_button.set_sensitive(True)
self.nextoption_page_button.set_sensitive(True)
Expand Down
Loading
Loading