Implement world view: selection and simple changes

Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
This commit is contained in:
TheJackiMonster 2022-10-30 12:51:07 +01:00
parent 9129d1fa5a
commit 906d05e377
No known key found for this signature in database
GPG key ID: D850A5F772E880F9
6 changed files with 209 additions and 16 deletions

View file

@ -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

View file

@ -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:

View file

@ -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__()

View file

@ -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)

View file

@ -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()

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2
<!-- Generated with glade 3.40.0
Copyright (C) 2015-2021 Olivier Keshavjee et al.
@ -25,18 +25,22 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<!-- interface-name Manuskript -->
<!-- interface-description Manuskript is an open-source tool for writers. -->
<!-- interface-copyright 2015-2021 Olivier Keshavjee et al. -->
<object class="GtkTextBuffer" id="description"/>
<object class="GtkEntryBuffer" id="filter_world"/>
<object class="GtkEntryBuffer" id="name"/>
<object class="GtkTextBuffer" id="source_of_conflict"/>
<object class="GtkTextBuffer" id="source_of_passion"/>
<object class="GtkTreeStore" id="world_store">
<columns>
<!-- column-name ID -->
<column type="gint"/>
<!-- column-name name -->
<column type="gchararray"/>
<!-- column-name description -->
<column type="gchararray"/>
<!-- column-name passion -->
<column type="gchararray"/>
<!-- column-name conflict -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkTreeModelFilter" id="filtered_world_store">
<property name="child-model">world_store</property>
</object>
<object class="GtkScrolledWindow" id="world_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
@ -69,14 +73,14 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkTreeView">
<object class="GtkTreeView" id="world_tree_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="model">world_store</property>
<property name="model">filtered_world_store</property>
<property name="search-column">0</property>
<property name="enable-tree-lines">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
<object class="GtkTreeSelection" id="world_selection"/>
</child>
<child>
<object class="GtkTreeViewColumn">
@ -84,7 +88,7 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
@ -109,7 +113,7 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<property name="margin-bottom">8</property>
<property name="spacing">4</property>
<child>
<object class="GtkButton">
<object class="GtkButton" id="add_to_world">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
@ -128,7 +132,7 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
</packing>
</child>
<child>
<object class="GtkButton">
<object class="GtkButton" id="remove_from_world">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
@ -150,6 +154,7 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<object class="GtkEntry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="buffer">filter_world</property>
<property name="placeholder-text" translatable="yes">Filter</property>
</object>
<packing>
@ -216,6 +221,7 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<object class="GtkEntry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="buffer">name</property>
</object>
<packing>
<property name="expand">False</property>
@ -249,6 +255,8 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<property name="height-request">300</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">description</property>
</object>
</child>
<child type="label_item">
@ -323,6 +331,8 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<property name="height-request">300</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">source_of_passion</property>
</object>
</child>
<child type="label_item">
@ -361,6 +371,8 @@ along with Manuskript. If not, see <http://www.gnu.org/licenses/>.
<property name="height-request">300</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">source_of_conflict</property>
</object>
</child>
<child type="label_item">