This commit is contained in:
Gustavo Henrique Miranda 2025-07-23 10:27:43 +00:00 committed by GitHub
commit 5c03823df6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 714 additions and 2 deletions

View File

@ -9,6 +9,7 @@ from textual.containers import Vertical, Container, Horizontal
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.ui.screens.about_screen import AboutScreen
from pilgrim.ui.screens.diary_settings_screen import SettingsScreen
from pilgrim.ui.screens.edit_diary_modal import EditDiaryModal
from pilgrim.ui.screens.new_diary_modal import NewDiaryModal
from pilgrim.ui.screens.edit_entry_screen import EditEntryScreen
@ -23,6 +24,7 @@ class DiaryListScreen(Screen):
Binding("enter", "open_selected_diary", "Open diary"),
Binding("e", "edit_selected_diary", "Edit diary"),
Binding("r", "force_refresh", "Force refresh"),
Binding("s", "diary_settings", "Open The Selected Diary Settings"),
]
def __init__(self):
@ -285,4 +287,14 @@ class DiaryListScreen(Screen):
def action_quit(self):
"""Action to quit the application"""
self.app.exit()
self.app.exit()
def action_diary_settings(self):
if self.selected_diary_index is not None:
diary_id = self.diary_id_map.get(self.selected_diary_index)
if diary_id:
self.app.push_screen(SettingsScreen(diary_id=diary_id))
else:
self.notify("Invalid diary ID")
else:
self.notify("Select a diary to open the settings")

View File

