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

1 Commits

Author SHA1 Message Date
Filippo Scognamiglio
3d34ce7f76 Tentative first implementation of client side decorations. 2026-01-15 23:16:14 +01:00
43 changed files with 624 additions and 465 deletions

98
.github/workflows/appimage.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
name: "ci"
on:
push:
tags: "**"
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
appimage:
runs-on: ubuntu-18.04
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
submodules: true
- name: Install dependencies
run: |
sudo add-apt-repository -y ppa:beineri/opt-qt-5.15.2-bionic
sudo apt-get update -qq
sudo apt-get install -y \
build-essential make wget libgl1-mesa-dev \
qt515declarative qt515graphicaleffects \
qt515quickcontrols qt515quickcontrols2
- name: Download QT appimage builder
run: |
wget -c -O linuxdeployqt.AppImage \
https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage
chmod a+x linuxdeployqt.AppImage
- name: Build project
run: |
source /opt/qt*/bin/qt*-env.sh && \
qmake -v && \
qmake CONFIG+=release PREFIX=/usr && \
make -j$(nproc)
- name: Install to appdir
run: |
source /opt/qt*/bin/qt*-env.sh && \
make INSTALL_ROOT=appdir -j$(nproc) install
- name: Extract version number
run: |
# Extract version for linuxdeployqt to name the file. Use the tag as
# release name but remove prefix.
echo "VERSION=$(echo '${{ github.ref }}' | sed 's;.*/;;')" >> $GITHUB_ENV
- name: Build appimage directory
run: |
mkdir -p \
appdir/usr/bin \
appdir/usr/lib \
appdir/usr/share/applications \
appdir/usr/share/metainfo \
appdir/usr/share/icons/hicolor/128x128/apps
cp cool-retro-term appdir/usr/bin/
cp cool-retro-term.desktop appdir/usr/share/applications/
cp packaging/appdata/cool-retro-term.appdata.xml appdir/usr/share/metainfo/
cp app/icons/128x128/cool-retro-term.png appdir/usr/share/icons/hicolor/128x128/apps/
cp -r ./app/qml appdir/usr/
# Workaround for https://github.com/probonopd/linuxdeployqt/issues/78
cp -r ./qmltermwidget/QMLTermWidget appdir/usr/qml/
find appdir | sort
- name: Build appimage
run: |
source /opt/qt*/bin/qt*-env.sh && \
./linuxdeployqt.AppImage appdir/usr/share/applications/cool-retro-term.desktop \
-verbose=1 -appimage \
-qmldir=./app/qml/ \
-qmldir=./qmltermwidget/
env:
# Unset environment variables
QTDIR:
QT_PLUGIN_PATH:
LD_LIBRARY_PATH:
- name: Upload release
uses: softprops/action-gh-release@v1
with:
body: appimage release
files: ./**/Cool_Retro_Term-*-x86_64.AppImage
- name: Clean up
if: always()
run: |
find appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq
make clean
rm -rf appdir

View File

