Make combo boxes fit translated content

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-08-22 21:28:19 +08:00
parent 1d750b2198
commit 429b470916
6 changed files with 149 additions and 27 deletions
+2 -1
View File
@@ -295,6 +295,7 @@ class NetworkStateBadge(Mixins.QTranslatable, Mixins.ThemeAware, QFrame):
"""Publish size constraints for the current responsive presentation."""
if self._profileDetailsVisible:
requiredWidth = self.widthWithProfile()
self.setMinimumWidth(requiredWidth)
self.setMaximumWidth(requiredWidth)
else:
@@ -751,8 +752,8 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
self.searchButton = SearchButton()
self.subscriptionFilterComboBox = AppQComboBox()
self.subscriptionFilterComboBox.setContentWidthAdjustable()
self.subscriptionFilterComboBox.setMinimumWidth(190)
self.subscriptionFilterComboBox.setMaximumWidth(300)
self.subscriptionFilterComboBox.setSizePolicy(
QSizePolicy.Policy.Preferred,
QSizePolicy.Policy.Fixed,
+1
View File
@@ -156,6 +156,7 @@ class LogPage(Mixins.QTranslatable, QMainWindow):
# metadata, so LogPage rebuilds this application-styled selector with
# the correct per-category translation policy.
self.filterComboBox = AppQComboBox(translatable=False)
self.filterComboBox.setContentWidthAdjustable()
self.filterComboBox.setMinimumWidth(180)
self.textBrowser = DraculaTextBrowser(
+5 -5
View File
@@ -94,12 +94,12 @@ class _TrafficMetricsCard(QFrame):
AppQLabel(_('Granularity'), parent=self),
)
self.timeRangeComboBox, self.granularityComboBox = (
AppQComboBox(parent=self),
AppQComboBox(parent=self),
)
self.timeRangeComboBox = AppQComboBox(parent=self)
self.timeRangeComboBox.setContentWidthAdjustable()
self.timeRangeComboBox.setMinimumWidth(160)
self.granularityComboBox = AppQComboBox(parent=self)
self.granularityComboBox.setContentWidthAdjustable()
self.granularityComboBox.setMinimumWidth(130)
self.downloadTitleLabel, self.uploadTitleLabel = (
+2
View File
@@ -469,6 +469,7 @@ class _ApplicationThemeSettingsCard(_SettingsCard):
"""Initialize the translated application-theme selector."""
self.comboBox = AppQComboBox()
self.comboBox.setObjectName('SettingsComboBox')
self.comboBox.setContentWidthAdjustable()
self.comboBox.setMinimumWidth(260)
for label, preference in self.Options:
@@ -522,6 +523,7 @@ class _SystemProxySettingsCard(_SettingsCard):
"""Initialize the translated system-proxy mode selector."""
self.comboBox = AppQComboBox()
self.comboBox.setObjectName('SettingsComboBox')
self.comboBox.setContentWidthAdjustable()
self.comboBox.setMinimumWidth(260)
for label, mode in self.Options:
+46 -19
View File
@@ -84,22 +84,32 @@ class _SubscriptionEditorDialog(AppQTransientDialog):
subscription = dict(subscription or {})
self.remarkEdit = AppQLineEdit(subscription.get('remark', ''))
self.urlEdit = AppQLineEdit(subscription.get('webURL', ''))
self.enabledSwitch = AppQSwitch()
self.enabledSwitch.syncChecked(subscription.get('enabled', True))
self.autoUpdateComboBox = AppQComboBox()
self.proxyComboBox = AppQComboBox()
self.userAgentEdit = AppQLineEdit(subscription.get('userAgent', ''))
self.filterEdit = AppQLineEdit(subscription.get('filter', ''))
(
self.remarkEdit,
self.urlEdit,
self.enabledSwitch,
self.autoUpdateComboBox,
self.proxyComboBox,
self.userAgentEdit,
self.filterEdit,
) = (
AppQLineEdit(subscription.get('remark', '')),
AppQLineEdit(subscription.get('webURL', '')),
AppQSwitch(),
AppQComboBox(),
AppQComboBox(),
AppQLineEdit(subscription.get('userAgent', '')),
AppQLineEdit(subscription.get('filter', '')),
)
self.remarkEdit.setMinimumWidth(240)
self.remarkEdit.setMaximumWidth(360)
self.urlEdit.setMinimumWidth(420)
self.enabledSwitch.syncChecked(subscription.get('enabled', True))
self.autoUpdateComboBox.setMinimumWidth(220)
self.autoUpdateComboBox.setMaximumWidth(320)
self.autoUpdateComboBox.setContentWidthAdjustable()
self.proxyComboBox.setMinimumWidth(220)
self.proxyComboBox.setMaximumWidth(320)
self.proxyComboBox.setContentWidthAdjustable()
self.userAgentEdit.setMinimumWidth(260)
self.filterEdit.setMinimumWidth(260)
@@ -109,8 +119,10 @@ class _SubscriptionEditorDialog(AppQTransientDialog):
for value in SubscriptionTableView.ProxyOptions:
self.proxyComboBox.addItem(_(value), value)
autoIndex = self.autoUpdateComboBox.findData(subscription.get('autoupdate', ''))
proxyIndex = self.proxyComboBox.findData(subscription.get('proxy', ''))
autoIndex, proxyIndex = (
self.autoUpdateComboBox.findData(subscription.get('autoupdate', '')),
self.proxyComboBox.findData(subscription.get('proxy', '')),
)
self.autoUpdateComboBox.setCurrentIndex(max(autoIndex, 0))
self.proxyComboBox.setCurrentIndex(max(proxyIndex, 0))
@@ -236,20 +248,35 @@ class SubscriptionPage(Mixins.QTranslatable, Mixins.ThemeAware, QMainWindow):
super().__init__(parent)
self.setObjectName('SubscriptionPage')
self.serverTable = serverTable
self.pageTitleLabel = AppQLabel(_('Subscriptions'))
self.pageTitleLabel.setObjectName('SubscriptionPageTitle')
self.proxyLabel = AppQLabel(_('Update Using'))
self.proxyComboBox = AppQComboBox()
self.proxyComboBox.setContentWidthAdjustable()
self.proxyComboBox.setMinimumWidth(160)
self.addButton = AppQPushButton(_('Add'))
self.editButton = AppQPushButton(_('Edit'))
self.deleteButton = AppQPushButton(_('Delete'))
self.copyURLButton = AppQPushButton(_('Copy URL'))
self.viewProfilesButton = AppQPushButton(_('View Profiles'))
self.updateSelectedButton = AppQPushButton(_('Update Selected'))
self.updateAllButton = AppQPushButton(_('Update All'))
(
self.addButton,
self.editButton,
self.deleteButton,
self.copyURLButton,
self.viewProfilesButton,
self.updateSelectedButton,
self.updateAllButton,
) = (
AppQPushButton(_('Add')),
AppQPushButton(_('Edit')),
AppQPushButton(_('Delete')),
AppQPushButton(_('Copy URL')),
AppQPushButton(_('View Profiles')),
AppQPushButton(_('Update Selected')),
AppQPushButton(_('Update All')),
)
self.table = SubscriptionTableView(
deleteUniqueCallback=self._deleteProfilesForSubscription,
+93 -2
View File
@@ -55,10 +55,17 @@ from Furious.Controllers.ConnectionController import (
ConnectionState,
)
from Furious.Controllers.SettingsController import (
APPLICATION_THEME_SETTING,
LOG_AUTO_CLEAR_SETTING,
LOG_AUTO_SCROLL_DOWN_SETTING,
)
from Furious.Frozenlib import APPLICATION_NAME, AppSettings, Mixins
from Furious.Frozenlib import (
APPLICATION_NAME,
AppBuiltinProxyMode,
ApplicationTheme,
AppSettings,
Mixins,
)
from Furious.Models import ProfileMetadata, Protocol, ServerProfile
from Furious.Plugins.API import RoutingOption
from Furious.Qt import (
@@ -76,13 +83,23 @@ from Furious.Service import (
LogManager,
)
from Furious.Window.LogPage import LogPage
from Furious.Window.SettingsPage import (
_ApplicationThemeSettingsCard,
_SystemProxySettingsCard,
)
from Furious.Window.SubscriptionPage import _SubscriptionEditorDialog
from Furious.Widget.ConnectionButton import ConnectionButton
from Furious.Widget.RoutingSelector import RoutingSelector
from PySide6 import QtCore
from PySide6.QtTest import QTest
from PySide6.QtWidgets import QHBoxLayout, QWidget
from PySide6.QtWidgets import (
QComboBox,
QHBoxLayout,
QStyle,
QStyleOptionComboBox,
QWidget,
)
from tests.support import (
application,
@@ -144,6 +161,22 @@ class ComboTranslationLayoutTest(unittest.TestCase):
"""Finish deferred widget deletion between tests."""
collectAtBoundary()
def assertCurrentComboTextFits(self, comboBox):
"""Assert the styled combo edit field can display its current text."""
option = QStyleOptionComboBox()
comboBox.initStyleOption(option)
textRect = comboBox.style().subControlRect(
QStyle.ComplexControl.CC_ComboBox,
option,
QStyle.SubControl.SC_ComboBoxEditField,
comboBox,
)
self.assertGreaterEqual(
textRect.width(),
comboBox.fontMetrics().horizontalAdvance(comboBox.currentText()),
)
def testContentAwareComboRefreshesHintWithoutChangingSemanticData(self):
"""Grow for longer translated text while retaining item identity."""
parent = QWidget()
@@ -223,6 +256,64 @@ class ComboTranslationLayoutTest(unittest.TestCase):
parent.close()
parent.deleteLater()
def testTranslatedSettingsChoicesResizeWithoutChangingSemanticValues(self):
"""Resize translated Settings choices without applying fake changes."""
cases = (
(
_ApplicationThemeSettingsCard,
APPLICATION_THEME_SETTING,
ApplicationTheme.System.value,
'Follow System Appearance',
'setApplicationTheme',
),
(
_SystemProxySettingsCard,
'SystemProxyMode',
AppBuiltinProxyMode.Auto.value,
'Automatically Configure System Proxy',
'setSystemProxyMode',
),
)
with isolatedSettings(), mock.patch(
'Furious.Window.SettingsPage.AppSettingsController'
) as controllerFactory:
for cardType, settingName, semanticValue, sourceText, callbackName in cases:
AppSettings.set('Language', 'EN')
AppSettings.set(settingName, semanticValue)
parent = QWidget()
layout = QHBoxLayout(parent)
card = cardType(parent=parent)
layout.addWidget(card)
parent.resize(1800, 120)
parent.show()
processQtEvents()
comboBox = card.comboBox
callback = getattr(controllerFactory.return_value, callbackName)
indexChanges = []
comboBox.currentIndexChanged.connect(indexChanges.append)
self.assertEqual(
comboBox.sizeAdjustPolicy(),
QComboBox.SizeAdjustPolicy.AdjustToContents,
)
for language in ('EN', 'ZH', 'RU'):
AppSettings.set('Language', language)
comboBox.retranslate()
processQtEvents()
self.assertEqual(comboBox.currentData(), semanticValue)
self.assertEqual(comboBox.currentText(), _(sourceText, language))
self.assertCurrentComboTextFits(comboBox)
callback.assert_not_called()
self.assertEqual(indexChanges, [])
parent.close()
parent.deleteLater()
class EditorMappingTest(unittest.TestCase):
"""Verify editor fields preserve structured configuration semantics."""