diff --git a/src/main/python/plotlyst/view/common.py b/src/main/python/plotlyst/view/common.py
index 16b485bf7..43953fa35 100644
--- a/src/main/python/plotlyst/view/common.py
+++ b/src/main/python/plotlyst/view/common.py
@@ -370,9 +370,12 @@ def insert_before(parent: QWidget, widget: QWidget, reference: QWidget):
parent.layout().insertWidget(i, widget)
-def insert_after(parent: QWidget, widget: QWidget, reference: QWidget):
+def insert_after(parent: QWidget, widget: QWidget, reference: QWidget, alignment=None):
i = parent.layout().indexOf(reference)
- parent.layout().insertWidget(i + 1, widget)
+ if alignment is not None:
+ parent.layout().insertWidget(i + 1, widget, alignment=alignment)
+ else:
+ parent.layout().insertWidget(i + 1, widget)
def tool_btn(icon: QIcon, tooltip: str = '', checkable: bool = False, base: bool = False,
diff --git a/src/main/python/plotlyst/view/scene_editor.py b/src/main/python/plotlyst/view/scene_editor.py
index ec3e60e89..1fd7cf390 100644
--- a/src/main/python/plotlyst/view/scene_editor.py
+++ b/src/main/python/plotlyst/view/scene_editor.py
@@ -26,12 +26,12 @@
from PyQt6.QtWidgets import QWidget, QTableView
from overrides import overrides
from qtanim import fade_in
-from qthandy import flow, clear_layout, underline, incr_font, margins
+from qthandy import flow, underline, incr_font, margins
from qtmenu import MenuWidget, ScrollableMenuWidget
from src.main.python.plotlyst.core.client import json_client
from src.main.python.plotlyst.core.domain import Novel, Scene, Document, StoryBeat, \
- Character, ScenePlotReference, TagReference, ScenePurposeType, ScenePurpose
+ Character, TagReference, ScenePurposeType, ScenePurpose
from src.main.python.plotlyst.env import app_env
from src.main.python.plotlyst.event.core import emit_info, EventListener, Event, emit_event
from src.main.python.plotlyst.event.handler import event_dispatchers
@@ -45,7 +45,6 @@
from src.main.python.plotlyst.view.widget.labels import CharacterLabel
from src.main.python.plotlyst.view.widget.scene.editor import ScenePurposeSelectorWidget, ScenePurposeTypeButton, \
SceneStorylineEditor, SceneAgendaEditor
-from src.main.python.plotlyst.view.widget.scene.plot import ScenePlotSelector
from src.main.python.plotlyst.view.widget.scenes import SceneTagSelector
@@ -87,8 +86,6 @@ def __init__(self, novel: Novel, scene: Optional[Scene] = None):
self.ui.lblTitleEmoji.setText(emoji.emojize(':clapper_board:'))
self.ui.lblSynopsisEmoji.setFont(self._emoji_font)
self.ui.lblSynopsisEmoji.setText(emoji.emojize(':scroll:'))
- self.ui.lblPlotEmoji.setFont(self._emoji_font)
- self.ui.lblPlotEmoji.setText(emoji.emojize(':chart_increasing:'))
self.ui.wdgStructure.setBeatsCheckable(True)
self.ui.wdgStructure.setStructure(self.novel)
@@ -140,7 +137,7 @@ def __init__(self, novel: Novel, scene: Optional[Scene] = None):
self._btnPurposeType.selectionRequested.connect(self._resetPurposeEditor)
self.ui.wdgMidbar.layout().insertWidget(0, self._btnPurposeType)
- self._storylineEditor = SceneStorylineEditor()
+ self._storylineEditor = SceneStorylineEditor(self.novel)
self.ui.tabStorylines.layout().addWidget(self._storylineEditor)
self._agencyEditor = SceneAgendaEditor(self.novel)
@@ -148,8 +145,6 @@ def __init__(self, novel: Novel, scene: Optional[Scene] = None):
self.ui.btnClose.clicked.connect(self._on_close)
- flow(self.ui.wdgPlotContainer)
-
self.ui.wdgSceneStructure.setUnsetCharacterSlot(self._pov_not_selected_notification)
self._update_view(scene)
@@ -185,12 +180,8 @@ def _update_view(self, scene: Optional[Scene] = None):
self.ui.sbDay.setValue(self.scene.day)
self.ui.wdgSceneStructure.setScene(self.novel, self.scene)
- clear_layout(self.ui.wdgPlotContainer)
- for plot_v in self.scene.plot_values:
- self._add_plot_selector(plot_v)
- self._add_plot_selector()
-
self.tag_selector.setScene(self.scene)
+ self._storylineEditor.setScene(self.scene)
self.ui.lineTitle.setText(self.scene.title)
self.ui.textSynopsis.setText(self.scene.synopsis)
@@ -259,14 +250,6 @@ def _pov_not_selected_notification(self):
emit_info('POV character must be selected first')
qtanim.shake(self.ui.wdgPov)
- def _add_plot_selector(self, plot_value: Optional[ScenePlotReference] = None):
- if plot_value or len(self.novel.plots) > len(self.scene.plot_values):
- plot_selector = ScenePlotSelector(self.novel, self.scene, simplified=len(self.scene.plot_values) > 0)
- plot_selector.plotSelected.connect(self._add_plot_selector)
- if plot_value:
- plot_selector.setPlot(plot_value)
- self.ui.wdgPlotContainer.layout().addWidget(plot_selector)
-
def _on_pov_changed(self, pov: Character):
self.scene.pov = pov
diff --git a/src/main/python/plotlyst/view/widget/scene/editor.py b/src/main/python/plotlyst/view/widget/scene/editor.py
index 6cc359ebe..e5490ba8f 100644
--- a/src/main/python/plotlyst/view/widget/scene/editor.py
+++ b/src/main/python/plotlyst/view/widget/scene/editor.py
@@ -18,7 +18,6 @@
along with this program. If not, see .
"""
from abc import abstractmethod
-from enum import Enum
from functools import partial
from typing import List, Optional, Any
@@ -35,17 +34,18 @@
from src.main.python.plotlyst.common import raise_unrecognized_arg
from src.main.python.plotlyst.core.domain import Scene, Novel, ScenePurpose, advance_story_scene_purpose, \
ScenePurposeType, reaction_story_scene_purpose, character_story_scene_purpose, setup_story_scene_purpose, \
- emotion_story_scene_purpose, exposition_story_scene_purpose, scene_purposes, Character
+ emotion_story_scene_purpose, exposition_story_scene_purpose, scene_purposes, Character, Plot
from src.main.python.plotlyst.event.core import EventListener, Event, emit_event
from src.main.python.plotlyst.event.handler import event_dispatchers
from src.main.python.plotlyst.events import SceneChangedEvent
from src.main.python.plotlyst.service.persistence import RepositoryPersistenceManager
from src.main.python.plotlyst.view.common import DelayedSignalSlotConnector, action, wrap, label, scrolled, \
- ButtonPressResizeEventFilter
+ ButtonPressResizeEventFilter, insert_after
from src.main.python.plotlyst.view.icons import IconRegistry
from src.main.python.plotlyst.view.widget.characters import CharacterSelectorButton
from src.main.python.plotlyst.view.widget.display import Icon
from src.main.python.plotlyst.view.widget.input import RemovalButton
+from src.main.python.plotlyst.view.widget.scene.plot import ScenePlotSelectorButton
class SceneMiniEditor(QWidget, EventListener):
@@ -362,11 +362,6 @@ def __init__(self, parent=None):
self._wdgPurposes.layout().addWidget(spacer())
-class SceneElementIconDisplayMode(Enum):
- Center = 0
- Left = 1
-
-
class SceneElementWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
@@ -434,7 +429,7 @@ def leaveEvent(self, event: QEvent) -> None:
else:
self._btnClose.setVisible(False)
- def setIcon(self, icon: str, colorActive: str):
+ def setIcon(self, icon: str, colorActive: str = 'black'):
self._colorActive = QColor(colorActive)
self._iconActive.setIcon(IconRegistry.from_name(icon, colorActive))
self._iconIdle.setIcon(IconRegistry.from_name(icon, 'lightgrey'))
@@ -497,13 +492,32 @@ def _updateValue(self, value: Any):
class PlotSceneElementEditor(TextBasedSceneElementWidget):
- def __init__(self, parent=None):
+ def __init__(self, novel: Novel, parent=None):
super().__init__(parent)
+ self._novel = novel
+
+ self._btnPlotSelector = ScenePlotSelectorButton(self._novel)
+ self._btnPlotSelector.plotSelected.connect(self._plotSelected)
+ self._btnPlotSelector.setFixedHeight(self._titleActive.sizeHint().height())
+ self._titleActive.setHidden(True)
+ insert_after(self._pageEditor, self._btnPlotSelector, reference=self._titleActive,
+ alignment=Qt.AlignmentFlag.AlignCenter)
+
+ def setScene(self, scene: Scene):
+ self._btnPlotSelector.setScene(scene)
+
+ def _plotSelected(self, plot: Plot):
+ self.setIcon(plot.icon, plot.icon_color)
+ font = self._btnPlotSelector.font()
+ font.setPointSize(self._titleActive.font().pointSize())
+ self._btnPlotSelector.setFont(font)
class AbstractSceneElementsEditor(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
+ self._scene: Optional[Scene] = None
+
vbox(self)
sp(self).h_exp()
self._scrollarea, self._wdgElementsParent = scrolled(self, frameless=True)
@@ -520,15 +534,19 @@ def __init__(self, parent=None):
self._wdgElementsParent.layout().addWidget(self._lblBottom)
self._wdgElementsParent.layout().addWidget(self._wdgElementsBottomRow)
+ def setScene(self, scene: Scene):
+ self._scene = scene
+
class SceneStorylineEditor(AbstractSceneElementsEditor):
- def __init__(self, parent=None):
+ def __init__(self, novel: Novel, parent=None):
super().__init__(parent)
- self._lblBottom.setText('Character relations')
+ self._novel = novel
+ # self._lblBottom.setText('Character relations')
- self._plotElement = PlotSceneElementEditor()
+ self._plotElement = PlotSceneElementEditor(self._novel)
self._plotElement.setText('Plot')
- self._plotElement.setIcon('fa5s.theater-masks', 'grey')
+ self._plotElement.setIcon('fa5s.theater-masks')
self._plotElement.setPlaceholderText('Describe how this scene is related to the selected plot')
self._themeElement = TextBasedSceneElementWidget()
@@ -541,13 +559,18 @@ def __init__(self, parent=None):
self._consequencesElement = TextBasedSceneElementWidget()
self._consequencesElement.setText('Consequences')
- self._consequencesElement.setIcon('mdi.ray-start-arrow', 'black')
+ self._consequencesElement.setIcon('mdi.ray-start-arrow')
self._wdgElementsTopRow.layout().addWidget(self._plotElement)
self._wdgElementsTopRow.layout().addWidget(self._themeElement)
self._wdgElementsTopRow.layout().addWidget(self._outcomeElement)
self._wdgElementsTopRow.layout().addWidget(self._consequencesElement)
+ @overrides
+ def setScene(self, scene: Scene):
+ super().setScene(scene)
+ self._plotElement.setScene(scene)
+
class SceneAgendaEditor(AbstractSceneElementsEditor):
def __init__(self, novel: Novel, parent=None):
diff --git a/src/main/python/plotlyst/view/widget/scene/plot.py b/src/main/python/plotlyst/view/widget/scene/plot.py
index 6b12dd561..5c554b586 100644
--- a/src/main/python/plotlyst/view/widget/scene/plot.py
+++ b/src/main/python/plotlyst/view/widget/scene/plot.py
@@ -23,19 +23,18 @@
import qtanim
from PyQt6.QtCore import Qt, pyqtSignal
from PyQt6.QtGui import QColor, QMouseEvent, QShowEvent
-from PyQt6.QtWidgets import QWidget, QToolButton, QTextEdit, QLabel
+from PyQt6.QtWidgets import QWidget, QToolButton, QTextEdit, QLabel, QPushButton
from overrides import overrides
-from qthandy import vbox, hbox, pointy, transparent, retain_when_hidden, spacer, sp, decr_icon, ask_confirmation, line
+from qthandy import vbox, hbox, transparent, retain_when_hidden, spacer, sp, decr_icon, line, pointy, underline
from qthandy.filter import OpacityEventFilter
from qtmenu import MenuWidget
from src.main.python.plotlyst.core.domain import Novel, Scene, ScenePlotReference, PlotValue, ScenePlotValueCharge, Plot
-from src.main.python.plotlyst.view.common import action, fade_out_and_gc
+from src.main.python.plotlyst.view.common import action
from src.main.python.plotlyst.view.icons import IconRegistry
-from src.main.python.plotlyst.view.style.base import apply_white_menu
-from src.main.python.plotlyst.view.widget.button import SecondaryActionToolButton, SecondaryActionPushButton
+from src.main.python.plotlyst.view.widget.button import SecondaryActionToolButton
from src.main.python.plotlyst.view.widget.display import IconText
-from src.main.python.plotlyst.view.widget.labels import PlotValueLabel, SelectionItemLabel, ScenePlotValueLabel
+from src.main.python.plotlyst.view.widget.labels import PlotValueLabel
class ScenePlotValueChargeWidget(QWidget):
@@ -157,68 +156,68 @@ def _commentChanged(self):
self.plotReference.data.comment = self.textComment.toPlainText()
-class ScenePlotSelector(QWidget):
- plotSelected = pyqtSignal()
+class ScenePlotSelectorMenu(MenuWidget):
+ plotSelected = pyqtSignal(Plot)
- def __init__(self, novel: Novel, scene: Scene, simplified: bool = False, parent=None):
- super(ScenePlotSelector, self).__init__(parent)
- self.novel = novel
- self.scene = scene
+ def __init__(self, novel: Novel, parent=None):
+ super().__init__(parent)
+ self._novel = novel
+ self._scene: Optional[Scene] = None
+
+ self.aboutToShow.connect(self._beforeShow)
+
+ def setScene(self, scene: Scene):
+ self._scene = scene
+
+ def _beforeShow(self):
+ if self._scene is None:
+ return
+ self.clear()
+ occupied_plot_ids = [x.plot.id for x in self._scene.plot_values]
+ self.addSection('Link storylines to this scene')
+ self.addSeparator()
+ for plot in self._novel.plots:
+ action_ = action(plot.text, IconRegistry.from_name(plot.icon, plot.icon_color),
+ partial(self.plotSelected.emit, plot))
+ if plot.id in occupied_plot_ids:
+ action_.setDisabled(True)
+ self.addAction(action_)
+
+
+class ScenePlotSelectorButton(QPushButton):
+ plotSelected = pyqtSignal(Plot)
+
+ def __init__(self, novel: Novel, parent=None):
+ super().__init__(parent)
+ self._novel = novel
+ self._scene: Optional[Scene] = None
self.plotValue: Optional[ScenePlotReference] = None
- hbox(self)
- self.label: Optional[SelectionItemLabel] = None
+ transparent(self)
+ self.setProperty('no-menu', True)
+ self.setText('Storyline')
- self.btnLinkPlot = SecondaryActionPushButton(self)
- if simplified:
- self.btnLinkPlot.setIcon(IconRegistry.plus_circle_icon('grey'))
- else:
- self.btnLinkPlot.setText('Associate plot')
- self.layout().addWidget(self.btnLinkPlot)
+ self.installEventFilter(OpacityEventFilter(parent=self, leaveOpacity=0.7))
- self.btnLinkPlot.installEventFilter(
- OpacityEventFilter(parent=self.btnLinkPlot, leaveOpacity=0.4 if simplified else 0.7))
+ if self._novel.plots:
+ pointy(self)
+ underline(self)
+ self._menu = ScenePlotSelectorMenu(self._novel, self)
+ self._menu.plotSelected.connect(self._plotSelected)
- self._menu = MenuWidget(self.btnLinkPlot)
- self._menu.aboutToShow.connect(self._beforeShow)
+ def setScene(self, scene: Scene):
+ self._scene = scene
+ self._menu.setScene(scene)
def setPlot(self, plotValue: ScenePlotReference):
self.plotValue = plotValue
- self.label = ScenePlotValueLabel(plotValue, self)
- pointy(self.label)
- self.label.clicked.connect(self._plotValueClicked)
-
- self.label.removalRequested.connect(self._remove)
- self.layout().addWidget(self.label)
- self.btnLinkPlot.setHidden(True)
+ self.setText(plotValue.plot.text)
+ underline(self, False)
def _plotSelected(self, plot: Plot):
plotValue = ScenePlotReference(plot)
- self.scene.plot_values.append(plotValue)
+ # TODO add back later
+ # self._scene.plot_values.append(plotValue)
self.setPlot(plotValue)
- self.plotSelected.emit()
- self._plotValueClicked()
-
- def _plotValueClicked(self):
- menu = MenuWidget(self.label)
- apply_white_menu(menu)
- menu.addWidget(ScenePlotValueEditor(self.plotValue))
- menu.exec()
-
- def _beforeShow(self):
- self._menu.clear()
- occupied_plot_ids = [x.plot.id for x in self.scene.plot_values]
- self._menu.addSection('Link plotlines to this scene')
- self._menu.addSeparator()
- for plot in self.novel.plots:
- action_ = action(plot.text, IconRegistry.from_name(plot.icon, plot.icon_color),
- partial(self._plotSelected, plot))
- if plot.id in occupied_plot_ids:
- action_.setDisabled(True)
- self._menu.addAction(action_)
-
- def _remove(self):
- if ask_confirmation(f"Remove scene association for plot '{self.plotValue.plot.text}'?"):
- self.scene.plot_values.remove(self.plotValue)
- fade_out_and_gc(self.parent(), self)
+ self.plotSelected.emit(plot)
diff --git a/ui/scene_editor.ui b/ui/scene_editor.ui
index eee1e3a97..57c981cb5 100644
--- a/ui/scene_editor.ui
+++ b/ui/scene_editor.ui
@@ -287,49 +287,6 @@
- -
-
-
-
- 0
- 0
-
-
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
- emo
-
-
-
- -
-
-
-
-
-
-