@ -0,0 +1,280 @@
from textual.widgets import Static
from textual.containers import Container
from textual.widgets import Header, Footer, Label, Button,Checkbox,Input
from textual.screen import Screen
from textual.reactive import reactive
from textual.binding import Binding
from textual import on
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.ui.screens.modals.delete_all_entries_from_diary_modal import DeleteAllEntriesModal
from pilgrim.ui.screens.modals.delete_all_photos_from_diary_modal import DeleteAllPhotosModal
from pilgrim.ui.screens.modals.delete_diary_modal import DeleteDiaryModal
class SettingsScreen(Screen):
is_changed = reactive(False)
BINDINGS = [
Binding("escape","cancel","Cancel"),
]
def __init__(self,diary_id:int):
super().__init__()
self.current_diary = self.app.service_manager.get_travel_diary_service().read_by_id(diary_id)
self.header = Header()
self.footer = Footer()
self.title = "Settings"
self.diary_name = Static(self.current_diary.name,id="DiarySettingsScreen-DiaryName")
self.notify(str(self.app.config_manager))
self.is_the_diary_set_to_auto_open = self.app.config_manager.get_auto_open_diary() == self.current_diary.name
self.diary_entry_count = Static(str(len(self.current_diary.entries)))
self.diary_photo_count = Static(str(len(self.current_diary.photos)))
self.save_button = Button("Save",id="DiarySettingsScreen-SaveButton" )
self.cancel_button = Button("Cancel",id="DiarySettingsScreen-cancel_button")
self.apply_button = Button("Apply",id="DiarySettingsScreen-ApplyButton")
self.backup_diary_button = Button("Backup Diary")
self.delete_diary_button = Button("Delete Diary",id="DiarySettingsScreen-DeleteDiaryButton")
self.delete_all_entries_button = Button("Delete All Entries",id="DiarySettingsScreen-DeleteAllEntriesButton")
self.delete_all_photos_button = Button("Delete All Photos",id="DiarySettingsScreen-DeleteAllPhotosButton")
self.set_auto_open_to_this_diary = Checkbox(id="set_auto_open_to_this_diary",value=self.is_the_diary_set_to_auto_open)
self.delete_diary_button_container = Container(
Label("Delete Diary:"),
self.delete_diary_button,
id="DiarySettingsScreen-DeleteDiaryButtonContainer",
classes="DiarySettingsScreen-DeleteDiaryButtonContainer Button_Container"
)
self.backup_diary_button_container = Container(
Label("Backup Diary:"),
self.backup_diary_button,
id="DiarySettingsScreen-BackupDiaryButtonContainer",
classes="DiarySettingsScreen-BackupDiaryButtonContainer Button_Container"
)
self.delete_all_entries_button_container = Container(
Label("Delete All Entries:"),
self.delete_all_entries_button,
id="DiarySettingsScreen-DeleteAllEntriesButtonContainer",
classes="DiarySettingsScreen-DeleteAllEntriesButtonContainer Button_Container"
)
self.delete_all_photos_button_container = Container(
Label("Delete All Photos:"),
self.delete_all_photos_button,
id="DiarySettingsScreen-DeleteAllPhotosButtonContainer",
classes="DiarySettingsScreen-DeleteAllPhotosButtonContainer Button_Container"
)
self.diary_name_container = Container(
Label("Diary Name:"),
self.diary_name,
id="DiarySettingsScreen-DiaryNameContainer",
classes="DiarySettingsScreen-DiaryNameContainer Data_Container"
)
self.diary_entry_count_container = Container(
Label("Diary Entries:"),
self.diary_entry_count,
id="DiarySettingsScreen-DiaryEntryCountContainer",
classes="DiarySettingsScreen-DiaryEntryCountContainer Data_Container"
)
self.set_auto_open_to_this_diary_container = Container(
Label("Set Open This Diary On App Start?:"),
self.set_auto_open_to_this_diary,
id="DiarySettingsScreen-SetAutoOpenToThisDiaryContainer",
classes="DiarySettingsScreen-SetAutoOpenToThisDiaryContainer Data_Container"
)
self.diary_photo_count_container = Container(
Label("Diary Photos:"),
self.diary_photo_count,
id="DiarySettingsScreen-DiaryPhotoCountContainer",
classes="DiarySettingsScreen-DiaryPhotoCountContainer Data_Container"
)
self.diary_info_container = Container(
self.diary_name_container,
self.diary_entry_count_container,
self.diary_photo_count_container,
self.set_auto_open_to_this_diary_container,
id="DiarySettingsScreen-DiaryInfoContainer",
classes="DiarySettingsScreen-DiaryInfoContainer",
)
self.diary_denger_zone_container = Container(
self.backup_diary_button_container,
self.delete_diary_button_container,
self.delete_all_entries_button_container,
self.delete_all_photos_button_container,
id="DiarySettingsScreen-DiaryDengerZoneContainer",
classes="DiarySettingsScreen-DiaryDengerZoneContainer"
)
self.button_container = Container(
self.save_button,
self.apply_button,
self.cancel_button,
id="DiarySettingsScreen-ButtonContainer",
classes="DiarySettingsScreen-ButtonContainer"
)
self.main = Container(
self.diary_info_container,
self.diary_denger_zone_container,
self.button_container,
id="DiarySettingsScreen-MainContainer",
classes="DiarySettingsScreen-MainContainer"
)
self.diary_info_container.border_title = "Diary Info"
self.diary_denger_zone_container.border_title = "Denger Zone"
@on(Checkbox.Changed, "#set_auto_open_to_this_diary")
def on_checkbox_changed(self, event):
self.is_changed = not self.is_changed
@on(Button.Pressed, "#DiarySettingsScreen-cancel_button")
def on_cancel_button_pressed(self, event):
self.action_cancel()
@on(Button.Pressed, "#DiarySettingsScreen-DeleteDiaryButton")
def on_delete_diary_button_pressed(self, event):
self.app.push_screen(DeleteDiaryModal(diary_id=self.current_diary.id,diary_name=self.current_diary.name))
@on(Button.Pressed, "#DiarySettingsScreen-DeleteAllEntriesButton")
def on_delete_all_entries_button_pressed(self, event):
self.app.push_screen(DeleteAllEntriesModal(diary_id=self.current_diary.id))
@on(Button.Pressed, "#DiarySettingsScreen-DeleteAllPhotosButton")
def on_delete_all_photos_button_pressed(self, event):
self.app.push_screen(DeleteAllPhotosModal(diary_id=self.current_diary.id))
def action_cancel(self):
if self.is_changed:
self.notify("Cancel button pressed, but changes are not saved",severity="error")
return
self.dismiss()
@on(Button.Pressed, "#DiarySettingsScreen-SaveButton")
def on_save_button_pressed(self, event):
self.action_save()
@on(Button.Pressed, "#DiarySettingsScreen-ApplyButton")
def on_apply_button_pressed(self, event):
self.action_apply()
def watch_is_changed(self, value):
label = self.set_auto_open_to_this_diary_container.query_one(Label)
if value:
label.add_class("DiarySettingsScreen-SetAutoOpenToThisDiaryContainer-Not-Saved-Label")
else:
label.remove_class("DiarySettingsScreen-SetAutoOpenToThisDiaryContainer-Not-Saved-Label")
def compose(self):
yield Header()
yield self.main
yield Footer()
def on_mount(self):
if self.app.config_manager.get_auto_open_diary() == self.current_diary.name:
self.call_after_refresh(self.set_checkbox_state)
def set_checkbox_state(self):
self.set_auto_open_to_this_diary.value = True
def _set_auto_open_diary(self,value):
self.app.config_manager.set_auto_open_diary(value)
self.app.config_manager.save_config()
self.is_changed = False
def _get_auto_open_diary(self):
return self.app.config_manager.get_auto_open_diary()
def _make_auto_open_diary_value(self):
value = None
if self.set_auto_open_to_this_diary.value:
value = self.current_diary.name
return value
def action_save(self):
if not self.is_changed:
self.dismiss()
return
value = self._make_auto_open_diary_value()
current_auto_open = self._get_auto_open_diary()
if current_auto_open is None:
self._set_auto_open_diary(value)
self.notify("Settings saved")
self.dismiss()
return
if current_auto_open == self.current_diary.name:
if value is None:
self._set_auto_open_diary(None)
self.notify("Auto-open disabled")
else:
self.is_changed = False
self.notify("No changes made")
self.dismiss()
return
if value is not None:
self._set_auto_open_diary(value)
self.notify(f"Auto-open changed from '{current_auto_open}' to '{self.current_diary.name}'")
self.dismiss()
else:
self.is_changed = False
self.notify("No changes made")
self.dismiss()
def action_apply(self):
if not self.is_changed:
return
value = self._make_auto_open_diary_value()
current_auto_open = self._get_auto_open_diary()
if current_auto_open is None:
self._set_auto_open_diary(value)
self.notify("Settings applied")
return
if current_auto_open == self.current_diary.name:
if value is None:
self._set_auto_open_diary(None)
self.notify("Auto-open disabled")
else:
self.is_changed = False
self.notify("No changes made")
return
if value is not None:
self._set_auto_open_diary(value)
self.notify(f"Auto-open changed from '{current_auto_open}' to '{self.current_diary.name}'")
else:
self.is_changed = False
self.notify("No changes made")

