Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove "Apply" buttons in Project Settings #2151

Merged
merged 4 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading