Skip to content

Commit

Permalink
Remove "Apply" buttons in Project Settings (#2151)
Browse files Browse the repository at this point in the history
  • Loading branch information
vkbo authored Dec 30, 2024
2 parents c39a5f9 + 4a6511b commit 33aea82
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 97 deletions.
155 changes: 75 additions & 80 deletions novelwriter/dialogs/projectsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from PyQt5.QtGui import QCloseEvent, QColor
from PyQt5.QtWidgets import (
QAbstractItemView, QApplication, QColorDialog, QDialogButtonBox,
QHBoxLayout, QLineEdit, QMenu, QStackedWidget, QToolButton, QTreeWidget,
QHBoxLayout, QLineEdit, QMenu, QStackedWidget, QTreeWidget,
QTreeWidgetItem, QVBoxLayout, QWidget
)

Expand Down Expand Up @@ -352,17 +352,17 @@ def __init__(self, parent: QWidget, isStatus: bool) -> None:
self.listBox.setIndentation(0)
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.listBox.itemSelectionChanged.connect(self._selectionChanged)
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)

for key, entry in status.iterItems():
self._addItem(key, StatusEntry.duplicate(entry))

# List Controls
self.addButton = NIconToolButton(self, iSz, "add")
self.addButton.clicked.connect(self._newItem)
self.addButton.clicked.connect(self._onItemCreate)

self.delButton = NIconToolButton(self, iSz, "remove")
self.delButton.clicked.connect(self._delItem)
self.delButton.clicked.connect(self._onItemDelete)

self.upButton = NIconToolButton(self, iSz, "up")
self.upButton.clicked.connect(qtLambda(self._moveItem, -1))
Expand All @@ -371,10 +371,11 @@ def __init__(self, parent: QWidget, isStatus: bool) -> None:
self.dnButton.clicked.connect(qtLambda(self._moveItem, 1))

# Edit Form
self.editName = QLineEdit(self)
self.editName.setMaxLength(40)
self.editName.setPlaceholderText(self.tr("Select item to edit"))
self.editName.setEnabled(False)
self.labelText = QLineEdit(self)
self.labelText.setMaxLength(40)
self.labelText.setPlaceholderText(self.tr("Select item to edit"))
self.labelText.setEnabled(False)
self.labelText.textEdited.connect(self._onNameEdit)

buttonStyle = (
f"QToolButton {{padding: 0 {bPd}px;}} "
Expand All @@ -386,13 +387,13 @@ def __init__(self, parent: QWidget, isStatus: bool) -> None:
self.colorButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
self.colorButton.setStyleSheet(buttonStyle)
self.colorButton.setEnabled(False)
self.colorButton.clicked.connect(self._selectColour)
self.colorButton.clicked.connect(self._onColourSelect)

def buildMenu(menu: QMenu, items: dict[nwStatusShape, str]) -> None:
for shape, label in items.items():
icon = NWStatus.createIcon(self._iPx, iColor, shape)
action = menu.addAction(icon, trConst(label))
action.triggered.connect(lambda _, shape=shape: self._selectShape(shape))
action.triggered.connect(qtLambda(self._selectShape, shape))
self._icons[shape] = icon

self.shapeMenu = QMenu(self)
Expand All @@ -408,12 +409,6 @@ def buildMenu(menu: QMenu, items: dict[nwStatusShape, str]) -> None:
self.shapeButton.setStyleSheet(buttonStyle)
self.shapeButton.setEnabled(False)

self.applyButton = QToolButton(self)
self.applyButton.setText(self.tr("Apply"))
self.applyButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
self.applyButton.setEnabled(False)
self.applyButton.clicked.connect(self._applyChanges)

# Assemble
self.listControls = QVBoxLayout()
self.listControls.addWidget(self.addButton)
Expand All @@ -423,10 +418,9 @@ def buildMenu(menu: QMenu, items: dict[nwStatusShape, str]) -> None:
self.listControls.addStretch(1)

self.editBox = QHBoxLayout()
self.editBox.addWidget(self.editName, 1)
self.editBox.addWidget(self.labelText, 1)
self.editBox.addWidget(self.colorButton, 0)
self.editBox.addWidget(self.shapeButton, 0)
self.editBox.addWidget(self.applyButton, 0)

self.mainBox = QVBoxLayout()
self.mainBox.addWidget(self.listBox, 1)
Expand Down Expand Up @@ -474,16 +468,28 @@ def columnWidth(self) -> int:
# Private Slots
##

@pyqtSlot(str)
def _onNameEdit(self, text: str) -> None:
"""Update the status label text."""
if item := self._getSelectedItem():
name = simplified(text)
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
entry.name = name
item.setText(self.C_LABEL, name)
self._changed = True
return

@pyqtSlot()
def _selectColour(self) -> None:
def _onColourSelect(self) -> None:
"""Open a dialog to select the status icon colour."""
if (color := QColorDialog.getColor(self._color, self, self.trSelColor)).isValid():
self._color = color
self._setButtonIcons()
self._updateIcon()
return

@pyqtSlot()
def _newItem(self) -> None:
def _onItemCreate(self) -> None:
"""Create a new status item."""
color = QColor(100, 100, 100)
shape = nwStatusShape.SQUARE
Expand All @@ -493,7 +499,7 @@ def _newItem(self) -> None:
return

@pyqtSlot()
def _delItem(self) -> None:
def _onItemDelete(self) -> None:
"""Delete a status item."""
if item := self._getSelectedItem():
iRow = self.listBox.indexOfTopLevelItem(item)
Expand All @@ -506,28 +512,7 @@ def _delItem(self) -> None:
return

@pyqtSlot()
def _applyChanges(self) -> None:
"""Save changes made to a status item."""
if item := self._getSelectedItem():
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)

