1
0
mirror of https://github.com/Swordfish90/cool-retro-term.git synced 2026-02-08 00:32:27 +00:00

Tentative first implementation of client side decorations.

This commit is contained in:
Filippo Scognamiglio
2026-01-15 23:02:38 +01:00
parent a0a16fc300
commit 3d34ce7f76
6 changed files with 328 additions and 76 deletions

View File

@@ -18,18 +18,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/ *******************************************************************************/
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
Item { Item {
id: tabsRoot id: tabsRoot
readonly property int innerPadding: 6 readonly property string currentTitle: tabsModel.count > 0
readonly property string currentTitle: tabsModel.get(currentIndex).title ?? "cool-retro-term" ? (tabsModel.get(currentIndex).title ?? "cool-retro-term")
: "cool-retro-term"
readonly property size terminalSize: stack.currentItem ? stack.currentItem.terminalSize : Qt.size(0, 0) readonly property size terminalSize: stack.currentItem ? stack.currentItem.terminalSize : Qt.size(0, 0)
property alias currentIndex: tabBar.currentIndex property int currentIndex: 0
readonly property int count: tabsModel.count readonly property int count: tabsModel.count
property var hostWindow property var hostWindow
property alias tabsModel: tabsModel
function normalizeTitle(rawTitle) { function normalizeTitle(rawTitle) {
if (rawTitle === undefined || rawTitle === null) { if (rawTitle === undefined || rawTitle === null) {
@@ -40,7 +41,7 @@ Item {
function addTab() { function addTab() {
tabsModel.append({ title: "" }) tabsModel.append({ title: "" })
tabBar.currentIndex = tabsModel.count - 1 currentIndex = tabsModel.count - 1
} }
function closeTab(index) { function closeTab(index) {
@@ -50,7 +51,7 @@ Item {
} }
tabsModel.remove(index) tabsModel.remove(index)
tabBar.currentIndex = Math.min(tabBar.currentIndex, tabsModel.count - 1) currentIndex = Math.min(currentIndex, tabsModel.count - 1)
} }
ListModel { ListModel {
@@ -63,69 +64,11 @@ Item {
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
Rectangle {
id: tabRow
Layout.fillWidth: true
height: rowLayout.implicitHeight
color: palette.window
visible: tabsModel.count > 1
RowLayout {
id: rowLayout
anchors.fill: parent
spacing: 0
TabBar {
id: tabBar
Layout.fillWidth: true
Layout.fillHeight: true
focusPolicy: Qt.NoFocus
Repeater {
model: tabsModel
TabButton {
id: tabButton
contentItem: RowLayout {
anchors.fill: parent
anchors { leftMargin: innerPadding; rightMargin: innerPadding }
spacing: innerPadding
Label {
text: model.title
elide: Text.ElideRight
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
ToolButton {
text: "\u00d7"
focusPolicy: Qt.NoFocus
padding: innerPadding
Layout.alignment: Qt.AlignVCenter
onClicked: tabsRoot.closeTab(index)
}
}
}
}
}
ToolButton {
id: addTabButton
text: "+"
focusPolicy: Qt.NoFocus
Layout.fillHeight: true
padding: innerPadding
Layout.alignment: Qt.AlignVCenter
onClicked: tabsRoot.addTab()
}
}
}
StackLayout { StackLayout {
id: stack id: stack
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
currentIndex: tabBar.currentIndex currentIndex: tabsRoot.currentIndex
Repeater { Repeater {
model: tabsModel model: tabsModel

217
app/qml/TerminalTabsBar.qml Normal file
View File

@@ -0,0 +1,217 @@
/*******************************************************************************
* Copyright (c) 2013-2021 "Filippo Scognamiglio"
* https://github.com/Swordfish90/cool-retro-term
*
* This file is part of cool-retro-term.
*
* cool-retro-term is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
Item {
id: barRoot
readonly property int innerPadding: 6
readonly property int leftInset: (isMacOS && !showWindowControls) ? 72 : 0
property var tabsController
property var hostWindow
property bool isMacOS: false
property bool showWindowControls: true
property bool windowControlsOnLeft: false
property bool enableSystemMove: true
property bool enableDoubleClickMaximize: true
implicitHeight: rowLayout.implicitHeight
function toggleMaximize() {
if (!hostWindow) {
return
}
hostWindow.visibility = (hostWindow.visibility === Window.Maximized)
? Window.Windowed
: Window.Maximized
}
onTabsControllerChanged: {
if (tabsController) {
tabBar.currentIndex = tabsController.currentIndex
}
}
Component.onCompleted: {
if (tabsController) {
tabBar.currentIndex = tabsController.currentIndex
}
}
Rectangle {
anchors.fill: parent
color: palette.window
}
RowLayout {
id: rowLayout
anchors.fill: parent
spacing: 0
Item {
Layout.fillHeight: true
Layout.preferredWidth: leftInset
visible: leftInset > 0
}
Loader {
active: showWindowControls && windowControlsOnLeft
sourceComponent: windowControlsComponent
}
TabBar {
id: tabBar
Layout.fillWidth: true
Layout.fillHeight: true
focusPolicy: Qt.NoFocus
onCurrentIndexChanged: {
if (tabsController && tabsController.currentIndex !== currentIndex) {
tabsController.currentIndex = currentIndex
}
}
Repeater {
model: tabsController ? tabsController.tabsModel : null
TabButton {
id: tabButton
contentItem: RowLayout {
anchors.fill: parent
anchors { leftMargin: innerPadding; rightMargin: innerPadding }
spacing: innerPadding
Label {
text: model.title
elide: Text.ElideRight
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
ToolButton {
text: "\u00d7"
focusPolicy: Qt.NoFocus
padding: innerPadding
Layout.alignment: Qt.AlignVCenter
onClicked: {
if (tabsController) {
tabsController.closeTab(index)
}
}
}
}
}
}
}
ToolButton {
id: addTabButton
text: "+"
focusPolicy: Qt.NoFocus
Layout.fillHeight: true
padding: innerPadding
Layout.alignment: Qt.AlignVCenter
onClicked: {
if (tabsController) {
tabsController.addTab()
}
}
}
Loader {
active: showWindowControls && !windowControlsOnLeft
sourceComponent: windowControlsComponent
}
}
Component {
id: windowControlsComponent
RowLayout {
id: windowControls
Layout.fillHeight: true
Layout.alignment: Qt.AlignVCenter
spacing: 0
ToolButton {
text: "\u2212"
focusPolicy: Qt.NoFocus
padding: innerPadding
Layout.alignment: Qt.AlignVCenter
onClicked: {
if (hostWindow) {
hostWindow.visibility = Window.Minimized
}
}
}
ToolButton {
text: hostWindow && hostWindow.visibility === Window.Maximized ? "\u2752" : "\u25a1"
focusPolicy: Qt.NoFocus
padding: innerPadding
Layout.alignment: Qt.AlignVCenter
onClicked: toggleMaximize()
}
ToolButton {
text: "\u00d7"
focusPolicy: Qt.NoFocus
padding: innerPadding
Layout.alignment: Qt.AlignVCenter
onClicked: {
if (hostWindow) {
hostWindow.close()
}
}
}
}
}
Connections {
target: tabsController
function onCurrentIndexChanged() {
if (tabBar.currentIndex !== tabsController.currentIndex) {
tabBar.currentIndex = tabsController.currentIndex
}
}
}
DragHandler {
acceptedDevices: PointerDevice.Mouse
acceptedButtons: Qt.LeftButton
grabPermissions: PointerHandler.CanTakeOverFromItems
target: null
onActiveChanged: {
if (active && hostWindow && enableSystemMove) {
hostWindow.startSystemMove()
}
}
}
TapHandler {
acceptedButtons: Qt.LeftButton
onTapped: {
if (tapCount === 2 && enableDoubleClickMaximize) {
toggleMaximize()
}
}
}
}

View File

@@ -20,6 +20,7 @@
import QtQuick 2.2 import QtQuick 2.2
import QtQuick.Window 2.1 import QtQuick.Window 2.1
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts
import "menus" import "menus"
@@ -39,6 +40,8 @@ ApplicationWindow {
visible: false visible: false
flags: Qt.Window | Qt.FramelessWindowHint
property bool fullscreen: appSettings.fullscreen property bool fullscreen: appSettings.fullscreen
onFullscreenChanged: visibility = (fullscreen ? Window.FullScreen : Window.Windowed) onFullscreenChanged: visibility = (fullscreen ? Window.FullScreen : Window.Windowed)
@@ -137,11 +140,27 @@ ApplicationWindow {
text: qsTr("New Tab") text: qsTr("New Tab")
onTriggered: terminalTabs.addTab() onTriggered: terminalTabs.addTab()
} }
TerminalTabs { ColumnLayout {
id: terminalTabs anchors.fill: parent
width: parent.width spacing: 0
height: (parent.height + Math.abs(y))
hostWindow: terminalWindow TerminalTabsBar {
Layout.fillWidth: true
tabsController: terminalTabs
hostWindow: terminalWindow
isMacOS: appSettings.isMacOS
showWindowControls: true
windowControlsOnLeft: appSettings.isMacOS
enableSystemMove: true
enableDoubleClickMaximize: true
}
TerminalTabs {
id: terminalTabs
Layout.fillWidth: true
Layout.fillHeight: true
hostWindow: terminalWindow
}
} }
Loader { Loader {
anchors.centerIn: parent anchors.centerIn: parent
@@ -151,6 +170,77 @@ ApplicationWindow {
terminalSize: terminalTabs.terminalSize terminalSize: terminalTabs.terminalSize
} }
} }
Item {
id: resizeHandles
anchors.fill: parent
visible: true
property int resizeMargin: 6
MouseArea {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: resizeHandles.resizeMargin
cursorShape: Qt.SizeHorCursor
onPressed: terminalWindow.startSystemResize(Qt.LeftEdge)
}
MouseArea {
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
width: resizeHandles.resizeMargin
cursorShape: Qt.SizeHorCursor
onPressed: terminalWindow.startSystemResize(Qt.RightEdge)
}
MouseArea {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: resizeHandles.resizeMargin
cursorShape: Qt.SizeVerCursor
onPressed: terminalWindow.startSystemResize(Qt.BottomEdge)
}
MouseArea {
anchors.left: parent.left
anchors.top: parent.top
width: resizeHandles.resizeMargin
height: resizeHandles.resizeMargin
cursorShape: Qt.SizeFDiagCursor
onPressed: terminalWindow.startSystemResize(Qt.TopEdge | Qt.LeftEdge)
}
MouseArea {
anchors.right: parent.right
anchors.top: parent.top
width: resizeHandles.resizeMargin
height: resizeHandles.resizeMargin
cursorShape: Qt.SizeBDiagCursor
onPressed: terminalWindow.startSystemResize(Qt.TopEdge | Qt.RightEdge)
}
MouseArea {
anchors.left: parent.left
anchors.bottom: parent.bottom
width: resizeHandles.resizeMargin
height: resizeHandles.resizeMargin
cursorShape: Qt.SizeBDiagCursor
onPressed: terminalWindow.startSystemResize(Qt.BottomEdge | Qt.LeftEdge)
}
MouseArea {
anchors.right: parent.right
anchors.bottom: parent.bottom
width: resizeHandles.resizeMargin
height: resizeHandles.resizeMargin
cursorShape: Qt.SizeFDiagCursor
onPressed: terminalWindow.startSystemResize(Qt.BottomEdge | Qt.RightEdge)
}
}
onClosing: { onClosing: {
appRoot.closeWindow(terminalWindow) appRoot.closeWindow(terminalWindow)
} }

View File

@@ -38,14 +38,15 @@ QtObject {
visible: false visible: false
} }
property Component windowComponent: Component {
TerminalWindow { }
}
property ListModel windowsModel: ListModel { } property ListModel windowsModel: ListModel { }
function createWindow() { function createWindow() {
var window = windowComponent.createObject(null) var component = Qt.createComponent("TerminalWindow.qml")
if (component.status === Component.Error) {
console.error(component.errorString())
return
}
var window = component.createObject(null)
if (!window) if (!window)
return return

View File

@@ -22,6 +22,7 @@
<file>SettingsAdvancedTab.qml</file> <file>SettingsAdvancedTab.qml</file>
<file>TerminalContainer.qml</file> <file>TerminalContainer.qml</file>
<file>TerminalTabs.qml</file> <file>TerminalTabs.qml</file>
<file>TerminalTabsBar.qml</file>
<file>images/crt256.png</file> <file>images/crt256.png</file>
<file>utils.js</file> <file>utils.js</file>
<file>images/allNoise512.png</file> <file>images/allNoise512.png</file>