fix: fix self-hosted TextFields and Keyboard reset issue (#1983)

Co-authored-by: vkamn <vk@amnezia.org>
This commit is contained in:
NickVs2015
2025-11-12 10:57:53 +03:00
committed by GitHub
parent 3cc18c5807
commit b53cdcff08
10 changed files with 321 additions and 81 deletions

View File

@@ -247,6 +247,7 @@
<file>ui/qml/Components/OtpCodeDrawer.qml</file>
<file>ui/qml/Components/AwgTextField.qml</file>
<file>ui/qml/Pages2/PageSettingsApiSubscriptionKey.qml</file>
<file>ui/qml/Components/SmartScroll.qml</file>
</qresource>
<qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file>

View File

@@ -446,7 +446,11 @@ bool SettingsController::isOnTv()
bool SettingsController::isEdgeToEdgeEnabled()
{
#ifdef Q_OS_ANDROID
return AndroidController::instance()->isEdgeToEdgeEnabled();
if (!m_edgeToEdgeCached) {
m_cachedEdgeToEdgeEnabled = AndroidController::instance()->isEdgeToEdgeEnabled();
m_edgeToEdgeCached = true;
}
return m_cachedEdgeToEdgeEnabled;
#else
return false;
#endif

View File

@@ -150,6 +150,8 @@ private:
mutable int m_cachedStatusBarHeight = -1;
mutable int m_cachedNavigationBarHeight = -1;
mutable bool m_cachedEdgeToEdgeEnabled = false;
mutable bool m_edgeToEdgeCached = false;
int m_imeHeight = 0;
std::shared_ptr<Settings> m_settings;

View File

@@ -0,0 +1,55 @@
import QtQuick
import QtQuick.Controls
QtObject {
id: root
property var listView: null
property var scrollToItemTarget: null
property Connections imeConnection: Connections {
target: SettingsController
function onImeHeightChanged() {
if (root.scrollToItemTarget && SettingsController.imeHeight > 0) {
scrollTimer.restart()
}
}
}
property Timer scrollTimer: Timer {
interval: 100
repeat: false
onTriggered: {
if (root.scrollToItemTarget && root.listView) {
if (SettingsController.imeHeight > 0) {
var item = root.scrollToItemTarget
var itemY = item.mapToItem(root.listView.contentItem, 0, 0).y
var itemHeight = item.height
var keyboardHeight = SettingsController.imeHeight + SettingsController.safeAreaBottomMargin
var visibleHeight = root.listView.height - keyboardHeight
var desiredTopOffset = visibleHeight * 0.25
var targetContentY = itemY - desiredTopOffset
if (targetContentY < 0) {
targetContentY = 0
}
var maxContentY = root.listView.contentHeight - root.listView.height
if (targetContentY > maxContentY) {
targetContentY = maxContentY
}
root.listView.contentY = targetContentY
root.scrollToItemTarget = null
}
}
}
}
function scrollToItem(item) {
scrollToItemTarget = item
scrollTimer.restart()
}
}

View File

@@ -19,6 +19,9 @@ Item {
property string buttonText
property string buttonImageSource
property string buttonImageColor: AmneziaStyle.color.midnightBlack
property string buttonBackgroundColor: AmneziaStyle.color.paleGray
property string buttonHoveredColor: AmneziaStyle.color.lightGray
property var clickedFunc
property alias textField: textField
@@ -48,7 +51,7 @@ Item {
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
@@ -67,7 +70,7 @@ Item {
border.width: 1
Behavior on border.color {
PropertyAnimation { duration: 200 }
PropertyAnimation { duration: 100 }
}
RowLayout {
@@ -194,6 +197,14 @@ Item {
focusPolicy: Qt.NoFocus
text: root.buttonText
leftImageSource: root.buttonImageSource
leftImageColor: root.buttonImageColor
defaultColor: root.buttonBackgroundColor
hoveredColor: root.buttonHoveredColor
pressedColor: root.buttonHoveredColor
disabledColor: AmneziaStyle.color.transparent
borderWidth: 0
anchors.top: content.top
anchors.bottom: content.bottom
@@ -201,7 +212,7 @@ Item {
height: content.implicitHeight
width: content.implicitHeight
squareLeftSide: true
squareLeftSide: false
clickedFunc: function() {
if (root.clickedFunc && typeof root.clickedFunc === "function") {

View File

@@ -31,6 +31,11 @@ PageType {
}
}
SmartScroll {
id: smartScroll
listView: listView
}
ListViewType {
id: listView
@@ -80,6 +85,13 @@ PageType {
clientMtu = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(mtuTextField)
}
}
checkEmptyText: true
}
@@ -97,6 +109,12 @@ PageType {
clientJunkPacketCount = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(junkPacketCountTextField)
}
}
}
AwgTextField {
@@ -113,6 +131,12 @@ PageType {
clientJunkPacketMinSize = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(junkPacketMinSizeTextField)
}
}
}
AwgTextField {
@@ -129,6 +153,12 @@ PageType {
clientJunkPacketMaxSize = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(junkPacketMaxSizeTextField)
}
}
}
AwgTextField {
@@ -147,6 +177,12 @@ PageType {
clientSpecialJunk1 = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(specialJunk1TextField)
}
}
}
AwgTextField {
@@ -165,6 +201,12 @@ PageType {
clientSpecialJunk2 = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(specialJunk2TextField)
}
}
}
AwgTextField {
@@ -183,6 +225,12 @@ PageType {
clientSpecialJunk3 = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(specialJunk3TextField)
}
}
}
AwgTextField {
@@ -201,6 +249,12 @@ PageType {
clientSpecialJunk4 = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(specialJunk4TextField)
}
}
}
AwgTextField {
@@ -219,6 +273,12 @@ PageType {
clientSpecialJunk5 = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(specialJunk5TextField)
}
}
}
AwgTextField {
@@ -237,6 +297,12 @@ PageType {
clientControlledJunk1 = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(controlledJunk1TextField)
}
}
}
AwgTextField {
@@ -255,6 +321,12 @@ PageType {
clientControlledJunk2 = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(controlledJunk2TextField)
}
}
}
AwgTextField {
@@ -273,6 +345,12 @@ PageType {
clientControlledJunk3 = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(controlledJunk3TextField)
}
}
}
AwgTextField {
@@ -290,6 +368,12 @@ PageType {
clientSpecialHandshakeTimeout = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(iTimeTextField)
}
}
}
Header2TextType {

View File

@@ -34,6 +34,11 @@ PageType {
}
}
SmartScroll {
id: smartScroll
listView: listView
}
ListViewType {
id: listView
@@ -81,6 +86,12 @@ PageType {
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(vpnAddressSubnetTextField)
}
}
checkEmptyText: true
}
@@ -104,6 +115,12 @@ PageType {
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(portTextField)
}
}
checkEmptyText: true
}
@@ -121,6 +138,12 @@ PageType {
serverJunkPacketCount = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(junkPacketCountTextField)
}
}
}
AwgTextField {
@@ -137,6 +160,12 @@ PageType {
serverJunkPacketMinSize = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(junkPacketMinSizeTextField)
}
}
}
AwgTextField {
@@ -153,6 +182,12 @@ PageType {
serverJunkPacketMaxSize = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(junkPacketMaxSizeTextField)
}
}
}
AwgTextField {
@@ -169,6 +204,12 @@ PageType {
serverInitPacketJunkSize = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(initPacketJunkSizeTextField)
}
}
}
AwgTextField {
@@ -185,6 +226,12 @@ PageType {
serverResponsePacketJunkSize = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(responsePacketJunkSizeTextField)
}
}
}
// AwgTextField {
@@ -233,6 +280,12 @@ PageType {
serverInitPacketMagicHeader = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(initPacketMagicHeaderTextField)
}
}
}
AwgTextField {
@@ -249,6 +302,12 @@ PageType {
serverResponsePacketMagicHeader = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(responsePacketMagicHeaderTextField)
}
}
}
AwgTextField {
@@ -265,6 +324,12 @@ PageType {
serverUnderloadPacketMagicHeader = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(underloadPacketMagicHeaderTextField)
}
}
}
AwgTextField {
@@ -281,6 +346,12 @@ PageType {
serverTransportPacketMagicHeader = textField.text
}
}
textField.onActiveFocusChanged: {
if (textField.activeFocus) {
smartScroll.scrollToItem(transportPacketMagicHeaderTextField)
}
}
}
BasicButtonType {

View File

@@ -165,9 +165,11 @@ PageType {
ScrollBar.vertical: ScrollBarType { policy: ScrollBar.AlwaysOn }
anchors.top: header.bottom
anchors.bottom: addAppButton.top
anchors.bottom: parent.bottom
anchors.bottomMargin: addAppButton.implicitHeight + 48 + SettingsController.safeAreaBottomMargin + (searchField.textField.activeFocus ? 0 : SettingsController.imeHeight)
anchors.left: parent.left
anchors.right: parent.right
clip: true
model: SortFilterProxyModel {
id: proxyAppSplitTunnelingModel
@@ -215,50 +217,54 @@ PageType {
}
Rectangle {
anchors.fill: addAppButton
anchors.bottomMargin: -24 - SettingsController.safeAreaBottomMargin
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
}
RowLayout {
id: addAppButton
enabled: root.pageEnabled
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 24 + SettingsController.safeAreaBottomMargin
anchors.bottom: parent.bottom
height: addAppButton.implicitHeight + 48 + SettingsController.safeAreaBottomMargin
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
RowLayout {
id: addAppButton
TextFieldWithHeaderType {
id: searchField
enabled: root.pageEnabled
Layout.fillWidth: true
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 24 + SettingsController.safeAreaBottomMargin
textField.placeholderText: qsTr("application name")
buttonImageSource: "qrc:/images/controls/plus.svg"
TextFieldWithHeaderType {
id: searchField
rightButtonClickedOnEnter: true
Layout.fillWidth: true
clickedFunc: function() {
searchField.focus = false
PageController.showBusyIndicator(true)
textField.placeholderText: qsTr("application name")
buttonImageSource: "qrc:/images/controls/plus.svg"
if (Qt.platform.os === "windows") {
var fileName = SystemController.getFileName(qsTr("Open executable file"),
qsTr("Executable files (*.*)"))
if (fileName !== "") {
AppSplitTunnelingController.addApp(fileName)
rightButtonClickedOnEnter: true
clickedFunc: function() {
searchField.focus = false
PageController.showBusyIndicator(true)
if (Qt.platform.os === "windows") {
var fileName = SystemController.getFileName(qsTr("Open executable file"),
qsTr("Executable files (*.*)"))
if (fileName !== "") {
AppSplitTunnelingController.addApp(fileName)
}
} else if (Qt.platform.os === "android"){
installedAppDrawer.openTriggered()
}
} else if (Qt.platform.os === "android"){
installedAppDrawer.openTriggered()
}
PageController.showBusyIndicator(false)
PageController.showBusyIndicator(false)
}
}
}
}

View File

@@ -168,11 +168,13 @@ PageType {
anchors.top: header.bottom
anchors.topMargin: 16
anchors.bottom: addSiteButton.top
anchors.bottom: parent.bottom
anchors.bottomMargin: addSiteButton.implicitHeight + 48 + (searchField.textField.activeFocus ? 0 : SettingsController.imeHeight)
width: parent.width
enabled: root.pageEnabled
clip: true
model: SortFilterProxyModel {
id: proxySitesModel
@@ -231,56 +233,60 @@ PageType {
}
Rectangle {
anchors.fill: addSiteButton
anchors.bottomMargin: -24
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
}
RowLayout {
id: addSiteButton
enabled: root.pageEnabled
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 24
anchors.bottom: parent.bottom
height: addSiteButton.implicitHeight + 48
color: AmneziaStyle.color.midnightBlack
opacity: 0.8
RowLayout {
id: addSiteButton
TextFieldWithHeaderType {
id: searchField
enabled: root.pageEnabled
Layout.fillWidth: true
rightButtonClickedOnEnter: true
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 24
anchors.rightMargin: 16
anchors.leftMargin: 16
anchors.bottomMargin: 24
textField.placeholderText: qsTr("website or IP")
buttonImageSource: "qrc:/images/controls/plus.svg"
TextFieldWithHeaderType {
id: searchField
clickedFunc: function() {
PageController.showBusyIndicator(true)
SitesController.addSite(textField.text)
textField.text = ""
PageController.showBusyIndicator(false)
}
}
Layout.fillWidth: true
rightButtonClickedOnEnter: true
ImageButtonType {
id: addSiteButtonImage
implicitWidth: 56
implicitHeight: 56
textField.placeholderText: qsTr("website or IP")
buttonImageSource: "qrc:/images/controls/plus.svg"
image: "qrc:/images/controls/more-vertical.svg"
imageColor: AmneziaStyle.color.paleGray
onClicked: function () {
moreActionsDrawer.openTriggered()
clickedFunc: function() {
PageController.showBusyIndicator(true)
SitesController.addSite(textField.text)
textField.text = ""
PageController.showBusyIndicator(false)
}
}
Keys.onReturnPressed: addSiteButtonImage.clicked()
Keys.onEnterPressed: addSiteButtonImage.clicked()
ImageButtonType {
id: addSiteButtonImage
implicitWidth: 56
implicitHeight: 56
image: "qrc:/images/controls/more-vertical.svg"
imageColor: AmneziaStyle.color.paleGray
onClicked: function () {
moreActionsDrawer.openTriggered()
}
Keys.onReturnPressed: addSiteButtonImage.clicked()
Keys.onEnterPressed: addSiteButtonImage.clicked()
}
}
}

View File

@@ -183,7 +183,7 @@ Window {
id: privateKeyPassphraseDrawer
anchors.fill: parent
expandedHeight: root.height * 0.35 + SettingsController.safeAreaBottomMargin
expandedHeight: root.height * 0.35 + SettingsController.safeAreaBottomMargin + SettingsController.imeHeight
expandedStateContent: ColumnLayout {
anchors.top: parent.top