View File

@ -0,0 +1,20 @@
from textual.widgets import Button
from textual import on
from pilgrim.ui.screens.modals.delete_yes_confirmation_modal import DeleteYesConfirmationModal
class DeleteAllEntriesModal(DeleteYesConfirmationModal):
def __init__(self,diary_id:int):
super().__init__(diary_id)
self.head_text.update("Are you sure you want to delete all entries from this diary?")
@on(Button.Pressed, "#DeleteDiaryModal-DeleteButton")
def on_delete_button_pressed(self, event):
self.result = True
self.dismiss()

View File

@ -0,0 +1,20 @@
from textual.widgets import Button
from textual import on
from pilgrim.ui.screens.modals.delete_yes_confirmation_modal import DeleteYesConfirmationModal
class DeleteAllPhotosModal(DeleteYesConfirmationModal):
def __init__(self,diary_id:int):
super().__init__(diary_id)
self.head_text.update("Are you sure you want to delete all photos from this diary?")
@on(Button.Pressed, "#DeleteDiaryModal-DeleteButton")
def on_delete_button_pressed(self, event):
self.result = True
self.dismiss()

View File

@ -0,0 +1,59 @@
from textual.containers import Container
from textual.widgets import Header, Footer, Label, Button,Input
from textual.screen import Screen
from textual.binding import Binding
from textual import on
class DeleteDiaryModal(Screen):
BINDINGS = [
Binding("escape","cancel","Cancel"),
]
def __init__(self, diary_id: int,diary_name:str):
super().__init__()
self.diary_id = diary_id
self.diary_name = diary_name
self.user_input = Input(placeholder=f"Type diary name to confirm: ({self.diary_name})",id="DeleteDiaryModal-UserInput")
self.delete_button = Button("Delete Diary",id="DeleteDiaryModal-DeleteButton",disabled=True)
self.cancel_button = Button("Cancel",id="DeleteDiaryModal-CancelButton")
self.result = None
def compose(self):
yield Header()
yield Container(
Label("Are you sure you want to delete this diary?"),
self.user_input,
Container(
self.delete_button,
self.cancel_button,
id="DeleteDiaryModal-ButtonContainer",
classes="DeleteDiaryModal-ButtonContainer"
),
id="DeleteDiaryModal-MainContainer",
classes="DeleteDiaryModal-MainContainer"
)
yield Footer()
@on(Input.Changed,"#DeleteDiaryModal-UserInput")
def on_user_input_changed(self, event):
input_text = event.value.strip()
if input_text == self.diary_name:
self.delete_button.disabled = False
else:
self.delete_button.disabled = True
@on(Button.Pressed,"#DeleteDiaryModal-DeleteButton")
def on_delete_button_pressed(self, event):
self.result = True
self.dismiss()
@on(Button.Pressed,"#DeleteDiaryModal-CancelButton")
def on_cancel_button_pressed(self, event):
self.action_cancel()
def action_cancel(self):
self.dismiss()