@@ -1,49 +0,0 @@
name: Build AppImage
on:
workflow_dispatch:
push:
branches:
- main
pull_request:
jobs:
build-appimage:
name: Build (Linux, AppImage)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install build deps
run: |
sudo apt update
sudo apt install -y build-essential rsync wget
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: 6.10.0
dir: ..
modules: qtwayland qtshadertools
setup-python: false
cache: true
- name: Build AppImage
run: |
./scripts/build-appimage.sh
- name: Collect artifact
run: |
mkdir -p release
mv ./*.AppImage release/
- name: Attestation
uses: actions/attest-build-provenance@v1
with:
subject-path: ./release/*
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: cool-retro-term-appimage
path: ./release/*

View File

@@ -1,44 +0,0 @@
name: Build DMG
on:
workflow_dispatch:
push:
branches:
- main
pull_request:
jobs:
build-dmg:
name: Build (macOS, DMG)
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: 6.10.*
modules: qtshadertools
setup-python: false
cache: true
- name: Build DMG
run: |
JOBS="$(sysctl -n hw.ncpu)"
./scripts/build-dmg.sh
- name: Collect artifact
run: |
mkdir -p release
mv ./*.dmg release/
- name: Attestation
uses: actions/attest-build-provenance@v1
with:
subject-path: ./release/*
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: cool-retro-term-dmg
path: ./release/*

6
.gitignore vendored
View File

@@ -41,17 +41,11 @@ Makefile*
*.json
# Excludes compiled files
imports
cool-retro-term
build
# Linux
*.AppImage
# Mac OSX
.DS_Store
*.app
*.dmg

37
.travis.yml Normal file
View File

@@ -0,0 +1,37 @@
sudo: required
dist: trusty
language: c++
install:
- sudo add-apt-repository -y ppa:beineri/opt-qt58-trusty
- sudo apt-get update -qq
- sudo apt-get -y install build-essential qt58declarative qt58graphicaleffects qt58quickcontrols libgl1-mesa-dev
- source /opt/qt*/bin/qt*-env.sh
script:
- qmake CONFIG+=release PREFIX=/usr
- make -j$(nproc)
- mkdir -p appdir/usr/share/metainfo appdir/usr/bin
- cp packaging/appdata/cool-retro-term.appdata.xml appdir/usr/share/metainfo/
- cp cool-retro-term appdir/usr/bin/
- cp ./cool-retro-term.desktop appdir/
- cp ./app/icons/128x128/cool-retro-term.png appdir/
- cp -r ./app/qml appdir/usr/
- cp -r ./qmltermwidget/QMLTermWidget appdir/usr/qml/ # Workaround for https://github.com/probonopd/linuxdeployqt/issues/78
- wget -c https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage
- chmod a+x linuxdeployqt-*.AppImage
- unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
- export VERSION=$(git rev-parse --short HEAD) # linuxdeployqt uses this for naming the file
- ./linuxdeployqt-*.AppImage appdir/usr/bin/cool-retro-term -qmldir=./app/qml/ -qmldir=./qmltermwidget/ # -verbose=3 2>&1 | grep "path:" -C 3
- ./linuxdeployqt-*.AppImage appdir/usr/bin/cool-retro-term -qmldir=./app/qml/ -qmldir=./qmltermwidget/ -verbose=2 -appimage
after_success:
- find appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq
- # curl --upload-file Cool*.AppImage https://transfer.sh/Cool_Retro_Term-git.$(git rev-parse --short HEAD)-x86_64.AppImage
- wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh
- bash upload.sh Cool*.AppImage*
branches:
except:
- # Do not build tags that we create when we upload to GitHub Releases
- /^(?i:continuous)/

View File

@@ -1,8 +1,5 @@
QT += qml quick widgets sql quickcontrols2
TARGET = cool-retro-term
APP_VERSION = $$system(git -C $$PWD/.. describe --tags --always --dirty=-dirty)
isEmpty(APP_VERSION): APP_VERSION = "unknown"
DEFINES += APP_VERSION=\\\"$$APP_VERSION\\\"
# TODO: When migrating to CMake, use KDSingleApplication's CMakeLists.txt instead of these manual sources.
INCLUDEPATH += $$PWD/../KDSingleApplication/src

View File

