import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtGraphicalEffects 1.12 import QtQuick.VirtualKeyboard 2.4 Window { id: root visible: true width: 480 height: 480 title: "ButtonTask" color: "black" visibility: Window.FullScreen property bool darkMode: true BackgroundLayer { id: bg anchors.fill: parent } // Main content area Item { id: content anchors.fill: parent anchors.margins: 10 Loader { id: viewLoader anchors.fill: parent sourceComponent: settingsContainer.layoutMode === "list" ? listComp : gridComp } Component { id: gridComp Item { id: gridRoot anchors.fill: parent readonly property int gridColumns: Math.max(1, settingsContainer.layoutColumns) readonly property real cellSide: Math.max(64, width / gridColumns) readonly property int gridRows: buttonsModel.count === 0 ? 0 : Math.ceil(buttonsModel.count / gridColumns) readonly property real gridContentHeight: gridRows * cellSide readonly property bool gridFits: gridContentHeight > 0 && gridContentHeight <= height GridView { id: gridView width: gridRoot.width height: gridRoot.gridFits ? gridRoot.gridContentHeight : gridRoot.height y: gridRoot.gridFits ? (gridRoot.height - gridRoot.gridContentHeight) / 2 : 0 cellWidth: gridRoot.cellSide cellHeight: gridRoot.cellSide model: buttonsModel clip: true interactive: !gridRoot.gridFits boundsBehavior: Flickable.StopAtBounds delegate: ButtonDelegate { width: gridView.cellWidth - settingsContainer.layoutSpacing height: gridView.cellHeight - settingsContainer.layoutSpacing showLabel: settingsContainer.showLabels } } } } Component { id: listComp SwipeView { id: swipeView anchors.fill: parent clip: true Repeater { model: buttonsModel Item { ButtonDelegate { anchors.centerIn: parent width: Math.min(swipeView.width, swipeView.height) * 0.8 height: width showLabel: settingsContainer.showLabels } } } } } Text { anchors.centerIn: parent visible: buttonsModel.count === 0 text: "Нет кнопок.\nОткройте настройки и добавьте кнопку." color: root.darkMode ? "#cccccc" : "#222222" font.pixelSize: 18 horizontalAlignment: Text.AlignHCenter } } // Settings gear (top-right) Rectangle { id: settingsBtn anchors.right: parent.right anchors.top: parent.top anchors.margins: 8 width: Math.max(40, parent.width / 14) height: width color: "transparent" opacity: 0.85 Rectangle { anchors.fill: parent radius: width / 2 color: root.darkMode ? "#40ffffff" : "#40000000" } Text { anchors.centerIn: parent text: "\u2699" font.pixelSize: parent.width * 0.6 color: root.darkMode ? "white" : "black" } MouseArea { anchors.fill: parent onClicked: passwordDialog.open() } } // Password dialog Dialog { id: passwordDialog modal: true title: "Пароль" anchors.centerIn: parent width: Math.min(420, parent.width * 0.8) closePolicy: Popup.CloseOnEscape standardButtons: Dialog.Ok | Dialog.Cancel contentItem: ColumnLayout { spacing: 8 TextField { id: pwdField Layout.fillWidth: true placeholderText: "Введите пароль" echoMode: TextInput.Password onAccepted: passwordDialog.accept() } Text { id: wrongPwd Layout.fillWidth: true visible: false color: "red" text: "Неверный пароль" } } onOpened: { pwdField.text = "" wrongPwd.visible = false pwdField.forceActiveFocus() } onAccepted: { if (btnController.checkPassword(pwdField.text)) { wrongPwd.visible = false pwdField.text = "" settingsDialog.open() } else { wrongPwd.visible = true Qt.callLater(passwordDialog.open) } } } // Settings dialog (single instance) SettingsDialog { id: settingsDialog } // Virtual keyboard — parented to Overlay so it's above dialogs InputPanel { id: inputPanel parent: Overlay.overlay z: 999 anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom visible: active } }