name = simplified(self.editName.text())
icon = NWStatus.createIcon(self._iPx, self._color, self._shape)

entry.name = name
entry.color = self._color
entry.shape = self._shape
entry.icon = icon

item.setText(self.C_LABEL, name)
item.setIcon(self.C_LABEL, icon)

self._changed = True

return

@pyqtSlot()
def _selectionChanged(self) -> None:
def _onSelectionChanged(self) -> None:
"""Extract the info of a selected item and populate the settings
boxes and button. If no item is selected, clear the form.
"""
Expand All @@ -537,25 +522,22 @@ def _selectionChanged(self) -> None:
self._shape = entry.shape
self._setButtonIcons()

self.editName.setText(entry.name)
self.editName.selectAll()
self.editName.setFocus()
self.labelText.setText(entry.name)
self.labelText.selectAll()
self.labelText.setFocus()

self.editName.setEnabled(True)
self.labelText.setEnabled(True)
self.colorButton.setEnabled(True)
self.shapeButton.setEnabled(True)
self.applyButton.setEnabled(True)

else:
self._color = QColor(100, 100, 100)
self._shape = nwStatusShape.SQUARE
self._setButtonIcons()
self.editName.setText("")
self.labelText.setText("")

self.editName.setEnabled(False)
self.labelText.setEnabled(False)
self.colorButton.setEnabled(False)
self.shapeButton.setEnabled(False)
self.applyButton.setEnabled(False)
return

##
Expand All @@ -566,6 +548,19 @@ def _selectShape(self, shape: nwStatusShape) -> None:
"""Set the current shape."""
self._shape = shape
self._setButtonIcons()
self._updateIcon()
return

def _updateIcon(self) -> None:
"""Apply changes made to a status icon."""
if item := self._getSelectedItem():
icon = NWStatus.createIcon(self._iPx, self._color, self._shape)
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
entry.color = self._color
entry.shape = self._shape
entry.icon = icon
item.setIcon(self.C_LABEL, icon)
self._changed = True
return

def _addItem(self, key: str | None, entry: StatusEntry) -> None:
Expand Down Expand Up @@ -644,7 +639,7 @@ def __init__(self, parent: QWidget) -> None:
self.listBox.setIndentation(0)
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.listBox.itemSelectionChanged.connect(self._selectionChanged)
self.listBox.itemSelectionChanged.connect(self._onSelectionChanged)

for aKey, aVal in SHARED.project.data.autoReplace.items():
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
Expand All @@ -655,25 +650,22 @@ def __init__(self, parent: QWidget) -> None:

# List Controls
self.addButton = NIconToolButton(self, iSz, "add")
self.addButton.clicked.connect(self._addEntry)
self.addButton.clicked.connect(self._onEntryCreated)

self.delButton = NIconToolButton(self, iSz, "remove")
self.delButton.clicked.connect(self._delEntry)
self.delButton.clicked.connect(self._onEntryDeleted)

# Edit Form
self.editKey = QLineEdit(self)
self.editKey.setPlaceholderText(self.tr("Select item to edit"))
self.editKey.setEnabled(False)
self.editKey.setMaxLength(40)
self.editKey.textEdited.connect(self._onKeyEdit)

self.editValue = QLineEdit(self)
self.editValue.setEnabled(False)
self.editValue.setMaxLength(80)

self.applyButton = QToolButton(self)
self.applyButton.setText(self.tr("Apply"))
self.applyButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
self.applyButton.clicked.connect(self._applyChanges)
self.editValue.setMaxLength(250)
self.editValue.textEdited.connect(self._onValueEdit)

# Assemble
self.listControls = QVBoxLayout()
Expand All @@ -684,7 +676,6 @@ def __init__(self, parent: QWidget) -> None:
self.editBox = QHBoxLayout()
self.editBox.addWidget(self.editKey, 4)
self.editBox.addWidget(self.editValue, 5)
self.editBox.addWidget(self.applyButton, 0)

self.mainBox = QVBoxLayout()
self.mainBox.addWidget(self.listBox, 1)
Expand Down Expand Up @@ -716,7 +707,7 @@ def getNewList(self) -> dict[str, str]:
new = {}
for n in range(self.listBox.topLevelItemCount()):
if item := self.listBox.topLevelItem(n):
if key := self._stripNotAllowed(item.text(self.C_KEY)):
if key := self._stripKey(item.text(self.C_KEY)):
new[key] = item.text(self.C_REPL)
return new