View File

@ -0,0 +1,54 @@
from textual.containers import Container
from textual.widgets import Header, Footer, Label, Button,Input
from textual.screen import Screen
from textual.binding import Binding
from textual import on
class DeleteYesConfirmationModal(Screen):
BINDINGS = [
Binding("escape", "cancel", "Cancel"),
]
def __init__(self,diary_id:int):
super().__init__()
self.diary_id = diary_id
self.user_input = Input(placeholder="Type 'Yes, I do ' to confirm",id="DeleteYesConfirmationModal-UserInput")
self.delete_button = Button("Delete",id="DeleteYesConfirmationModal-DeleteButton",disabled=True)
self.cancel_button = Button("Cancel",id="DeleteYesConfirmationModal-CancelButton")
self.head_text = Label("Are you sure you want to delete this diary?",id="DeleteYesConfirmationModal-HeadText")
self.second_head_text = Label("This action cannot be undone.",id="DeleteYesConfirmationModal-SecondHeadText")
self.delete_modal_container = Container(
self.head_text,
self.second_head_text,
self.user_input,
Container(
self.delete_button,
self.cancel_button,
id="DeleteYesConfirmationModal-DeleteButtonContainer",
classes="DeleteYesConfirmationModal-DeleteButtonContainer"
),
id="DeleteYesConfirmationModal-DeleteModalContainer",
classes="DeleteYesConfirmationModal-DeleteModalContainer"
)
self.result = None
@on(Input.Changed,"#DeleteYesConfirmationModal-UserInput")
def on_user_input_changed(self, event):
input_text = event.value.strip()
if input_text == "Yes, I do":
self.delete_button.disabled = False
else:
self.delete_button.disabled = True
@on(Button.Pressed,"#DeleteYesConfirmationModal-CancelButton")
def on_cancel_button_pressed(self, event):
self.action_cancel()
def action_cancel(self):
self.dismiss()
self.app.push_screen(SettingsScreen(diary=travel_diary[0]))
def compose(self):
yield Header()
yield Footer()
yield self.delete_modal_container

View File

