mirror of
https://github.com/Windscribe/Desktop-App.git
synced 2026-05-07 20:12:44 +00:00
v2.22.5
This commit is contained in:
+1
-1
@@ -12,7 +12,7 @@ variables:
|
||||
BUILD_LINUX_CLI: 'y'
|
||||
GIT_DEPTH: 5 # Only grab the last 5 commits when cloning
|
||||
NEXUS_PATH_ROOT: 'https://nexus.int.windscribe.com/repository/client-desktop/client-desktop'
|
||||
NEXUS_PATH_DEPS: '$NEXUS_PATH_ROOT/dependencies/current'
|
||||
NEXUS_PATH_DEPS: '$NEXUS_PATH_ROOT/dependencies/2.22'
|
||||
NEXUS_PATH_VCPKG_CACHE: '$NEXUS_PATH_ROOT/vcpkg_cache/current'
|
||||
NEXUS_PATH_BRANCH_UPLOAD: '${NEXUS_PATH_ROOT}/branches/${CI_COMMIT_BRANCH}'
|
||||
NEXUS_PATH_TAGGED_UPLOAD: '${NEXUS_PATH_ROOT}/tagged-builds'
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
2.22.5 (15/04/2026)
|
||||
All:
|
||||
* Fixed dropdown menus in Preferences scrolling to the wrong position. #1692
|
||||
* Fixed El Salvador flag icon. #1692
|
||||
* Fixed app does not consistently connect to the specific server that has the user's pinned IP. #1734
|
||||
* Fixed purchased ALC locations cannot be selected. #1753
|
||||
macOS:
|
||||
* Fixed unnecessary VPN reconnection when switching between WiFi access points on the same network. #1708
|
||||
* Fixed a crash when deleting a session. #1751
|
||||
* Fixed a crash that could occur while reinstalling the app. #1752
|
||||
* Fixed a crash when interacting with credential fields. #1748
|
||||
Linux:
|
||||
* Fixed unnecessary VPN reconnection when switching between WiFi access points on the same network. #1708
|
||||
|
||||
|
||||
2.22.4 (10/04/2026)
|
||||
All:
|
||||
* Improved WireGuard PersistentKeepalive to use a value of 25 on all platforms. #1741
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
set(WS_VERSION_MAJOR 2)
|
||||
set(WS_VERSION_MINOR 22)
|
||||
set(WS_VERSION_BUILD 4)
|
||||
set(WS_VERSION_BUILD 5)
|
||||
set(WS_BUILD_TYPE "guinea_pig")
|
||||
|
||||
# WS_APP_IDENTIFIER: Internal identifier, no spaces. Used for service names,
|
||||
|
||||
@@ -108,7 +108,6 @@ SessionStatus::SessionStatus(const std::string &json) : d(new SessionStatusData)
|
||||
d->alc_ << v.toString();
|
||||
}
|
||||
}
|
||||
|
||||
d->staticIpsUpdateDevices_.clear();
|
||||
if (jsonData.contains("sip"))
|
||||
{
|
||||
|
||||
@@ -133,18 +133,12 @@ int Utils::generateIntegerRandom(const int &min, const int &max)
|
||||
|
||||
bool Utils::isSubdomainsEqual(const QString &hostname1, const QString &hostname2)
|
||||
{
|
||||
// Compare only the subdomain portion so pinned IP matching still works when the server TLD changes.
|
||||
int i1 = hostname1.indexOf('.');
|
||||
int i2 = hostname2.indexOf('.');
|
||||
if (i1 != -1 && i2 != -1)
|
||||
{
|
||||
QString sub1 = hostname1.mid(0, i1);
|
||||
QString sub2 = hostname2.mid(0, i2);
|
||||
return sub1 == sub2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QString sub1 = (i1 != -1) ? hostname1.left(i1) : hostname1;
|
||||
QString sub2 = (i2 != -1) ? hostname2.left(i2) : hostname2;
|
||||
return !sub1.isEmpty() && sub1 == sub2;
|
||||
}
|
||||
|
||||
std::wstring Utils::getDirPathFromFullPath(const std::wstring &fullPath)
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#include "helperbackend_mac.h"
|
||||
#include <QMetaObject>
|
||||
#include <QMutexLocker>
|
||||
#include "installhelper_mac.h"
|
||||
|
||||
HelperBackend_mac::HelperBackend_mac(QObject *parent, spdlog::logger *logger) :
|
||||
IHelperBackend(parent), connection_(nullptr), logger_(logger)
|
||||
IHelperBackend(parent), logger_(logger)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HelperBackend_mac::~HelperBackend_mac()
|
||||
{
|
||||
curState_ = State::kInit;
|
||||
if (connection_) {
|
||||
xpc_connection_set_event_handler(connection_, ^(xpc_object_t) {});
|
||||
xpc_connection_cancel(connection_);
|
||||
dispatch_sync(queue_, ^{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +23,8 @@ void HelperBackend_mac::startInstallHelper(bool bForceDeleteOld)
|
||||
bool isUserCanceled;
|
||||
if (InstallHelper_mac::installHelper(bForceDeleteOld, isUserCanceled, logger_)) {
|
||||
// start XPC connection
|
||||
connection_ = xpc_connection_create_mach_service(WS_MAC_HELPER_BUNDLE_ID, NULL, 0);
|
||||
queue_ = dispatch_queue_create("com.windscribe.helper.xpc", DISPATCH_QUEUE_SERIAL);
|
||||
connection_ = xpc_connection_create_mach_service(WS_MAC_HELPER_BUNDLE_ID, queue_, 0);
|
||||
if (!connection_)
|
||||
return;
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "ihelperbackend.h"
|
||||
#include <atomic>
|
||||
#include <QMutex>
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <xpc/xpc.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
@@ -21,8 +23,9 @@ public:
|
||||
std::string sendCmd(int cmdId, const std::string &data) override;
|
||||
|
||||
private:
|
||||
State curState_ = State::kInit;
|
||||
std::atomic<State> curState_ = State::kInit;
|
||||
mutable QMutex mutex_;
|
||||
xpc_connection_t connection_;
|
||||
dispatch_queue_t queue_ = nullptr;
|
||||
xpc_connection_t connection_ = nullptr;
|
||||
spdlog::logger *logger_;
|
||||
};
|
||||
|
||||
@@ -124,8 +124,9 @@ void MutableLocationInfo::selectNodeByIp(const QString &addr)
|
||||
|
||||
bool MutableLocationInfo::selectNodeByHostname(const QString &hostname)
|
||||
{
|
||||
// Match by subdomain only so pinned IPs still work when the server TLD changes.
|
||||
for (int i = 0; i < nodes_.count(); i++) {
|
||||
if (nodes_[i]->getHostname() == hostname) {
|
||||
if (Utils::isSubdomainsEqual(nodes_[i]->getHostname(), hostname)) {
|
||||
qCDebug(LOG_BASIC) << "Selected node by hostname: " << i;
|
||||
selectedNode_ = i;
|
||||
return true;
|
||||
|
||||
@@ -72,8 +72,18 @@ void NetworkDetectionManager_linux::updateNetworkInfo(bool bWithEmitSignal)
|
||||
|
||||
types::NetworkInterface newNetworkInterface = NetworkUtils_linux::networkInterfaceByName(ifname);
|
||||
if (newNetworkInterface != networkInterface_) {
|
||||
bool significantChange = false;
|
||||
|
||||
if (newNetworkInterface.interfaceName != networkInterface_.interfaceName) {
|
||||
significantChange = true;
|
||||
} else if (newNetworkInterface.networkOrSsid != networkInterface_.networkOrSsid) {
|
||||
significantChange = true;
|
||||
} else {
|
||||
qCInfo(LOG_BASIC) << "Minor interface change (e.g. same-SSID AP roam), skipping reconnect";
|
||||
}
|
||||
|
||||
networkInterface_ = newNetworkInterface;
|
||||
if (bWithEmitSignal) {
|
||||
if (bWithEmitSignal && significantChange) {
|
||||
emit networkChanged(networkInterface_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,11 @@ void NetworkDetectionManager_mac::onNetworkStateChanged()
|
||||
|
||||
if (networkInterface != lastNetworkInterface_)
|
||||
{
|
||||
bool significantChange = false;
|
||||
|
||||
if (networkInterface.interfaceName != lastNetworkInterface_.interfaceName)
|
||||
{
|
||||
significantChange = true;
|
||||
if (networkInterface.interfaceIndex == -1)
|
||||
{
|
||||
qCInfo(LOG_BASIC) << "Primary Adapter down: " << lastNetworkInterface_.interfaceName;
|
||||
@@ -62,6 +65,7 @@ void NetworkDetectionManager_mac::onNetworkStateChanged()
|
||||
}
|
||||
else if (networkInterface.networkOrSsid != lastNetworkInterface_.networkOrSsid)
|
||||
{
|
||||
significantChange = true;
|
||||
qCInfo(LOG_BASIC) << "Primary Network Changed: "
|
||||
<< networkInterface.interfaceName
|
||||
<< " : " << networkInterface.networkOrSsid;
|
||||
@@ -75,12 +79,14 @@ void NetworkDetectionManager_mac::onNetworkStateChanged()
|
||||
}
|
||||
else
|
||||
{
|
||||
qCInfo(LOG_BASIC) << "Unidentified interface change";
|
||||
// Can happen when changing interfaces
|
||||
qCInfo(LOG_BASIC) << "Minor interface change on" << networkInterface.interfaceName
|
||||
<< "(e.g. same-SSID AP roam), skipping reconnect";
|
||||
}
|
||||
|
||||
lastNetworkInterface_ = networkInterface;
|
||||
emit networkChanged(networkInterface);
|
||||
if (significantChange) {
|
||||
emit networkChanged(networkInterface);
|
||||
}
|
||||
}
|
||||
else if (wifiAdapterUp != lastWifiAdapterUp_)
|
||||
{
|
||||
|
||||
@@ -415,7 +415,12 @@ void MainService::onPinIp()
|
||||
}
|
||||
|
||||
// Get the current connecting hostname from backend
|
||||
// Store only the subdomain portion so pinned IPs still work when the server TLD changes.
|
||||
QString hostname = backend_->getCurrentConnectingHostname();
|
||||
int dotIndex = hostname.indexOf('.');
|
||||
if (dotIndex != -1) {
|
||||
hostname = hostname.left(dotIndex);
|
||||
}
|
||||
|
||||
// Add to favorites with hostname and IP via kPinnedIp role
|
||||
QVariantList pinnedData;
|
||||
|
||||
@@ -517,7 +517,7 @@ void Backend::onEngineSessionDeleted()
|
||||
void Backend::onEngineUpdateSessionStatus(const api_responses::SessionStatus &sessionStatus)
|
||||
{
|
||||
latestSessionStatus_ = sessionStatus;
|
||||
locationsModelManager_->setFreeSessionStatus(!latestSessionStatus_.isPremium());
|
||||
locationsModelManager_->setFreeSessionStatus(!latestSessionStatus_.isPremium(), latestSessionStatus_.getAlc());
|
||||
updateAccountInfo();
|
||||
emit sessionStatusChanged(latestSessionStatus_);
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ void LocationsModelManager::setLocationOrder(ORDER_LOCATION_TYPE orderLocationTy
|
||||
filterLocationsProxyModel_->setLocationOrder(orderLocationType);
|
||||
}
|
||||
|
||||
void LocationsModelManager::setFreeSessionStatus(bool isFreeSessionStatus)
|
||||
void LocationsModelManager::setFreeSessionStatus(bool isFreeSessionStatus, const QStringList &alcLocations)
|
||||
{
|
||||
locationsModel_->setFreeSessionStatus(isFreeSessionStatus);
|
||||
locationsModel_->setFreeSessionStatus(isFreeSessionStatus, alcLocations);
|
||||
}
|
||||
|
||||
QModelIndex LocationsModelManager::getIndexByLocationId(const LocationID &id) const
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
void updateDeviceName(const QString &staticIpDeviceName);
|
||||
void changeConnectionSpeed(LocationID id, PingTime speed);
|
||||
void setLocationOrder(ORDER_LOCATION_TYPE orderLocationType);
|
||||
void setFreeSessionStatus(bool isFreeSessionStatus);
|
||||
void setFreeSessionStatus(bool isFreeSessionStatus, const QStringList &alcLocations);
|
||||
|
||||
QAbstractItemModel *locationsModel() { return locationsModel_; }
|
||||
QAbstractItemModel *sortedLocationsProxyModel() { return sortedLocationsProxyModel_; }
|
||||
|
||||
@@ -110,6 +110,15 @@ void FavoriteLocationsStorage::readFromSettings()
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate any stored FQDNs to subdomain-only so pinned IPs still work when the server TLD changes.
|
||||
for (auto it = favoriteLocations_.begin(); it != favoriteLocations_.end(); ++it) {
|
||||
int dotIndex = it.value().pinnedHostname.indexOf('.');
|
||||
if (dotIndex != -1) {
|
||||
it.value().pinnedHostname = it.value().pinnedHostname.left(dotIndex);
|
||||
isFavoriteLocationsSetModified_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have migrated to a new version, rewrite the data back to storage
|
||||
writeToSettings();
|
||||
}
|
||||
@@ -161,7 +170,13 @@ void FavoriteLocationsStorage::fromJson(const QJsonArray &arr)
|
||||
continue;
|
||||
LocationID loc(entry["type"].toInt(), entry["id"].toInt(), entry["city"].toString());
|
||||
FavoriteData data;
|
||||
data.pinnedHostname = entry["pinnedHostname"].toString();
|
||||
// Store only the subdomain portion so pinned IPs still work when the server TLD changes.
|
||||
QString hostname = entry["pinnedHostname"].toString();
|
||||
int dotIndex = hostname.indexOf('.');
|
||||
if (dotIndex != -1) {
|
||||
hostname = hostname.left(dotIndex);
|
||||
}
|
||||
data.pinnedHostname = hostname;
|
||||
data.pinnedIp = entry["pinnedIp"].toString();
|
||||
favoriteLocations_.insert(loc, data);
|
||||
}
|
||||
|
||||
@@ -227,11 +227,13 @@ void LocationsModel::changeConnectionSpeed(LocationID id, PingTime speed)
|
||||
}
|
||||
}
|
||||
|
||||
void LocationsModel::setFreeSessionStatus(bool isFreeSessionStatus)
|
||||
void LocationsModel::setFreeSessionStatus(bool isFreeSessionStatus, const QStringList &alcLocations)
|
||||
{
|
||||
if (isFreeSessionStatus != isFreeSessionStatus_)
|
||||
QSet<QString> alcSet(alcLocations.begin(), alcLocations.end());
|
||||
if (isFreeSessionStatus != isFreeSessionStatus_ || alcSet != alcLocations_)
|
||||
{
|
||||
isFreeSessionStatus_ = isFreeSessionStatus;
|
||||
alcLocations_ = alcSet;
|
||||
emit dataChanged(index(0, 0), index(locations_.size() - 1, 0));
|
||||
for (int i = 0; i < locations_.size(); ++i)
|
||||
{
|
||||
@@ -561,7 +563,7 @@ QVariant LocationsModel::dataForLocation(int row, int role) const
|
||||
}
|
||||
else if (role == kIsShowAsPremium)
|
||||
{
|
||||
return locations_[row]->location().isPremiumOnly && isFreeSessionStatus_;
|
||||
return locations_[row]->location().isPremiumOnly && isFreeSessionStatus_ && !alcLocations_.contains(locations_[row]->location().shortName);
|
||||
}
|
||||
else if (role == kIs10Gbps)
|
||||
{
|
||||
@@ -686,7 +688,7 @@ QVariant LocationsModel::dataForCity(LocationItem *l, int row, int role) const
|
||||
if (lid.isStaticIpsLocation())
|
||||
return false;
|
||||
else
|
||||
return l->location().cities[row].isPremiumOnly && isFreeSessionStatus_;
|
||||
return l->location().cities[row].isPremiumOnly && isFreeSessionStatus_ && !alcLocations_.contains(l->location().shortName);
|
||||
}
|
||||
else if (role == kIsFavorite)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
void updateBestLocation(const LocationID &bestLocation);
|
||||
void updateCustomConfigLocation(const types::Location &location);
|
||||
void changeConnectionSpeed(LocationID id, PingTime speed);
|
||||
void setFreeSessionStatus(bool isFreeSessionStatus);
|
||||
void setFreeSessionStatus(bool isFreeSessionStatus, const QStringList &alcLocations);
|
||||
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
@@ -85,6 +85,7 @@ private:
|
||||
|
||||
int *root_; // Fake root node. The typename does not matter, only the pointer to identify the root node matters.
|
||||
bool isFreeSessionStatus_;
|
||||
QSet<QString> alcLocations_;
|
||||
FavoriteLocationsStorage favoriteLocationsStorage_;
|
||||
RenamedLocationsStorage renamedLocationsStorage_;
|
||||
const char *BEST_LOCATION_NAME = QT_TR_NOOP("Best Location");
|
||||
|
||||
@@ -312,7 +312,7 @@ void TestLocationsModel::testFreeSessionStatusChange()
|
||||
{
|
||||
QModelIndex ind = locationsModel_->getIndexByLocationId(LocationID::createApiLocationId(63, "Vancouver", "Granville"));
|
||||
QVERIFY(ind.data(gui_locations::kIsShowAsPremium).toBool() == false);
|
||||
locationsModel_->setFreeSessionStatus(true);
|
||||
locationsModel_->setFreeSessionStatus(true, QStringList());
|
||||
QVERIFY(ind.data(gui_locations::kIsShowAsPremium).toBool() == true);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ void CredentialLineEdit::clear()
|
||||
|
||||
void CredentialLineEdit::focusInEvent(QFocusEvent *event)
|
||||
{
|
||||
QTimer::singleShot(0, [this]() {
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
lineEdit_->setFocus();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,9 +179,14 @@ void CityItemDelegate::paint(QPainter *painter, const ItemStyleOption &option, c
|
||||
|
||||
// Disabled/custom config error/latency
|
||||
|
||||
bool showAsPremium = index.data(kIsShowAsPremium).toBool();
|
||||
// only show disabled locations to pro users
|
||||
bool disabled = index.data(kIsShowAsPremium).toBool() ? false : index.data(kIsDisabled).toBool();
|
||||
if (disabled) {
|
||||
bool disabled = showAsPremium ? false : index.data(kIsDisabled).toBool();
|
||||
if (showAsPremium) {
|
||||
// no ping data available for premium locations when user is free
|
||||
xOffset -= 16*G_SCALE;
|
||||
xOffset -= LOCATION_ITEM_MARGIN*G_SCALE;
|
||||
} else if (disabled) {
|
||||
QSharedPointer<IndependentPixmap> consIcon = ImageResourcesSvg::instance().getIndependentPixmap("locations/UNDER_CONSTRUCTION_ICON");
|
||||
painter->setOpacity(textOpacity);
|
||||
xOffset -= consIcon->width();
|
||||
@@ -235,7 +240,7 @@ void CityItemDelegate::paint(QPainter *painter, const ItemStyleOption &option, c
|
||||
|
||||
// no p2p icon + favorite icon
|
||||
// You can't favorite a static ip or custom config location.
|
||||
if (!lid.isStaticIpsLocation() && !lid.isCustomConfigsLocation()) {
|
||||
if (!lid.isStaticIpsLocation() && !lid.isCustomConfigsLocation() && !showAsPremium) {
|
||||
if (qFuzzyCompare(option.selectedOpacity(), 1.0)) {
|
||||
QSharedPointer<IndependentPixmap> favIcon;
|
||||
bool isHoveringFavorite = (option.hoverClickableId() == (int)ClickableRect::kFavorite);
|
||||
|
||||
@@ -1088,7 +1088,12 @@ void MainWindow::onPinIp()
|
||||
}
|
||||
|
||||
// Get the current connecting hostname from backend
|
||||
// Store only the subdomain portion so pinned IPs still work when the server TLD changes.
|
||||
QString hostname = backend_->getCurrentConnectingHostname();
|
||||
int dotIndex = hostname.indexOf('.');
|
||||
if (dotIndex != -1) {
|
||||
hostname = hostname.left(dotIndex);
|
||||
}
|
||||
|
||||
// Add to favorites with hostname and IP via kPinnedIp role
|
||||
QVariantList pinnedData;
|
||||
@@ -1186,7 +1191,7 @@ void MainWindow::onPreferencesLoginClick()
|
||||
collapsePreferences();
|
||||
|
||||
if (backend_->getPreferencesHelper()->isExternalConfigMode()) {
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
setCursor(Qt::WaitCursor);
|
||||
logoutReason_ = LOGOUT_GO_TO_LOGIN;
|
||||
backend_->logout(false);
|
||||
} else if (mainWindowController_->currentWindowAfterAnimation() == MainWindowController::WINDOW_ID_GENERAL_MESSAGE) {
|
||||
@@ -1580,7 +1585,7 @@ void MainWindow::onUpgradeAccountCancel()
|
||||
|
||||
void MainWindow::onLogoutWindowAccept()
|
||||
{
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
setCursor(Qt::WaitCursor);
|
||||
setEnabled(false);
|
||||
logoutReason_ = LOGOUT_FROM_MENU;
|
||||
isExitingFromPreferences_ = false;
|
||||
@@ -2389,7 +2394,7 @@ void MainWindow::onBackendLogoutFinished()
|
||||
|
||||
mainWindowController_->hideUpdateWidget();
|
||||
setEnabled(true);
|
||||
QApplication::restoreOverrideCursor();
|
||||
unsetCursor();
|
||||
}
|
||||
|
||||
void MainWindow::onBackendCleanupFinished()
|
||||
@@ -2556,7 +2561,7 @@ void MainWindow::onBackendSessionDeleted()
|
||||
{
|
||||
qCInfo(LOG_BASIC) << "Handle deleted session";
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
setCursor(Qt::WaitCursor);
|
||||
setEnabled(false);
|
||||
logoutReason_ = LOGOUT_SESSION_EXPIRED;
|
||||
selectedLocation_->clear();
|
||||
|
||||
@@ -355,14 +355,17 @@ void ComboBoxItem::onMenuOpened()
|
||||
int item = menu_->indexOfItemByName(itemName);
|
||||
int numItems = menu_->itemCount();
|
||||
|
||||
int navigateToIndex = 0;
|
||||
bool hasScrollBar = false;
|
||||
|
||||
if (numItems <= menu_->visibleItems()) // all showing
|
||||
{
|
||||
// center on selected item
|
||||
heightCentering -= item * menu_->itemHeight();
|
||||
menu_->navigateItemToTop(0);
|
||||
}
|
||||
else // scrollbar on
|
||||
{
|
||||
hasScrollBar = true;
|
||||
int offBy = 0;
|
||||
if (item == numItems - 1) // last item
|
||||
{
|
||||
@@ -387,8 +390,7 @@ void ComboBoxItem::onMenuOpened()
|
||||
offBy = 2;
|
||||
}
|
||||
|
||||
menu_->navigateItemToTop(item - offBy);
|
||||
menu_->activateItem(item);
|
||||
navigateToIndex = item - offBy;
|
||||
}
|
||||
|
||||
// Ensure popup visibility when close to the bottom of the current screen.
|
||||
@@ -400,6 +402,14 @@ void ComboBoxItem::onMenuOpened()
|
||||
|
||||
menu_->move(point.x() - offsetX, heightCentering);
|
||||
menu_->show();
|
||||
|
||||
// Navigate and activate after show() so that Qt's first-show layout pass
|
||||
// does not clobber the scroll position (Qt 6.11 regression).
|
||||
menu_->navigateItemToTop(navigateToIndex);
|
||||
if (hasScrollBar) {
|
||||
menu_->activateItem(item);
|
||||
}
|
||||
|
||||
menu_->setFocus();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 197 KiB |
Reference in New Issue
Block a user