Expand All @@ -728,13 +719,29 @@ def columnWidth(self) -> int:
# Private Slots
##

@pyqtSlot(str)
def _onKeyEdit(self, text: str) -> None:
"""Update the key text."""
if (item := self._getSelectedItem()) and (key := self._stripKey(text)):
item.setText(self.C_KEY, f"<{key}>")
self._changed = True
return

@pyqtSlot(str)
def _onValueEdit(self, text: str) -> None:
"""Update the value text."""
if item := self._getSelectedItem():
item.setText(self.C_REPL, text)
self._changed = True
return

@pyqtSlot()
def _selectionChanged(self) -> None:
def _onSelectionChanged(self) -> None:
"""Extract the details from the selected item and populate the
edit form.
"""
if item := self._getSelectedItem():
self.editKey.setText(self._stripNotAllowed(item.text(self.C_KEY)))
self.editKey.setText(self._stripKey(item.text(self.C_KEY)))
self.editValue.setText(item.text(self.C_REPL))
self.editKey.setEnabled(True)
self.editValue.setEnabled(True)
Expand All @@ -748,26 +755,14 @@ def _selectionChanged(self) -> None:
return

@pyqtSlot()
def _applyChanges(self) -> None:
"""Save the form data into the list widget."""
if item := self._getSelectedItem():
key = self._stripNotAllowed(self.editKey.text())
value = self.editValue.text()
if key and value:
item.setText(self.C_KEY, f"<{key}>")
item.setText(self.C_REPL, value)
self._changed = True
return

@pyqtSlot()
def _addEntry(self) -> None:
def _onEntryCreated(self) -> None:
"""Add a new list entry."""
key = f"<keyword{self.listBox.topLevelItemCount() + 1:d}>"
self.listBox.addTopLevelItem(QTreeWidgetItem([key, ""]))
return

@pyqtSlot()
def _delEntry(self) -> None:
def _onEntryDeleted(self) -> None:
"""Delete the selected entry."""
if item := self._getSelectedItem():
self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(item))
Expand All @@ -784,6 +779,6 @@ def _getSelectedItem(self) -> QTreeWidgetItem | None:
return items[0]
return None

def _stripNotAllowed(self, key: str) -> str:
def _stripKey(self, key: str) -> str:
"""Clean up the replace key string."""
return "".join(c for c in key if c.isalnum())
4 changes: 0 additions & 4 deletions novelwriter/guimain.py
Original file line number Diff line number Diff line change
Expand Up @@ -957,12 +957,8 @@ def _appFocusChanged(self, old: QWidget, new: QWidget) -> None:
docEditor = True
elif self.docViewer.isAncestorOf(new):
docViewer = True

self.docEditor.changeFocusState(docEditor)
self.docViewer.changeFocusState(docViewer)

logger.debug("Main focus switched to: %s", type(new).__name__)

return

@pyqtSlot(bool)
Expand Down
18 changes: 5 additions & 13 deletions tests/test_dialogs/test_dlg_projectsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
status.addButton.click()
status.listBox.setCurrentItem(status.listBox.topLevelItem(3))
status.editName.setText("Final")
status._onNameEdit("Final")
status.colorButton.click()
status._selectShape(nwStatusShape.CIRCLE)
status.applyButton.click()
assert status.listBox.topLevelItemCount() == 4

assert status.changed is True
Expand Down Expand Up @@ -274,10 +273,9 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
importance.addButton.click()
importance.listBox.clearSelection()
importance.listBox.setCurrentItem(importance.listBox.topLevelItem(3))
importance.editName.setText("Final")
importance._onNameEdit("Final")
importance.colorButton.click()
importance._selectShape(nwStatusShape.TRIANGLE)
importance.applyButton.click()
assert importance.listBox.topLevelItemCount() == 4

assert importance.changed is True
Expand Down Expand Up @@ -351,8 +349,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):

# Nothing to save or delete
replace.listBox.clearSelection()
replace._applyChanges()
replace._delEntry()
replace._onEntryDeleted()
assert replace.listBox.topLevelItemCount() == 2

# Create a new entry
Expand All @@ -363,13 +360,8 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):

# Edit the entry
replace.listBox.setCurrentItem(replace.listBox.topLevelItem(2))
replace.editKey.setText("")
for c in "Th is ":
qtbot.keyClick(replace.editKey, c, delay=KEY_DELAY)
replace.editValue.setText("")
for c in "With This Stuff ":
qtbot.keyClick(replace.editValue, c, delay=KEY_DELAY)
qtbot.mouseClick(replace.applyButton, QtMouseLeft)
replace._onKeyEdit("Th is ")
replace._onValueEdit("With This Stuff ")
assert replace.listBox.topLevelItem(2).text(0) == "<This>" # type: ignore
assert replace.listBox.topLevelItem(2).text(1) == "With This Stuff " # type: ignore

Expand Down

0 comments on commit 33aea82

Please sign in to comment.