From 906d05e377ba85360b8896627a5319b2878974a5 Mon Sep 17 00:00:00 2001 From: TheJackiMonster Date: Sun, 30 Oct 2022 12:51:07 +0100 Subject: [PATCH] Implement world view: selection and simple changes Signed-off-by: TheJackiMonster --- manuskript/data/__init__.py | 1 + manuskript/data/links.py | 3 +- manuskript/data/world.py | 9 ++ manuskript/ui/mainWindow.py | 2 +- manuskript/ui/views/worldView.py | 172 ++++++++++++++++++++++++++++++- ui/world.glade | 38 ++++--- 6 files changed, 209 insertions(+), 16 deletions(-) diff --git a/manuskript/data/__init__.py b/manuskript/data/__init__.py index 45dd6be..b124673 100644 --- a/manuskript/data/__init__.py +++ b/manuskript/data/__init__.py @@ -18,3 +18,4 @@ from manuskript.data.summary import Summary from manuskript.data.template import Template, TemplateLevel, TemplateKind from manuskript.data.unique_id import UniqueIDHost, UniqueID from manuskript.data.version import Version +from manuskript.data.world import World, WorldItem diff --git a/manuskript/data/links.py b/manuskript/data/links.py index 2b6c1e3..0203887 100644 --- a/manuskript/data/links.py +++ b/manuskript/data/links.py @@ -22,7 +22,8 @@ class Links: self.callbacks.append(callback) def remove(self, callback: Callable[[LinkAction, UniqueID, any], None]): - self.callbacks.remove(callback) + if callback in self.callbacks: + self.callbacks.remove(callback) def call(self, action: LinkAction, UID: UniqueID, host: any): for callback in self.callbacks: diff --git a/manuskript/data/world.py b/manuskript/data/world.py index 49d6e28..724c2bf 100644 --- a/manuskript/data/world.py +++ b/manuskript/data/world.py @@ -19,6 +19,12 @@ class WorldItem: self.conflict = None self.children = list() + def remove(self): + for child in self.children: + child.remove() + + self.world.removeItem(self) + def __iter__(self): return self.children.__iter__() @@ -48,6 +54,9 @@ class World: self.host.removeID(item.UID) self.items.pop(item.UID.value) + def getItemByID(self, ID: int) -> WorldItem: + return self.items.get(ID, None) + def __iter__(self): return self.items.values().__iter__() diff --git a/manuskript/ui/mainWindow.py b/manuskript/ui/mainWindow.py index 7e44ad2..b3cb111 100644 --- a/manuskript/ui/mainWindow.py +++ b/manuskript/ui/mainWindow.py @@ -67,7 +67,7 @@ class MainWindow: self.summaryView = MainWindow.packViewIntoSlot(builder, "summary_slot", SummaryView, self.project.summary) self.charactersView = MainWindow.packViewIntoSlot(builder, "characters_slot", CharactersView, self.project.characters) self.plotView = MainWindow.packViewIntoSlot(builder, "plot_slot", PlotView, self.project.plots) - self.worldView = MainWindow.packViewIntoSlot(builder, "world_slot", WorldView) + self.worldView = MainWindow.packViewIntoSlot(builder, "world_slot", WorldView, self.project.world) self.outlineView = MainWindow.packViewIntoSlot(builder, "outline_slot", OutlineView) self.editorView = MainWindow.packViewIntoSlot(builder, "editor_slot", EditorView) diff --git a/manuskript/ui/views/worldView.py b/manuskript/ui/views/worldView.py index 9a32ffd..3a0bbc7 100644 --- a/manuskript/ui/views/worldView.py +++ b/manuskript/ui/views/worldView.py @@ -6,14 +6,184 @@ import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk +from manuskript.data import World, WorldItem +from manuskript.util import validString, invalidString, validInt, invalidInt + class WorldView: - def __init__(self): + def __init__(self, world: World): + self.world = world + self.worldItem = None + builder = Gtk.Builder() builder.add_from_file("ui/world.glade") self.widget = builder.get_object("world_view") + self.worldStore = builder.get_object("world_store") + self.refreshWorldStore() + + self.filteredWorldStore = builder.get_object("filtered_world_store") + self.filterWorldBuffer = builder.get_object("filter_world") + + self.filterWorldBuffer.connect("deleted-text", self.filterWorldDeletedText) + self.filterWorldBuffer.connect("inserted-text", self.filterWorldInsertedText) + + self.filteredWorldStore.set_visible_func(self.filterWorld) + self.filteredWorldStore.refilter() + + self.worldSelection = builder.get_object("world_selection") + + self.worldSelection.connect("changed", self.worldSelectionChanged) + + self.worldTreeView = builder.get_object("world_tree_view") + self.worldTreeView.expand_all() + + self.nameBuffer = builder.get_object("name") + self.descriptionBuffer = builder.get_object("description") + self.sourceOfPassionBuffer = builder.get_object("source_of_passion") + self.sourceOfConflictBuffer = builder.get_object("source_of_conflict") + + self.nameBuffer.connect("deleted-text", self.nameDeletedText) + self.nameBuffer.connect("inserted-text", self.nameInsertedText) + + self.descriptionBuffer.connect("changed", self.descriptionChanged) + self.sourceOfPassionBuffer.connect("changed", self.sourceOfPassionChanged) + self.sourceOfConflictBuffer.connect("changed", self.sourceOfConflictChanged) + + self.unloadWorldData() + + def __appendWorldItem(self, worldItem: WorldItem, parent_iter = None): + tree_iter = self.worldStore.append(parent_iter) + + if tree_iter is None: + return + + self.worldStore.set_value(tree_iter, 0, worldItem.UID.value) + self.worldStore.set_value(tree_iter, 1, validString(worldItem.name)) + + for item in worldItem: + self.__appendWorldItem(item, tree_iter) + + def refreshWorldStore(self): + self.worldStore.clear() + + for item in self.world.top: + self.__appendWorldItem(item) + + def loadWorldData(self, worldItem: WorldItem): + self.worldItem = None + + self.nameBuffer.set_text(validString(worldItem.name), -1) + self.descriptionBuffer.set_text(validString(worldItem.description), -1) + self.sourceOfPassionBuffer.set_text(validString(worldItem.passion), -1) + self.sourceOfConflictBuffer.set_text(validString(worldItem.conflict), -1) + + self.worldItem = worldItem + + def unloadWorldData(self): + self.worldItem = None + + self.nameBuffer.set_text("", -1) + self.descriptionBuffer.set_text("", -1) + self.sourceOfPassionBuffer.set_text("", -1) + self.sourceOfConflictBuffer.set_text("", -1) + + def worldSelectionChanged(self, selection: Gtk.TreeSelection): + model, tree_iter = selection.get_selected() + + if tree_iter is None: + self.unloadWorldData() + return + + worldItem = self.world.getItemByID(model[tree_iter][0]) + + if worldItem is None: + self.unloadWorldData() + else: + self.loadWorldData(worldItem) + + def __matchWorldItemByText(self, worldItem: WorldItem, text: str): + for item in worldItem: + if self.__matchWorldItemByText(item, text): + return True + + name = validString(worldItem.name) + return text in name.lower() + + def filterWorld(self, model, iter, userdata): + worldItem = self.world.getItemByID(model[iter][0]) + + if worldItem is None: + return False + + text = validString(self.filterWorldBuffer.get_text()) + return self.__matchWorldItemByText(worldItem, text.lower()) + + def filterWorldChanged(self, buffer: Gtk.EntryBuffer): + self.filteredWorldStore.refilter() + + def filterWorldDeletedText(self, buffer: Gtk.EntryBuffer, position: int, n_chars: int): + self.filterWorldChanged(buffer) + + def filterWorldInsertedText(self, buffer: Gtk.EntryBuffer, position: int, chars: str, n_chars: int): + self.filterWorldChanged(buffer) + + def nameChanged(self, buffer: Gtk.EntryBuffer): + if self.worldItem is None: + return + + text = buffer.get_text() + name = invalidString(text) + + self.worldItem.name = name + + world_id = self.worldItem.UID.value + + for row in self.worldStore: + if row[0] == world_id: + row[1] = validString(name) + break + + def nameDeletedText(self, buffer: Gtk.EntryBuffer, position: int, n_chars: int): + self.nameChanged(buffer) + + def nameInsertedText(self, buffer: Gtk.EntryBuffer, position: int, chars: str, n_chars: int): + self.nameChanged(buffer) + + def descriptionChanged(self, buffer: Gtk.TextBuffer): + if self.worldItem is None: + return + + start_iter = buffer.get_start_iter() + end_iter = buffer.get_end_iter() + + text = buffer.get_text(start_iter, end_iter, False) + + self.worldItem.description = invalidString(text) + + def sourceOfPassionChanged(self, buffer: Gtk.TextBuffer): + if self.worldItem is None: + return + + start_iter = buffer.get_start_iter() + end_iter = buffer.get_end_iter() + + text = buffer.get_text(start_iter, end_iter, False) + + self.worldItem.passion = invalidString(text) + + def sourceOfConflictChanged(self, buffer: Gtk.TextBuffer): + if self.worldItem is None: + return + + start_iter = buffer.get_start_iter() + end_iter = buffer.get_end_iter() + + text = buffer.get_text(start_iter, end_iter, False) + + self.worldItem.conflict = invalidString(text) + def show(self): self.widget.show_all() diff --git a/ui/world.glade b/ui/world.glade index deb15a1..5fd4b4e 100644 --- a/ui/world.glade +++ b/ui/world.glade @@ -1,5 +1,5 @@ - + + + + + + + - - - - - - + + world_store + True True @@ -69,14 +73,14 @@ along with Manuskript. If not, see . True False - + True True - world_store + filtered_world_store 0 True - + @@ -84,7 +88,7 @@ along with Manuskript. If not, see . - 0 + 1 @@ -109,7 +113,7 @@ along with Manuskript. If not, see . 8 4 - + True True True @@ -128,7 +132,7 @@ along with Manuskript. If not, see . - + True True True @@ -150,6 +154,7 @@ along with Manuskript. If not, see . True True + filter_world Filter @@ -216,6 +221,7 @@ along with Manuskript. If not, see . True True + name False @@ -249,6 +255,8 @@ along with Manuskript. If not, see . 300 True True + word-char + description @@ -323,6 +331,8 @@ along with Manuskript. If not, see . 300 True True + word-char + source_of_passion @@ -361,6 +371,8 @@ along with Manuskript. If not, see . 300 True True + word-char + source_of_conflict