@@ -73,7 +73,7 @@ int main(int argc, char *argv[])
return 0;
}
QString appVersion(QStringLiteral(APP_VERSION));
QString appVersion("1.2.0");
if (argc>1 && (!strcmp(argv[1],"-v") || !strcmp(argv[1],"--version"))) {
QTextStream cout(stdout, QIODevice::WriteOnly);
@@ -86,7 +86,6 @@ int main(int argc, char *argv[])
app.setApplicationName(QStringLiteral("cool-retro-term"));
app.setOrganizationName(QStringLiteral("cool-retro-term"));
app.setOrganizationDomain(QStringLiteral("cool-retro-term"));
app.setApplicationVersion(appVersion);
KDSingleApplication singleApp(QStringLiteral("cool-retro-term"));

View File

@@ -40,6 +40,7 @@ QtObject {
property bool isMacOS: Qt.platform.os === "osx"
// GENERAL SETTINGS ///////////////////////////////////////////////////////
property bool fullscreen: false
property bool showMenubar: false
property bool showTerminalSize: true
@@ -85,7 +86,7 @@ QtObject {
property real horizontalSync: 0.08
property real flickering: 0.1
property real rgbShift: 0.0
property real rbgShift: 0.0
property real _frameShininess: 0.2
property real frameShininess: _frameShininess * 0.5
@@ -187,7 +188,7 @@ QtObject {
"bloom": bloom,
"rasterization": rasterization,
"jitter": jitter,
"rgbShift": rgbShift,
"rbgShift": rbgShift,
"brightness": brightness,
"contrast": contrast,
"ambientLight": ambientLight,
@@ -288,7 +289,7 @@ QtObject {
jitter = settings.jitter !== undefined ? settings.jitter : jitter
rgbShift = settings.rgbShift !== undefined ? settings.rgbShift : rgbShift
rbgShift = settings.rbgShift !== undefined ? settings.rbgShift : rbgShift
ambientLight = settings.ambientLight !== undefined ? settings.ambientLight : ambientLight
contrast = settings.contrast !== undefined ? settings.contrast : contrast
@@ -383,9 +384,9 @@ QtObject {
"horizontalSync": 0.1,
"jitter": 0.2,
"rasterization": 0,
"rgbShift": 0,
"rbgShift": 0,
"saturationColor": 0.2,
"screenCurvature": 0.2,
"screenCurvature": 0.4,
"screenRadius": 0.1,
"staticNoise": 0.1,
"windowOpacity": 1,
@@ -409,7 +410,7 @@ QtObject {
"contrast": 0.8,
"flickering": 0.1,
"fontColor": "#0ccc68",
"fontName": "DEPARTURE_MONO_SCALED",
"fontName": "EXCELSIOR_SCALED",
"fontSource": 0,
"fontWidth": 1,
"lineSpacing": 0.1,
@@ -417,9 +418,9 @@ QtObject {
"horizontalSync": 0.1,
"jitter": 0.2,
"rasterization": 0,
"rgbShift": 0,
"rbgShift": 0,
"saturationColor": 0.0,
"screenCurvature": 0.3,
"screenCurvature": 0.7,
"screenRadius": 0.2,
"staticNoise": 0.1,
"windowOpacity": 1,
@@ -443,7 +444,7 @@ QtObject {
"contrast": 0.8,
"flickering": 0.1,
"fontColor": "#7fb4ff",
"fontName": "BIGBLUE_TERMINAL_SCALED",
"fontName": "COZETTE_SCALED",
"fontSource": 0,
"fontWidth": 1,
"lineSpacing": 0.1,
@@ -451,9 +452,9 @@ QtObject {
"horizontalSync": 0.1,
"jitter": 0.2,
"rasterization": 0,
"rgbShift": 0,
"rbgShift": 0,
"saturationColor": 0.2,
"screenCurvature": 0.4,
"screenCurvature": 1.0,
"screenRadius": 0.1,
"staticNoise": 0.1,
"windowOpacity": 1,
@@ -485,7 +486,7 @@ QtObject {
"horizontalSync": 0.0,
"jitter": 0.0,
"rasterization": 1,
"rgbShift": 0,
"rbgShift": 0,
"saturationColor": 0,
"screenCurvature": 0.5,
"screenRadius": 0.1,
@@ -494,7 +495,7 @@ QtObject {
"margin": 0.3,
"blinkingCursor": false,
"frameSize": 0.5,
"frameColor": "#999999",
"frameColor": "#444444",
"frameShininess": 0.0
}'
builtin: true
@@ -519,15 +520,15 @@ QtObject {
"horizontalSync": 0.2,
"jitter": 0.15,
"rasterization": 1,
"rgbShift": 0.0,
"rbgShift": 0.1,
"saturationColor": 0,
"screenCurvature": 0.7,
"screenCurvature": 1.0,
"screenRadius": 0.3,
"staticNoise": 0.2,
"windowOpacity": 1,
"margin": 0.2,
"blinkingCursor": false,
"frameSize": 0.5,
"frameSize": 0.8,
"frameColor": "#000000",
"frameShininess": 0.6
}'
@@ -553,15 +554,15 @@ QtObject {
"horizontalSync": 0.2,
"jitter": 0.2,
"rasterization": 1,
"rgbShift": 0.0,
"rbgShift": 0.1,
"saturationColor": 0,
"screenCurvature": 0.5,
"screenCurvature": 1.0,
"screenRadius": 0.3,
"staticNoise": 0.2,
"windowOpacity": 1,
"margin": 0.0,
"blinkingCursor": false,
"frameSize": 0.2,
"frameSize": 0.5,
"frameColor": "#ffffff",
"frameShininess": 0.8
}'
@@ -587,9 +588,9 @@ QtObject {
"horizontalSync": 0.0,
"jitter": 0.0,
"rasterization": 1,
"rgbShift": 0.0,
"rbgShift": 0.0,
"saturationColor": 0,
"screenCurvature": 0.4,
"screenCurvature": 0.3,
"screenRadius": 0.2,
"staticNoise": 0.1,
"windowOpacity": 1,
@@ -621,7 +622,7 @@ QtObject {
"horizontalSync": 0.0,
"jitter": 0.0,
"rasterization": 1,
"rgbShift": 0.1,
"rbgShift": 0.15,
"saturationColor": 0,
"screenCurvature": 0.3,
"screenRadius": 0.1,
@@ -636,13 +637,13 @@ QtObject {
builtin: true
}
ListElement {
text: "IBM 3278 Reborn"
text: "IBM 3278"
obj_string: '{
"ambientLight": 0.2,
"backgroundColor": "#000000",
"bloom": 0.2,
"brightness": 0.5,
"burnIn": 0.5,
"burnIn": 0.7,
"chromaColor": 0,
"contrast": 0.8,
"flickering": 0,
@@ -655,7 +656,7 @@ QtObject {
"horizontalSync": 0,
"jitter": 0,
"rasterization": 4,
"rgbShift": 0,
"rbgShift": 0,
"saturationColor": 0,
"screenCurvature": 0,
"screenRadius": 0.0,
@@ -689,7 +690,7 @@ QtObject {
"horizontalSync": 0.0,
"jitter": 0.1,
"rasterization": 4,
"rgbShift": 0.0,
"rbgShift": 0.0,
"saturationColor": 0.6,
"screenCurvature": 0,
"screenRadius": 0.0,
@@ -723,7 +724,7 @@ QtObject {
"horizontalSync": 0.0,
"jitter": 0.0,
"rasterization": 4,
"rgbShift": 0.0,
"rbgShift": 0.1,
"saturationColor": 0.0,
"screenCurvature": 0,
"screenRadius": 0.0,
@@ -757,7 +758,7 @@ QtObject {
"horizontalSync": 0.0,
"jitter": 0.1,
"rasterization": 4,
"rgbShift": 0.1,
"rbgShift": 0.1,
"saturationColor": 0.8,
"screenCurvature": 0,
"screenRadius": 0.0,
@@ -791,7 +792,7 @@ QtObject {
"horizontalSync": 0,
"jitter": 0.0,
"rasterization": 4,
"rgbShift": 0,
"rbgShift": 0,
"saturationColor": 0.0,
"screenCurvature": 0,
"screenRadius": 0.0,
@@ -825,7 +826,7 @@ QtObject {
"horizontalSync": 0.0,
"jitter": 0.0,
"rasterization": 4,
"rgbShift": 0,
"rbgShift": 0,
"saturationColor": 0,
"screenCurvature": 0,
"screenRadius": 0.0,
@@ -863,12 +864,17 @@ QtObject {
var profileArgPosition = args.indexOf("--profile")
if (profileArgPosition !== -1) {
var profileIndex = getProfileIndexByName(args[profileArgPosition + 1])
if (profileIndex !== -1) {
var profileIndex = getProfileIndexByName(
args[profileArgPosition + 1])
if (profileIndex !== -1)
loadProfile(profileIndex)
} else {
else
console.log("Warning: selected profile is not valid; ignoring it")
}
}
if (args.indexOf("--fullscreen") !== -1) {
fullscreen = true
showMenubar = false
}
initializedSettings()

View File

@@ -20,7 +20,6 @@
import QtQuick 2.2
import QtQuick.Controls 2.0
import QtQml
import QMLTermWidget 1.0
@@ -28,7 +27,7 @@ import "menus"
import "utils.js" as Utils
Item{
id: preprocessedTerminal
id: terminalContainer
signal sessionFinished()
property size virtualResolution: Qt.size(kterminal.totalWidth, kterminal.totalHeight)
@@ -48,14 +47,14 @@ Item{
// Manage copy and paste
Connections {
target: copyAction
enabled: terminalContainer.hasFocus
onTriggered: {
kterminal.copyClipboard()
}
}
Connections {
target: pasteAction
enabled: terminalContainer.hasFocus
onTriggered: {
kterminal.pasteClipboard()
}
@@ -66,22 +65,22 @@ Item{
target: appSettings
onFontScalingChanged: {
preprocessedTerminal.updateSources()
terminalContainer.updateSources()
}
onFontWidthChanged: {
preprocessedTerminal.updateSources()
terminalContainer.updateSources()
}
}
Connections {
target: preprocessedTerminal
target: terminalContainer
onWidthChanged: {
preprocessedTerminal.updateSources()
terminalContainer.updateSources()
}
onHeightChanged: {
preprocessedTerminal.updateSources()
terminalContainer.updateSources()
}
}
@@ -119,7 +118,7 @@ Item{
id: ksession
onFinished: {
preprocessedTerminal.sessionFinished()
terminalContainer.sessionFinished()
}
}
@@ -142,15 +141,14 @@ Item{
kterminal.antialiasText = !lowResolutionFont;
kterminal.smooth = !lowResolutionFont;
kterminal.enableBold = !lowResolutionFont;
kterminal.enableItalic = !lowResolutionFont;
kterminal.font = Qt.font({
family: fontFamily,
pixelSize: pixelSize
});
preprocessedTerminal.fontWidth = fontWidth;
preprocessedTerminal.screenScaling = screenScaling;
terminalContainer.fontWidth = fontWidth;
terminalContainer.screenScaling = screenScaling;
scaleTexture = Math.max(1.0, Math.floor(screenScaling * appSettings.windowScaling));
}
@@ -158,7 +156,7 @@ Item{
target: appSettings
onWindowScalingChanged: {
scaleTexture = Math.max(1.0, Math.floor(preprocessedTerminal.screenScaling * appSettings.windowScaling));
scaleTexture = Math.max(1.0, Math.floor(terminalContainer.screenScaling * appSettings.windowScaling));
}
}
@@ -201,7 +199,7 @@ Item{
Loader {
id: menuLoader
sourceComponent: (appSettings.isMacOS || (appSettings.showMenubar && !terminalWindow.fullscreen) ? shortContextMenu : fullContextMenu)
sourceComponent: (appSettings.isMacOS || appSettings.showMenubar ? shortContextMenu : fullContextMenu)
}
property alias contextmenu: menuLoader.item
@@ -214,7 +212,7 @@ Item{
cursorShape: kterminal.terminalUsesMouse ? Qt.ArrowCursor : Qt.IBeamCursor
onWheel: function(wheel) {
if (wheel.modifiers & Qt.ControlModifier) {
wheel.angleDelta.y > 0 ? zoomInAction.trigger() : zoomOutAction.trigger();
wheel.angleDelta.y > 0 ? zoomIn.trigger() : zoomOut.trigger();
} else {
var coord = correctDistortion(wheel.x, wheel.y);
kterminal.simulateWheel(coord.x, coord.y, wheel.buttons, wheel.modifiers, wheel.angleDelta);

View File

@@ -83,8 +83,8 @@ ColumnLayout {
}
CheckableSlider {
name: qsTr("RGB Shift")
onNewValue: function(newValue) { appSettings.rgbShift = newValue }
value: appSettings.rgbShift
onNewValue: function(newValue) { appSettings.rbgShift = newValue }
value: appSettings.rbgShift
}
CheckableSlider {
name: qsTr("Frame Shininess")

View File

@@ -36,7 +36,7 @@ Item {
}
function staticFragmentPath() {
var rgbShiftOn = appSettings.rgbShift > 0 ? 1 : 0;
var rgbShiftOn = appSettings.rbgShift > 0 ? 1 : 0;
var bloomOn = appSettings.bloom > 0 ? 1 : 0;
var curvatureOn = (appSettings.screenCurvature > 0 || appSettings.frameSize > 0) ? 1 : 0;
var shineOn = appSettings.frameShininess > 0 ? 1 : 0;
@@ -176,7 +176,7 @@ Item {
property real chromaColor: appSettings.chromaColor;
property real rgbShift: appSettings.rgbShift * (4.0 / width) * appSettings.totalFontScaling
property real rbgShift: (appSettings.rbgShift / width) * appSettings.totalFontScaling
property real screen_brightness: Utils.lint(0.5, 1.5, appSettings.brightness)
property real frameShininess: appSettings.frameShininess

View File

@@ -28,13 +28,6 @@ ShaderTerminal {
signal sessionFinished()
property bool loadBloomEffect: appSettings.bloom > 0 || appSettings._frameShininess > 0
property bool hasFocus
onHasFocusChanged: {
if (hasFocus) {
activate()
}
}
id: mainShader
opacity: appSettings.windowOpacity * 0.3 + 0.7

View File

@@ -18,18 +18,19 @@
* 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 int innerPadding: 6
readonly property string currentTitle: tabsModel.get(currentIndex).title ?? "cool-retro-term"
readonly property string currentTitle: tabsModel.count > 0
? (tabsModel.get(currentIndex).title ?? "cool-retro-term")
: "cool-retro-term"
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
property var hostWindow
property alias tabsModel: tabsModel
function normalizeTitle(rawTitle) {
if (rawTitle === undefined || rawTitle === null) {
@@ -40,7 +41,7 @@ Item {
function addTab() {
tabsModel.append({ title: "" })
tabBar.currentIndex = tabsModel.count - 1
currentIndex = tabsModel.count - 1
}
function closeTab(index) {
@@ -50,7 +51,7 @@ Item {
}
tabsModel.remove(index)
tabBar.currentIndex = Math.min(tabBar.currentIndex, tabsModel.count - 1)
currentIndex = Math.min(currentIndex, tabsModel.count - 1)
}
ListModel {
@@ -63,75 +64,21 @@ Item {
anchors.fill: parent
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 {
id: stack
Layout.fillWidth: true
Layout.fillHeight: true
currentIndex: tabBar.currentIndex
currentIndex: tabsRoot.currentIndex
Repeater {
model: tabsModel
TerminalContainer {
id: terminalContainer
hasFocus: terminalWindow.active && StackLayout.isCurrentItem
property bool shouldHaveFocus: terminalWindow.active && StackLayout.isCurrentItem
onShouldHaveFocusChanged: {
if (shouldHaveFocus) {
activate()
}
}
onTitleChanged: tabsModel.setProperty(index, "title", normalizeTitle(title))
Layout.fillWidth: true
Layout.fillHeight: true

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,7 +20,7 @@
import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 2.3
import QtQml
import QtQuick.Layouts
import "menus"
@@ -40,27 +40,23 @@ ApplicationWindow {
visible: false
property bool fullscreen: false
flags: Qt.Window | Qt.FramelessWindowHint
property bool fullscreen: appSettings.fullscreen
onFullscreenChanged: visibility = (fullscreen ? Window.FullScreen : Window.Windowed)
menuBar: qtquickMenuLoader.item
Loader {
id: qtquickMenuLoader
active: !appSettings.isMacOS && (appSettings.showMenubar && !fullscreen)
active: !appSettings.isMacOS && appSettings.showMenubar
sourceComponent: WindowMenu { }
}
Connections {
target: newTabAction
enabled: terminalWindow.active
onTriggered: terminalTabs.addTab()
}
Connections {
target: fullscreenAction
enabled: terminalWindow.active
onTriggered: terminalWindow.fullscreen = !terminalWindow.fullscreen
Loader {
id: globalMenuLoader
active: appSettings.isMacOS
sourceComponent: OSXMenu { }
}
property real normalizedWindowScale: 1024 / ((0.5 * width + 0.5 * height))
@@ -69,11 +65,102 @@ ApplicationWindow {
title: terminalTabs.currentTitle
TerminalTabs {
id: terminalTabs
width: parent.width
height: (parent.height + Math.abs(y))
hostWindow: terminalWindow
Action {
id: showMenubarAction
text: qsTr("Show Menubar")
enabled: !appSettings.isMacOS
shortcut: "Ctrl+Shift+M"
checkable: true
checked: appSettings.showMenubar
onTriggered: appSettings.showMenubar = !appSettings.showMenubar
}
Action {
id: fullscreenAction
text: qsTr("Fullscreen")
enabled: !appSettings.isMacOS
shortcut: "Alt+F11"
onTriggered: appSettings.fullscreen = !appSettings.fullscreen
checkable: true
checked: appSettings.fullscreen
}
Action {
id: newWindowAction
text: qsTr("New Window")
shortcut: "Ctrl+Shift+N"
onTriggered: appRoot.createWindow()
}
Action {
id: quitAction
text: qsTr("Quit")
shortcut: "Ctrl+Shift+Q"
onTriggered: appSettings.close()
}
Action {
id: showsettingsAction
text: qsTr("Settings")
onTriggered: {
settingsWindow.show()
settingsWindow.requestActivate()
settingsWindow.raise()
}
}
Action {
id: copyAction
text: qsTr("Copy")
shortcut: "Ctrl+Shift+C"
}
Action {
id: pasteAction
text: qsTr("Paste")
shortcut: "Ctrl+Shift+V"
}
Action {
id: zoomIn
text: qsTr("Zoom In")
shortcut: "Ctrl++"
onTriggered: appSettings.incrementScaling()
}
Action {
id: zoomOut
text: qsTr("Zoom Out")
shortcut: "Ctrl+-"
onTriggered: appSettings.decrementScaling()
}
Action {
id: showAboutAction
text: qsTr("About")
onTriggered: {
aboutDialog.show()
aboutDialog.requestActivate()
aboutDialog.raise()
}
}
Action {
id: newTabAction
text: qsTr("New Tab")
onTriggered: terminalTabs.addTab()
}
ColumnLayout {
anchors.fill: parent
spacing: 0
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 {
anchors.centerIn: parent
@@ -83,6 +170,77 @@ ApplicationWindow {
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: {
appRoot.closeWindow(terminalWindow)
}

View File

@@ -18,9 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
import QtQuick 2.2
import QtQuick.Controls 2.3
import "menus"
QtObject {
id: appRoot
@@ -37,102 +34,23 @@ QtObject {
visible: false
}
property AboutDialog aboutDialog: AboutDialog {
property AboutDialog aboutWindow: AboutDialog {
visible: false
}
property Component windowComponent: Component {
TerminalWindow { }
}
property ListModel windowsModel: ListModel { }
property Loader globalMenuLoader: Loader {
active: appSettings.isMacOS
sourceComponent: OSXMenu { }
}
property Action showMenubarAction: Action {
text: qsTr("Show Menubar")
enabled: !appSettings.isMacOS
shortcut: "Ctrl+Shift+M"
checkable: true
checked: appSettings.showMenubar
onTriggered: appSettings.showMenubar = !appSettings.showMenubar
}
property Action fullscreenAction: Action {
text: qsTr("Fullscreen")
enabled: !appSettings.isMacOS
shortcut: "Alt+F11"
}
property bool initialFullscreenRequested: Qt.application.arguments.indexOf("--fullscreen") !== -1
property Action newWindowAction: Action {
text: qsTr("New Window")
shortcut: "Ctrl+Shift+N"
onTriggered: appRoot.createWindow()
}
property Action quitAction: Action {
text: qsTr("Quit")
shortcut: "Ctrl+Shift+Q"
onTriggered: appSettings.close()
}
property Action showsettingsAction: Action {
text: qsTr("Settings")
onTriggered: {
settingsWindow.show()
settingsWindow.requestActivate()
settingsWindow.raise()
}
}
property Action copyAction: Action {
text: qsTr("Copy")
shortcut: "Ctrl+Shift+C"
}
property Action pasteAction: Action {
text: qsTr("Paste")
shortcut: "Ctrl+Shift+V"
}
property Action zoomInAction: Action {
text: qsTr("Zoom In")
shortcut: "Ctrl++"
onTriggered: appSettings.incrementScaling()
}
property Action zoomOutAction: Action {
text: qsTr("Zoom Out")
shortcut: "Ctrl+-"
onTriggered: appSettings.decrementScaling()
}
property Action showAboutAction: Action {
text: qsTr("About")
onTriggered: {
aboutDialog.show()
aboutDialog.requestActivate()
aboutDialog.raise()
}
}
property Action newTabAction: Action {
text: qsTr("New Tab")
}
function createWindow() {
var useFullscreen = initialFullscreenRequested
var window = windowComponent.createObject(null, { fullscreen: useFullscreen })
var component = Qt.createComponent("TerminalWindow.qml")
if (component.status === Component.Error) {
console.error(component.errorString())
return
}
var window = component.createObject(null)
if (!window)
return
windowsModel.append({ window: window })
initialFullscreenRequested = false
window.show()
window.requestActivate()
}
@@ -148,6 +66,8 @@ QtObject {
window.destroy()
if (windowsModel.count === 0) {
settingsWindow.close()
aboutWindow.close()
appSettings.close()
}
}

View File

@@ -69,10 +69,10 @@ Menu {
visible: showMenubarAction.enabled
}
MenuItem {
action: zoomInAction
action: zoomIn
}
MenuItem {
action: zoomOutAction
action: zoomOut
}
}
Menu {

View File

@@ -64,14 +64,14 @@ MenuBar {
Menu {
title: qsTr("View")
MenuItem {
text: zoomInAction.text
text: zoomIn.text
shortcut: "Meta++"
onTriggered: zoomInAction.trigger()
onTriggered: zoomIn.trigger()
}
MenuItem {
text: zoomOutAction.text
text: zoomOut.text
shortcut: "Meta+-"
onTriggered: zoomOutAction.trigger()
onTriggered: zoomOut.trigger()
}
}
Menu {

View File

@@ -61,10 +61,10 @@ MenuBar {
visible: showMenubarAction.enabled
}
MenuItem {
action: zoomInAction
action: zoomIn
}
MenuItem {
action: zoomOutAction
action: zoomOut
}
}
Menu {

View File

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

View File

@@ -29,7 +29,7 @@ layout(std140, binding = 0) uniform ubuf {
vec2 scaleNoiseSize;
float screen_brightness;
float bloom;
float rgbShift;
float rbgShift;
float frameShadowCoeff;
float frameShininess;
vec4 frameColor;

View File

@@ -20,7 +20,7 @@ layout(std140, binding = 0) uniform ubuf {
mat4 qt_Matrix;
float qt_Opacity;
float screenCurvature;
float rgbShift;
float rbgShift;
float frameShininess;
float frameSize;
float screen_brightness;
@@ -59,7 +59,7 @@ void main() {
vec3 txt_color = texture(source, txt_coords).rgb;
#if CRT_RGB_SHIFT == 1
vec2 displacement = vec2(rgbShift, 0.0);
vec2 displacement = vec2(12.0, 0.0) * rbgShift;
vec3 rightColor = texture(source, txt_coords + displacement).rgb;
vec3 leftColor = texture(source, txt_coords - displacement).rgb;
txt_color.r = leftColor.r * 0.10 + rightColor.r * 0.30 + txt_color.r * 0.60;

View File

@@ -7,7 +7,7 @@ layout(std140, binding = 0) uniform ubuf {
mat4 qt_Matrix;
float qt_Opacity;
float screenCurvature;
float rgbShift;
float rbgShift;
float frameShininess;
float frameSize;
float screen_brightness;

Binary file not shown.

View File

@@ -1,77 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
set -x
REPO_ROOT="$(readlink -f "$(dirname "$(dirname "$0")")")"
OLD_CWD="$(readlink -f .)"
BUILD_DIR="$REPO_ROOT/build/appimage"
TOOLS_DIR="$BUILD_DIR/tools"
if ! command -v qmake >/dev/null; then
echo "qmake not found in PATH." >&2
exit 1
fi
QTDIR="$(qmake -query QT_INSTALL_PREFIX)"
QT_INSTALL_QML="$(qmake -query QT_INSTALL_QML)"
APPDIR="$BUILD_DIR/AppDir"
mkdir -p "$BUILD_DIR"
rm -rf "$APPDIR"
pushd "$BUILD_DIR"
qmake "$REPO_ROOT/cool-retro-term.pro"
make -j"$(nproc)"
# Install targets from subprojects (the top-level install only installs the desktop file).
make -C app install INSTALL_ROOT="$APPDIR"
make -C qmltermwidget install INSTALL_ROOT="$APPDIR"
make install INSTALL_ROOT="$APPDIR"
popd
# Relocate QMLTermWidget into the standard AppDir QML import path.
QML_ROOT="$APPDIR$QT_INSTALL_QML"
if [ -d "$QML_ROOT" ]; then
mkdir -p "$APPDIR/usr/qml"
rsync -a "$QML_ROOT/" "$APPDIR/usr/qml/"
rm -rf "$QML_ROOT"
QML_PARENT="$(dirname "$QML_ROOT")"
while [ "$QML_PARENT" != "$APPDIR" ] && rmdir "$QML_PARENT" 2>/dev/null; do
QML_PARENT="$(dirname "$QML_PARENT")"
done
fi
# Build AppImage using linuxdeploy and linuxdeploy-plugin-qt.
mkdir -p "$TOOLS_DIR"
LINUXDEPLOY="$TOOLS_DIR/linuxdeploy-x86_64.AppImage"
LINUXDEPLOY_QT="$TOOLS_DIR/linuxdeploy-plugin-qt-x86_64.AppImage"
if [ ! -x "$LINUXDEPLOY" ]; then
wget -c -O "$LINUXDEPLOY" https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
chmod +x "$LINUXDEPLOY"
fi
if [ ! -x "$LINUXDEPLOY_QT" ]; then
wget -c -O "$LINUXDEPLOY_QT" https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
chmod +x "$LINUXDEPLOY_QT"
fi
export QML_SOURCES_PATHS="$REPO_ROOT/app"
pushd "$BUILD_DIR"
export EXTRA_PLATFORM_PLUGINS="libqwayland.so;"
export EXTRA_QT_MODULES="sql;waylandcompositor;waylandclient"
export DEPLOY_PLATFORM_THEMES=true
export LINUXDEPLOY_EXCLUDED_LIBRARIES="libmysqlclient.so;libqsqlmimer.so;libqsqlmysql.so;libqsqlodbc.so;libqsqlpsql.so;libqsqloci.so;libqsqlibase.so"
"$LINUXDEPLOY" \
--appdir "$APPDIR" \
-e "$APPDIR/usr/bin/cool-retro-term" \
-i "$REPO_ROOT/app/icons/256x256/cool-retro-term.png" \
-d "$REPO_ROOT/cool-retro-term.desktop" \
--plugin qt \
--output appimage
mv ./*.AppImage "$OLD_CWD"
popd

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
set -x
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
OLD_CWD="$(pwd -P)"
BUILD_DIR="$REPO_ROOT/build/dmg"
APP="cool-retro-term.app"
QML_DIR="$REPO_ROOT/app/qml"
JOBS="${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
if ! command -v qmake >/dev/null; then
echo "qmake not found in PATH." >&2
exit 1
fi
QT_DIR="${QT_DIR:-$(qmake -query QT_INSTALL_PREFIX)}"
QT_BIN="${QT_DIR%/}/bin"
mkdir -p "$BUILD_DIR"
rm -f "$BUILD_DIR/${APP%.app}.dmg"
pushd "$BUILD_DIR"
"$QT_BIN/qmake" CONFIG+=release "$REPO_ROOT/cool-retro-term.pro"
make -j"$JOBS"
PLUGIN_DST="$APP/Contents/PlugIns/qmltermwidget"
rm -rf "$PLUGIN_DST"
mkdir -p "$APP/Contents/PlugIns"
cp -R qmltermwidget/QMLTermWidget "$PLUGIN_DST"
export QML_IMPORT_PATH="$PWD/$APP/Contents/PlugIns"
"$QT_BIN/macdeployqt" "$APP" -qmldir="$QML_DIR" -dmg
rm -f "$APP/Contents/PlugIns/sqldrivers/"libqsql{odbc,psql,mimer}.dylib 2>/dev/null || true
mv "$BUILD_DIR/${APP%.app}.dmg" "$OLD_CWD/"
popd