mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-09-22 15:04:34 +03:00
Unify search clear button styling
Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
@@ -278,6 +278,7 @@ class AppStyleSheet:
|
||||
caretUpIcon = f'{iconPrefix}/caret-up-fill.svg'
|
||||
caretRightIcon = f'{iconPrefix}/caret-right-fill.svg'
|
||||
checkIcon = ':/Icons/bootstrap/white/check.svg'
|
||||
clearIcon = f'{iconPrefix}/x.svg'
|
||||
|
||||
return composeStyleSheet(
|
||||
palette,
|
||||
@@ -286,4 +287,5 @@ class AppStyleSheet:
|
||||
caretUpIcon,
|
||||
caretRightIcon,
|
||||
checkIcon,
|
||||
clearIcon,
|
||||
)
|
||||
|
||||
+24
-1
@@ -25,7 +25,7 @@ from Furious.Qt.AppStyleSheet import *
|
||||
from Furious.Qt.DynamicTheme import *
|
||||
from Furious.Qt.DynamicTranslate import gettext as _
|
||||
from Furious.Qt.QtGui import *
|
||||
from Furious.Qt.Signals import connectWeakly
|
||||
from Furious.Qt.Signals import connectWeakly, singleShotWeakly
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import *
|
||||
@@ -625,8 +625,31 @@ class AppQLineEdit(Mixins.QTranslatable, QLineEdit):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize the AppQLineEdit."""
|
||||
self._clearIconRefreshPending = False
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def changeEvent(self, event):
|
||||
"""Refresh the cached native icon after Qt finishes applying its style."""
|
||||
super().changeEvent(event)
|
||||
|
||||
if (
|
||||
event.type() == QtCore.QEvent.Type.StyleChange
|
||||
and self.isClearButtonEnabled()
|
||||
and not self._clearIconRefreshPending
|
||||
):
|
||||
self._clearIconRefreshPending = True
|
||||
|
||||
singleShotWeakly(0, self, '_refreshClearIcon')
|
||||
|
||||
def _refreshClearIcon(self):
|
||||
"""Recreate the native action outside Qt's child-polishing traversal."""
|
||||
self._clearIconRefreshPending = False
|
||||
|
||||
if self.isClearButtonEnabled():
|
||||
self.setClearButtonEnabled(False)
|
||||
self.setClearButtonEnabled(True)
|
||||
|
||||
def retranslate(self):
|
||||
"""Refresh translated text for the app q line edit."""
|
||||
self.setPlaceholderText(_(self.placeholderText()))
|
||||
|
||||
@@ -26,6 +26,7 @@ def controlStyleSheet(
|
||||
caretUpIcon,
|
||||
caretRightIcon,
|
||||
checkIcon,
|
||||
clearIcon,
|
||||
):
|
||||
"""Return interactive control styling."""
|
||||
return dedent(f"""
|
||||
@@ -361,6 +362,33 @@ def controlStyleSheet(
|
||||
selection-color: {palette['selection_text']};
|
||||
}}
|
||||
|
||||
QLineEdit {{
|
||||
lineedit-clear-button-icon: url("{clearIcon}");
|
||||
}}
|
||||
|
||||
/* Embedded actions keep Qt's geometry, without toolbar padding. */
|
||||
QLineEdit QToolButton {{
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background-color: transparent;
|
||||
}}
|
||||
|
||||
QLineEdit QToolButton:hover {{
|
||||
background-color: {palette['hover']};
|
||||
}}
|
||||
|
||||
QLineEdit QToolButton:pressed {{
|
||||
background-color: {palette['pressed']};
|
||||
}}
|
||||
|
||||
QLineEdit QToolButton:disabled {{
|
||||
background-color: transparent;
|
||||
}}
|
||||
|
||||
QLineEdit:hover,
|
||||
QTextEdit:hover,
|
||||
QPlainTextEdit:hover,
|
||||
|
||||
@@ -29,6 +29,7 @@ def composeStyleSheet(
|
||||
caretUpIcon,
|
||||
caretRightIcon,
|
||||
checkIcon,
|
||||
clearIcon,
|
||||
):
|
||||
"""Return the complete stylesheet without exposing component fragments."""
|
||||
return '\n\n'.join(
|
||||
@@ -40,6 +41,7 @@ def composeStyleSheet(
|
||||
caretUpIcon,
|
||||
caretRightIcon,
|
||||
checkIcon,
|
||||
clearIcon,
|
||||
),
|
||||
dataViewStyleSheet(palette, progressBarStyleSheet, checkIcon),
|
||||
)
|
||||
|
||||
@@ -19,15 +19,15 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Qt import AppStyleSheet
|
||||
from Furious.Qt import AppQLineEdit, AppStyleSheet
|
||||
from Furious.Widget.NavigationView import NavigationView
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtGui import QColor, QPalette
|
||||
from PySide6.QtTest import QTest
|
||||
from PySide6.QtWidgets import QPushButton, QStyleOptionButton, QWidget
|
||||
from PySide6.QtWidgets import QPushButton, QStyleOptionButton, QToolButton, QWidget
|
||||
|
||||
from tests.support import application, collectAtBoundary, processQtEvents
|
||||
from tests.support import application, collectAtBoundary, processQtEvents, waitFor
|
||||
|
||||
import unittest
|
||||
|
||||
@@ -84,6 +84,74 @@ class StyleSheetStateRenderingTest(unittest.TestCase):
|
||||
|
||||
return color
|
||||
|
||||
def testClearButtonKeepsNativeBehaviorAndGeometryAcrossThemes(self):
|
||||
"""Keep embedded buttons centered, theme-correct and bounded after restyling."""
|
||||
app = application()
|
||||
originalStyleSheet = app.styleSheet()
|
||||
edit = AppQLineEdit()
|
||||
edit.setClearButtonEnabled(True)
|
||||
edit.resize(340, 36)
|
||||
edit.show()
|
||||
edit.activateWindow()
|
||||
edit.setFocus()
|
||||
try:
|
||||
for theme in (AppStyleSheet.Light, AppStyleSheet.Dark) * 3:
|
||||
for direction in (QtCore.Qt.LeftToRight, QtCore.Qt.RightToLeft):
|
||||
with self.subTest(theme=theme, direction=direction):
|
||||
edit.setLayoutDirection(direction)
|
||||
edit.setText('example search')
|
||||
edit.setSelection(0, 7)
|
||||
app.setStyleSheet(AppStyleSheet.forTheme(theme))
|
||||
processQtEvents()
|
||||
self.assertEqual(edit.selectedText(), 'example')
|
||||
buttons = edit.findChildren(QToolButton)
|
||||
self.assertEqual(len(buttons), 1)
|
||||
button = buttons[0]
|
||||
self.assertTrue(edit.rect().contains(button.geometry()))
|
||||
self.assertLessEqual(
|
||||
abs(
|
||||
button.geometry().center().y()
|
||||
- edit.rect().center().y()
|
||||
),
|
||||
1,
|
||||
)
|
||||
self.assertFalse(button.icon().isNull())
|
||||
pixmap = button.icon().pixmap(16, 16).toImage()
|
||||
colors = [
|
||||
pixmap.pixelColor(x, y)
|
||||
for x in range(pixmap.width())
|
||||
for y in range(pixmap.height())
|
||||
if pixmap.pixelColor(x, y).alpha() > 128
|
||||
]
|
||||
self.assertTrue(colors)
|
||||
self.assertTrue(
|
||||
all(
|
||||
(color.lightness() > 128)
|
||||
== (theme == AppStyleSheet.Dark)
|
||||
for color in colors
|
||||
)
|
||||
)
|
||||
QTest.mouseClick(button, QtCore.Qt.LeftButton)
|
||||
self.assertEqual(edit.text(), '')
|
||||
self.assertTrue(edit.hasFocus())
|
||||
self.assertTrue(waitFor(lambda: not button.isVisible()))
|
||||
edit.setText('read only')
|
||||
edit.setReadOnly(True)
|
||||
app.setStyleSheet(AppStyleSheet.forTheme(AppStyleSheet.Light))
|
||||
processQtEvents()
|
||||
self.assertEqual(edit.text(), 'read only')
|
||||
self.assertFalse(edit.findChild(QToolButton).isEnabled())
|
||||
app.setStyleSheet(AppStyleSheet.forTheme(AppStyleSheet.Dark))
|
||||
edit.setClearButtonEnabled(False)
|
||||
processQtEvents()
|
||||
self.assertFalse(edit.isClearButtonEnabled())
|
||||
self.assertFalse(edit.findChildren(QToolButton))
|
||||
finally:
|
||||
edit.close()
|
||||
edit.deleteLater()
|
||||
processQtEvents()
|
||||
app.setStyleSheet(originalStyleSheet)
|
||||
|
||||
def testFlatAndLinkButtonsRetainFocusedOutlineDuringHover(self):
|
||||
"""Do not let flat presentation clear the keyboard-focus frame."""
|
||||
app = application()
|
||||
|
||||
Reference in New Issue
Block a user