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

First implementation of tabs.

This commit is contained in:
Filippo Scognamiglio
2026-01-04 19:24:43 +01:00
parent 048cfcce81
commit 11ad932965
27 changed files with 191 additions and 107 deletions

View File

@@ -813,6 +813,7 @@ QtObject {
wintitle = args[args.indexOf("-T") + 1] wintitle = args[args.indexOf("-T") + 1]
} }
settingsInitialized = true
initializedSettings() initializedSettings()
} }
Component.onDestruction: { Component.onDestruction: {
@@ -826,4 +827,5 @@ QtObject {
text: "100%" text: "100%"
} }
property real labelWidth: _sampleLabel.width property real labelWidth: _sampleLabel.width
property bool settingsInitialized: false
} }

View File

@@ -42,6 +42,7 @@ Item{
property size terminalSize: kterminal.terminalSize property size terminalSize: kterminal.terminalSize
property size fontMetrics: kterminal.fontMetrics property size fontMetrics: kterminal.fontMetrics
property bool sessionStarted: false
// Manage copy and paste // Manage copy and paste
Connections { Connections {
@@ -168,6 +169,10 @@ Item{
} }
function startSession() { function startSession() {
if (terminalContainer.sessionStarted)
return
terminalContainer.sessionStarted = true
appSettings.initializedSettings.disconnect(startSession); appSettings.initializedSettings.disconnect(startSession);
// Retrieve the variable set in main.cpp if arguments are passed. // Retrieve the variable set in main.cpp if arguments are passed.
@@ -192,6 +197,8 @@ Item{
Component.onCompleted: { Component.onCompleted: {
appSettings.terminalFontChanged.connect(handleFontChanged); appSettings.terminalFontChanged.connect(handleFontChanged);
appSettings.initializedSettings.connect(startSession); appSettings.initializedSettings.connect(startSession);
if (appSettings.settingsInitialized)
startSession();
appSettings.updateFont() appSettings.updateFont()
} }
} }
@@ -232,6 +239,7 @@ Item{
kterminal.simulateMouseDoubleClick(coord.x, coord.y, mouse.button, mouse.buttons, mouse.modifiers); kterminal.simulateMouseDoubleClick(coord.x, coord.y, mouse.button, mouse.buttons, mouse.modifiers);
} }
onPressed: function(mouse) { onPressed: function(mouse) {
kterminal.forceActiveFocus()
if ((!kterminal.terminalUsesMouse || mouse.modifiers & Qt.ShiftModifier) && mouse.button == Qt.RightButton) { if ((!kterminal.terminalUsesMouse || mouse.modifiers & Qt.ShiftModifier) && mouse.button == Qt.RightButton) {
contextmenu.popup(); contextmenu.popup();
} else { } else {

View File

@@ -25,6 +25,8 @@ import QtQuick.Layouts 1.3
import QtQuick.Dialogs import QtQuick.Dialogs
ApplicationWindow { ApplicationWindow {
readonly property real tabButtonPadding: 10
id: settings_window id: settings_window
title: qsTr("Settings") title: qsTr("Settings")
width: 640 width: 640
@@ -37,15 +39,19 @@ ApplicationWindow {
id: bar id: bar
anchors { left: parent.left; right: parent.right; top: parent.top; } anchors { left: parent.left; right: parent.right; top: parent.top; }
TabButton { TabButton {
padding: tabButtonPadding
text: qsTr("General") text: qsTr("General")
} }
TabButton { TabButton {
padding: tabButtonPadding
text: qsTr("Terminal") text: qsTr("Terminal")
} }
TabButton { TabButton {
padding: tabButtonPadding
text: qsTr("Effects") text: qsTr("Effects")
} }
TabButton { TabButton {
padding: tabButtonPadding
text: qsTr("Advanced") text: qsTr("Advanced")
} }
} }

View File

@@ -1,92 +0,0 @@
import QtQuick 2.0
QtObject {
property string rasterizationShader:
((appSettings.rasterization === appSettings.no_rasterization) ||
(appSettings.rasterization === appSettings.modern_rasterization) ? "
lowp vec3 applyRasterization(vec2 screenCoords, lowp vec3 texel, vec2 virtualResolution, float intensity) {
return texel;
}" : "") +
(appSettings.rasterization === appSettings.scanline_rasterization ? "
#define INTENSITY 0.30
#define BRIGHTBOOST 0.30
lowp vec3 applyRasterization(vec2 screenCoords, lowp vec3 texel, vec2 virtualResolution, float intensity) {
lowp vec3 pixelHigh = ((1.0 + BRIGHTBOOST) - (0.2 * texel)) * texel;
lowp vec3 pixelLow = ((1.0 - INTENSITY) + (0.1 * texel)) * texel;
vec2 coords = fract(screenCoords * virtualResolution) * 2.0 - vec2(1.0);
lowp float mask = 1.0 - abs(coords.y);
lowp vec3 rasterizationColor = mix(pixelLow, pixelHigh, mask);
return mix(texel, rasterizationColor, intensity);
}" : "") +
(appSettings.rasterization === appSettings.pixel_rasterization ? "
#define INTENSITY 0.30
#define BRIGHTBOOST 0.30
lowp vec3 applyRasterization(vec2 screenCoords, lowp vec3 texel, vec2 virtualResolution, float intensity) {
lowp vec3 result = texel;
lowp vec3 pixelHigh = ((1.0 + BRIGHTBOOST) - (0.2 * texel)) * texel;
lowp vec3 pixelLow = ((1.0 - INTENSITY) + (0.1 * texel)) * texel;
vec2 coords = fract(screenCoords * virtualResolution) * 2.0 - vec2(1.0);
coords = coords * coords;
lowp float mask = 1.0 - coords.x - coords.y;
lowp vec3 rasterizationColor = mix(pixelLow, pixelHigh, mask);
return mix(texel, rasterizationColor, intensity);
}" : "") +
(appSettings.rasterization === appSettings.subpixel_rasterization ? "
#define INTENSITY 0.30
#define BRIGHTBOOST 0.30
#define SUBPIXELS 3.0
const vec3 offsets = vec3(3.141592654) * vec3(1.0/2.0,1.0/2.0 - 2.0/3.0,1.0/2.0-4.0/3.0);
lowp vec3 applyRasterization(vec2 screenCoords, lowp vec3 texel, vec2 virtualResolution, float intensity) {
vec2 omega = vec2(3.141592654) * vec2(2.0) * virtualResolution;
vec2 angle = screenCoords * omega;
vec3 xfactors = (SUBPIXELS + sin(angle.x + offsets)) / (SUBPIXELS + 1.0);
lowp vec3 result = texel * xfactors;
lowp vec3 pixelHigh = ((1.0 + BRIGHTBOOST) - (0.2 * result)) * result;
lowp vec3 pixelLow = ((1.0 - INTENSITY) + (0.1 * result)) * result;
vec2 coords = fract(screenCoords * virtualResolution) * 2.0 - vec2(1.0);
lowp float mask = 1.0 - abs(coords.y);
lowp vec3 rasterizationColor = mix(pixelLow, pixelHigh, mask);
return mix(texel, rasterizationColor, intensity);
}" : "") +
"\n\n"
property string min2: "
float min2(vec2 v) {
return min(v.x, v.y);
}\n\n"
property string rgb2grey: "
float rgb2grey(vec3 v) {
return dot(v, vec3(0.21, 0.72, 0.04));
}\n\n"
property string max2: "
float max2(vec2 v) {
return max(v.x, v.y);
}\n\n"
property string prod2: "
float prod2(vec2 v) {
return v.x * v.y;
}\n\n"
property string sum2: "
float sum2(vec2 v) {
return v.x + v.y;
}\n\n"
}

View File

@@ -50,6 +50,7 @@ Item {
property ShaderEffectSource source property ShaderEffectSource source
property BurnInEffect burnInEffect property BurnInEffect burnInEffect
property ShaderEffectSource bloomSource property ShaderEffectSource bloomSource
property QtObject timeManager
property color fontColor: appSettings.fontColor property color fontColor: appSettings.fontColor
property color backgroundColor: appSettings.backgroundColor property color backgroundColor: appSettings.backgroundColor
@@ -105,7 +106,7 @@ Item {
property real displayTerminalFrame: appSettings._frameSize > 0 || appSettings.screenCurvature > 0 property real displayTerminalFrame: appSettings._frameSize > 0 || appSettings.screenCurvature > 0
property real time: timeManager.time property real time: timeManager ? timeManager.time : 0
property ShaderEffectSource noiseSource: noiseShaderSource property ShaderEffectSource noiseSource: noiseShaderSource
property real frameSize: appSettings.frameSize property real frameSize: appSettings.frameSize
@@ -160,10 +161,6 @@ Item {
} }
} }
ShaderLibrary {
id: shaderLibrary
}
ShaderEffect { ShaderEffect {
id: staticShader id: staticShader

View File

@@ -32,6 +32,7 @@ ShaderTerminal {
id: mainShader id: mainShader
opacity: appSettings.windowOpacity * 0.3 + 0.7 opacity: appSettings.windowOpacity * 0.3 + 0.7
timeManager: terminalWindow.timeManager
source: terminal.mainSource source: terminal.mainSource
burnInEffect: terminal.burnInEffect burnInEffect: terminal.burnInEffect
virtualResolution: terminal.virtualResolution virtualResolution: terminal.virtualResolution
@@ -41,16 +42,15 @@ ShaderTerminal {
) )
bloomSource: bloomSourceLoader.item bloomSource: bloomSourceLoader.item
TimeManager {
id: timeManager
enableTimer: terminalWindow.visible
}
PreprocessedTerminal { PreprocessedTerminal {
id: terminal id: terminal
anchors.fill: parent anchors.fill: parent
} }
function activate() {
terminal.mainTerminal.forceActiveFocus()
}
// EFFECTS //////////////////////////////////////////////////////////////// // EFFECTS ////////////////////////////////////////////////////////////////
Loader { Loader {
id: bloomEffectLoader id: bloomEffectLoader

128
app/qml/TerminalTabs.qml Normal file
View File

@@ -0,0 +1,128 @@
/*******************************************************************************
* 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
Item {
id: tabsRoot
readonly property string title: stack.currentItem ? stack.currentItem.title : ""
readonly property size terminalSize: stack.currentItem ? stack.currentItem.terminalSize : Qt.size(0, 0)
property alias currentIndex: tabBar.currentIndex
readonly property int count: tabsModel.count
function addTab() {
tabsModel.append({ title: qsTr("Tab %1").arg(tabsModel.count + 1) })
tabBar.currentIndex = tabsModel.count - 1
}
function closeTab(index) {
if (tabsModel.count <= 1)
return
tabsModel.remove(index)
if (tabBar.currentIndex >= tabsModel.count) {
tabBar.currentIndex = tabsModel.count - 1
}
}
function closeCurrentTab() {
closeTab(tabBar.currentIndex)
}
ListModel {
id: tabsModel
}
Component.onCompleted: addTab()
ColumnLayout {
anchors.fill: parent
spacing: 0
TabBar {
id: tabBar
Layout.fillWidth: true
focusPolicy: Qt.NoFocus
visible: tabsModel.count > 1
background: Rectangle {
color: palette.window
}
Repeater {
model: tabsModel
TabButton {
id: tabButton
contentItem: RowLayout {
anchors.fill: parent
anchors { leftMargin: 6; rightMargin: 6 }
spacing: 6
Label {
text: model.title
elide: Text.ElideRight
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
ToolButton {
text: "\u00d7"
focusPolicy: Qt.NoFocus
visible: tabsModel.count > 1
enabled: visible
Layout.alignment: Qt.AlignVCenter
onClicked: tabsRoot.closeTab(index)
}
}
}
}
}
StackLayout {
id: stack
Layout.fillWidth: true
Layout.fillHeight: true
currentIndex: tabBar.currentIndex
Repeater {
model: tabsModel
onItemAdded: function(index, item) {
if (index === tabBar.currentIndex)
item.activate()
}
TerminalContainer {
Layout.fillWidth: true
Layout.fillHeight: true
onTitleChanged: tabsModel.setProperty(index, "title", title)
}
}
}
}
Connections {
target: tabBar
onCurrentIndexChanged: {
if (stack.currentItem)
stack.currentItem.activate()
}
}
}

View File

@@ -71,7 +71,7 @@ ApplicationWindow {
color: "#00000000" color: "#00000000"
title: terminalContainer.title || qsTr(appSettings.wintitle) title: terminalTabs.title || qsTr(appSettings.wintitle)
Action { Action {
id: showMenubarAction id: showMenubarAction
@@ -137,11 +137,27 @@ ApplicationWindow {
aboutDialog.raise() aboutDialog.raise()
} }
} }
Action {
id: newTabAction
text: qsTr("New Tab")
onTriggered: terminalTabs.addTab()
}
Action {
id: closeTabAction
text: qsTr("Close Tab")
enabled: terminalTabs.count > 1
onTriggered: terminalTabs.closeCurrentTab()
}
ApplicationSettings { ApplicationSettings {
id: appSettings id: appSettings
} }
TerminalContainer { TimeManager {
id: terminalContainer id: sharedTimeManager
enableTimer: terminalWindow.visible
}
property alias timeManager: sharedTimeManager
TerminalTabs {
id: terminalTabs
width: parent.width width: parent.width
height: (parent.height + Math.abs(y)) height: (parent.height + Math.abs(y))
} }
@@ -158,7 +174,7 @@ ApplicationWindow {
active: appSettings.showTerminalSize active: appSettings.showTerminalSize
sourceComponent: SizeOverlay { sourceComponent: SizeOverlay {
z: 3 z: 3
terminalSize: terminalContainer.terminalSize terminalSize: terminalTabs.terminalSize
} }
} }
onClosing: { onClosing: {

View File

@@ -33,6 +33,15 @@ Menu {
} }
MenuSeparator {} MenuSeparator {}
Menu {
title: qsTr("Tabs")
MenuItem {
action: newTabAction
}
MenuItem {
action: closeTabAction
}
}
Menu { Menu {
title: qsTr("File") title: qsTr("File")

View File

@@ -28,4 +28,14 @@ Menu {
MenuItem { MenuItem {
action: pasteAction action: pasteAction
} }
MenuSeparator {}
Menu {
title: qsTr("Tabs")
MenuItem {
action: newTabAction
}
MenuItem {
action: closeTabAction
}
}
} }

View File

@@ -21,6 +21,7 @@
<file>Storage.qml</file> <file>Storage.qml</file>
<file>SettingsAdvancedTab.qml</file> <file>SettingsAdvancedTab.qml</file>
<file>TerminalContainer.qml</file> <file>TerminalContainer.qml</file>
<file>TerminalTabs.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>
@@ -42,7 +43,6 @@
<file>menus/WindowMenu.qml</file> <file>menus/WindowMenu.qml</file>
<file>menus/FullContextMenu.qml</file> <file>menus/FullContextMenu.qml</file>
<file>menus/ShortContextMenu.qml</file> <file>menus/ShortContextMenu.qml</file>
<file>ShaderLibrary.qml</file>
<file>menus/OSXMenu.qml</file> <file>menus/OSXMenu.qml</file>
<file>../shaders/terminal_dynamic.vert.qsb</file> <file>../shaders/terminal_dynamic.vert.qsb</file>
<file>../shaders/terminal_static.vert.qsb</file> <file>../shaders/terminal_static.vert.qsb</file>