@ -1,7 +1,8 @@
Screen {
layout: vertical;
background: $surface-darken-1;
background: $primary-background-darken-3;
align: center middle;
hatch: right $secondary-background-darken-3;
}
.EditEntryScreen-sub-header {
@ -623,4 +624,265 @@ Screen.-modal {
.ConfirmDeleteModal-Button {
margin: 0 1;
width: 1fr;
}
.DeleteYesConfirmationModal-DeleteModalContainer,
.DeleteDiaryModal-MainContainer {
align: center middle;
layout: vertical;
margin: 2;
padding: 2;
background: $primary-background;
height: auto;
width: auto;
min-width: 80%;
max-width: 95%;
border: solid $primary;
}
/* Labels de texto */
.DeleteYesConfirmationModal-DeleteModalContainer > Label,
.DeleteDiaryModal-MainContainer > Label {
margin: 1 0;
padding: 0 1;
color: $error;
text-align: center;
width: 100%;
}
/* Input fields */
.DeleteYesConfirmationModal-DeleteModalContainer > Input,
.DeleteDiaryModal-MainContainer > Input {
margin: 1 0;
width: 100%;
height: 3;
}
/* Container dos botões */
.DeleteYesConfirmationModal-DeleteButtonContainer,
.DeleteDiaryModal-ButtonContainer {
layout: horizontal;
align: center middle;
margin: 2 0 0 0;
height: auto;
width: 100%;
padding: 0;
}
/* Botões individuais */
.DeleteYesConfirmationModal-DeleteButtonContainer > Button,
.DeleteDiaryModal-ButtonContainer > Button {
width: 45%;
margin: 0 1;
height: 3;
}
/* Botão de delete (primeiro botão) */
.DeleteYesConfirmationModal-DeleteButtonContainer > Button:first-child,
.DeleteDiaryModal-ButtonContainer > Button:first-child {
background: $error-darken-1;
}
/* Botão de cancel */
.DeleteYesConfirmationModal-DeleteButtonContainer > Button:last-child,
.DeleteDiaryModal-ButtonContainer > Button:last-child {
background: $surface;
}
/* Estados disabled para botões de delete */
.DeleteYesConfirmationModal-DeleteButtonContainer > Button:first-child:disabled,
.DeleteDiaryModal-ButtonContainer > Button:first-child:disabled {
background: $surface-lighten-1;
color: $text-muted;
}
/* Espaçamento específico para labels secundários */
#DeleteYesConfirmationModal-SecondHeadText {
margin: 1 0;
padding: 0 1;
color: $warning;
text-align: center;
text-style: italic;
}
.Data_Container{
width: 100%;
layout: grid;
grid-size: 2 1; /* 2 colunas, 1 linha */
height:auto;
}
.Button_Container{
width: 100%;
layout: grid;
grid-size: 2 2;
height: auto;
}
.DiarySettingsScreen-DeleteDiaryButtonContainer{
grid-size: 2 1;
padding: 0 1;
content-align: center middle;
padding-bottom:1;
}
.DiarySettingsScreen-DeleteAllEntriesButtonContainer{
margin:0;
padding: 0 1;
height:auto;
padding-bottom:1;
}
.DiarySettingsScreen-DeleteAllPhotosButtonContainer{
margin:0;
padding: 0 1;
height:auto;
padding-bottom:1;
}
.DiarySettingsScreen-DeleteAllPhotosButtonContainer > Label,
.DiarySettingsScreen-DeleteAllEntriesButtonContainer > Label{
color: $error-lighten-3;
padding:1;
}
.DiarySettingsScreen-BackupDiaryButtonContainer{
margin:0;
padding:0 1;
padding-bottom:1
}
.DiarySettingsScreen-BackupDiaryButtonContainer > Label{
padding:1;
color: $success-darken-1;
}
.DiarySettingsScreen-BackupDiaryButtonContainer > Button{
background: $success;
}
.DiarySettingsScreen-DeleteDiaryButtonContainer > Label{
color: $error-lighten-3;
content-align: left middle;
padding:1
}
.DiarySettingsScreen-DeleteAllPhotosButtonContainer > Button,
.DiarySettingsScreen-DeleteAllEntriesButtonContainer > Button,
.DiarySettingsScreen-DeleteDiaryButtonContainer Button{
background: $error;
}
.DiarySettingsScreen-MainContainer{
align: center top;
layout: vertical;
margin:1;
padding:1;
background: $primary-background
}
.DiarySettingsScreen-DiaryInfoContainer{
padding:1 2;
border:round grey;
height:auto;
width: 90%;
padding-bottom: 0;
}
.DiarySettingsScreen-ButtonContainer{
height:auto;
layout: grid;
grid-size: 3 1;
grid-gutter:2;
dock:bottom;
}
.DiarySettingsScreen-ButtonContainer > Button:first-child {
margin-left:2
}
.DiarySettingsScreen-ButtonContainer > Button:last-child {
margin-right:2
}
.DiarySettingsScreen-ButtonContainer > Button {
width: 100%;
}
#DiarySettingsScreen-DiaryPhotoCountContainer > Static:first-child,
#DiarySettingsScreen-DiaryEntryCountContainer > Static:first-child,
#DiarySettingsScreen-DiaryNameContainer > Static:first-child{
text-align: left;
padding: 0 1;
padding-bottom:1
}
#DiarySettingsScreen-DiaryPhotoCountContainer > Static:last-child,
#DiarySettingsScreen-DiaryEntryCountContainer > Static:last-child,
#DiarySettingsScreen-DiaryNameContainer > Static:last-child{
text-align: right;
padding:0 1
}
.DiarySettingsScreen-SetAutoOpenToThisDiaryContainer > Checkbox{
margin:0;
padding:1;
background: $primary-background;
border:none;
}
.DiarySettingsScreen-SetAutoOpenToThisDiaryContainer-Not-Saved-Label{
text-style:bold;
color:$warning-lighten-2;
}
.DiarySettingsScreen-DiaryDengerZoneContainer{
border: round $error-darken-1;
width: 90%;
padding: 0 1;
height: auto;
padding-bottom: 0;
}

View File

@ -46,6 +46,8 @@ class ConfigManager(metaclass=SingletonMeta):
if self.__data["settings"]["diary"]["auto_open_diary_on_startup"] == "":
self.auto_open_diary = None
else:
self.auto_open_diary = self.__data["settings"]["diary"]["auto_open_diary_on_startup"]
self.auto_open_new_diary = self.__data["settings"]["diary"]["auto_open_on_creation"]
else:
print("Error: config.toml not found.")
@ -103,5 +105,8 @@ class ConfigManager(metaclass=SingletonMeta):
def set_auto_open_diary(self, value: str):
self.auto_open_diary = value
def get_auto_open_diary(self):
return self.auto_open_diary
def set_auto_open_new_diary(self, value: bool):
self.auto_open_new_diary = value