mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-08-04 14:55:43 +02:00
misc: implement frameless window
Several major changes to the layout have been made. - The chat search bar is moved into the message search layout. - The Searchbar component is stripped of unused features. - Some remaining logic that was used to switch main loader components is removed. - ViewCoordinator.getView gets a "force create" parameter and we no longer preload low-cost views. NOTE: the option to use a frameless window is available within general settings Gitlab: #1524 (Frameless Window) Change-Id: Iec6bdf162cb0335d3ae3d9bd09dd9783991a4a57
This commit is contained in:
parent
788ecaa496
commit
35482fa92f
52 changed files with 908 additions and 323 deletions
|
@ -21,7 +21,7 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
if(APPLE)
|
||||
project(Jami)
|
||||
|
@ -75,6 +75,37 @@ if(NOT MSVC)
|
|||
set(CMAKE_CXX_FLAGS_DEBUG "-Og -ggdb")
|
||||
endif()
|
||||
|
||||
include(${PROJECT_SOURCE_DIR}/extras/build/cmake/contrib_tools.cmake)
|
||||
set(EXTRA_PATCHES_DIR ${PROJECT_SOURCE_DIR}/extras/patches)
|
||||
|
||||
list(APPEND QWINDOWKIT_OPTIONS
|
||||
QWINDOWKIT_BUILD_WIDGETS OFF
|
||||
QWINDOWKIT_INSTALL OFF
|
||||
QWINDOWKIT_BUILD_STATIC ON
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND QWINDOWKIT_PATCHES ${EXTRA_PATCHES_DIR}/0002-workaround-right-margin.patch)
|
||||
list(APPEND QWINDOWKIT_OPTIONS QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS OFF)
|
||||
endif()
|
||||
|
||||
# qmsetup uses the wrong package dir on Fedora at least.
|
||||
check_redhat_based(IS_REDHAT_BASED)
|
||||
if(IS_REDHAT_BASED)
|
||||
list(APPEND QWINDOWKIT_PATCHES ${EXTRA_PATCHES_DIR}/0001-fix-fedora-fc-build.patch)
|
||||
set(qmsetup_cmake_path ${CMAKE_BINARY_DIR}/_install/lib64/cmake/qmsetup)
|
||||
endif()
|
||||
|
||||
# qwindowkit (frameless window)
|
||||
add_fetch_content(
|
||||
TARGET qwindowkit
|
||||
URL https://github.com/stdware/qwindowkit.git
|
||||
BRANCH 79b1f3110754f9c21af2d7dacbd07b1a9dbaf6ef
|
||||
PATCHES ${QWINDOWKIT_PATCHES}
|
||||
OPTIONS ${QWINDOWKIT_OPTIONS}
|
||||
)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${QWindowKit_BINARY_DIR}/include)
|
||||
list(APPEND CLIENT_LIBS QWindowKit::Quick)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
@ -102,7 +133,7 @@ if(QT6_VER AND QT6_PATH)
|
|||
find_package(QT NAMES Qt6 REQUIRED
|
||||
PATHS ${QT6_PATH} NO_DEFAULT_PATH)
|
||||
else()
|
||||
message(STATUS "Looking for Qt 6" ${CMAKE_PREFIX_PATH})
|
||||
message(STATUS "Looking for Qt 6 in ${CMAKE_PREFIX_PATH}")
|
||||
find_package(QT NAMES Qt6 REQUIRED)
|
||||
endif()
|
||||
if (${QT_VERSION_MINOR} GREATER_EQUAL ${QT6_MINVER_MINOR})
|
||||
|
|
84
extras/build/cmake/contrib_tools.cmake
Normal file
84
extras/build/cmake/contrib_tools.cmake
Normal file
|
@ -0,0 +1,84 @@
|
|||
# Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
#
|
||||
# This program 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
include(FetchContent)
|
||||
include(CMakeParseArguments)
|
||||
|
||||
# Helper function to check if the current distribution is Red Hat-based
|
||||
function(check_redhat_based IS_REDHAT_BASED)
|
||||
set(${IS_REDHAT_BASED} FALSE PARENT_SCOPE)
|
||||
# Check for the existence of /etc/os-release
|
||||
if(EXISTS "/etc/os-release")
|
||||
# Read the content of the file
|
||||
file(READ "/etc/os-release" OS_RELEASE_CONTENT)
|
||||
# Check if the distribution is Fedora or Red Hat-based
|
||||
string(REGEX MATCH "ID=fedora|ID_LIKE=\"rhel fedora\"" MATCH_RESULT "${OS_RELEASE_CONTENT}")
|
||||
if(MATCH_RESULT)
|
||||
set(${IS_REDHAT_BASED} TRUE PARENT_SCOPE)
|
||||
message(STATUS "Running on a Red Hat-based distribution (Fedora, RHEL, CentOS, etc.)")
|
||||
else()
|
||||
message(STATUS "Not a Red Hat-based distribution")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Cannot determine the distribution type: /etc/os-release not found")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Helper function to add external content with patches and options.
|
||||
# Parameters:
|
||||
# TARGET: Name of the target to create
|
||||
# URL: URL of the git repository
|
||||
# BRANCH: Branch to checkout
|
||||
# PATCHES: List of patch files to apply
|
||||
# OPTIONS: List of options to set prior to calling FetchContent_MakeAvailable
|
||||
function(add_fetch_content)
|
||||
# Parse function arguments
|
||||
set(oneValueArgs TARGET URL BRANCH)
|
||||
set(multiValueArgs PATCHES OPTIONS)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 AFCWP "" "${oneValueArgs}" "${multiValueArgs}")
|
||||
|
||||
# Create a string for the patch command
|
||||
set(patch_cmd "")
|
||||
# If patches is not empty, start the command with "git apply"
|
||||
if(NOT "${AFCWP_PATCHES}" STREQUAL "")
|
||||
set(patch_cmd git apply)
|
||||
endif()
|
||||
foreach(patch_file IN LISTS AFCWP_PATCHES)
|
||||
list(APPEND patch_cmd "${patch_file}")
|
||||
endforeach()
|
||||
|
||||
# Declare the external content
|
||||
FetchContent_Declare(
|
||||
${AFCWP_TARGET}
|
||||
GIT_REPOSITORY ${AFCWP_URL}
|
||||
GIT_TAG ${AFCWP_BRANCH}
|
||||
PATCH_COMMAND ${patch_cmd}
|
||||
UPDATE_DISCONNECTED 1
|
||||
)
|
||||
|
||||
# Apply options
|
||||
list(LENGTH AFCWP_OPTIONS options_length)
|
||||
math(EXPR max_idx "${options_length} - 1")
|
||||
foreach(idx RANGE 0 ${max_idx} 2)
|
||||
list(GET AFCWP_OPTIONS ${idx} key)
|
||||
math(EXPR value_idx "${idx} + 1")
|
||||
list(GET AFCWP_OPTIONS ${value_idx} value)
|
||||
set(${key} ${value} CACHE STRING "${key}" FORCE)
|
||||
endforeach()
|
||||
|
||||
# Make the content available
|
||||
FetchContent_MakeAvailable(${AFCWP_TARGET})
|
||||
endfunction()
|
|
@ -66,4 +66,9 @@ RUN apt-get install -y -o Acquire::Retries=10 \
|
|||
libssl-dev
|
||||
RUN apt-get install -y pandoc \
|
||||
googletest \
|
||||
libgtest-dev
|
||||
libgtest-dev \
|
||||
wget
|
||||
|
||||
# Install a recent version of CMake
|
||||
ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
|
||||
RUN /opt/install-cmake.sh
|
25
extras/patches/0001-fix-fedora-fc-build.patch
Normal file
25
extras/patches/0001-fix-fedora-fc-build.patch
Normal file
|
@ -0,0 +1,25 @@
|
|||
From 161d28abb6784115ad71fcb6977e112e9d5756d4 Mon Sep 17 00:00:00 2001
|
||||
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
Date: Tue, 23 Jan 2024 15:38:34 -0500
|
||||
Subject: [PATCH] fix-fedora-fc-build
|
||||
|
||||
---
|
||||
CMakeLists.txt | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 0fb89c8..3a6ad6d 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -65,7 +65,7 @@ if(NOT TARGET qmsetup::library)
|
||||
)
|
||||
|
||||
# Find package again
|
||||
- find_package(qmsetup REQUIRED PATHS ${_package_path})
|
||||
+ find_package(qmsetup REQUIRED PATHS ${_package_path} ${qmsetup_cmake_path})
|
||||
|
||||
# Update import path
|
||||
set(qmsetup_DIR ${_package_path} CACHE PATH "" FORCE)
|
||||
--
|
||||
2.34.1
|
||||
|
34
extras/patches/0002-workaround-right-margin.patch
Normal file
34
extras/patches/0002-workaround-right-margin.patch
Normal file
|
@ -0,0 +1,34 @@
|
|||
From ca2be6466c150d1b82a646d97b27df35b45d90f1 Mon Sep 17 00:00:00 2001
|
||||
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
Date: Tue, 9 Jan 2024 15:25:19 -0500
|
||||
Subject: [PATCH] workaround right margin
|
||||
|
||||
---
|
||||
src/core/contexts/win32windowcontext.cpp | 11 +++++++++++
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
|
||||
index 3f6623e..9ee7752 100644
|
||||
--- a/src/core/contexts/win32windowcontext.cpp
|
||||
+++ b/src/core/contexts/win32windowcontext.cpp
|
||||
@@ -402,6 +402,17 @@ namespace QWK {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+
|
||||
+#if !QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
|
||||
+ if (msg->message == WM_MOVE || msg->message == WM_SIZE ||
|
||||
+ (msg->message == WM_IME_SETCONTEXT && (GetForegroundWindow() == msg->hwnd))) {
|
||||
+ static const auto flags = SWP_FRAMECHANGED | SWP_NOMOVE |
|
||||
+ SWP_NOSIZE | SWP_NOZORDER |
|
||||
+ SWP_NOOWNERZORDER;
|
||||
+ SetWindowPos(msg->hwnd, NULL, 0, 0, 0, 0, flags);
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
return false;
|
||||
}
|
||||
|
||||
--
|
||||
2.7.4
|
||||
|
15
resources/icons/window-bar_close.svg
Normal file
15
resources/icons/window-bar_close.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" width="10.88" height="10.88"
|
||||
viewBox="0 0 10.88 10.88">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: none;
|
||||
stroke: white;
|
||||
stroke-miterlimit: 10;
|
||||
stroke-width: 1.25px;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<line class="cls-1" x1="0.44" y1="0.44" x2="10.44" y2="10.44" />
|
||||
<line class="cls-1" x1="0.44" y1="10.44" x2="10.44" y2="0.44" />
|
||||
</svg>
|
After Width: | Height: | Size: 444 B |
11
resources/icons/window-bar_fullscreen.svg
Normal file
11
resources/icons/window-bar_fullscreen.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1594017175519"
|
||||
class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1933"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path
|
||||
d="M874.666667 128h-170.666667a21.333333 21.333333 0 0 0 0 42.666667h119.168l-176.917333 176.917333a21.333333 21.333333 0 1 0 30.165333 30.165333L853.333333 200.832V320a21.333333 21.333333 0 0 0 42.666667 0V149.333333a21.333333 21.333333 0 0 0-21.333333-21.333333zM347.584 646.250667L170.666667 823.168V704a21.333333 21.333333 0 0 0-42.666667 0v170.666667a21.333333 21.333333 0 0 0 21.333333 21.333333h170.666667a21.333333 21.333333 0 0 0 0-42.666667H200.832l176.917333-176.917333a21.333333 21.333333 0 0 0-30.165333-30.165333zM874.666667 682.666667a21.333333 21.333333 0 0 0-21.333334 21.333333v119.168l-176.917333-176.917333a21.333333 21.333333 0 0 0-30.165333 30.165333L823.168 853.333333H704a21.333333 21.333333 0 0 0 0 42.666667h170.666667a21.333333 21.333333 0 0 0 21.333333-21.333333v-170.666667a21.333333 21.333333 0 0 0-21.333333-21.333333zM200.832 170.666667H320a21.333333 21.333333 0 0 0 0-42.666667H149.333333a21.333333 21.333333 0 0 0-21.333333 21.333333v170.666667a21.333333 21.333333 0 0 0 42.666667 0V200.832l176.917333 176.917333a21.333333 21.333333 0 0 0 30.165333-30.165333z"
|
||||
fill="#ffffff" p-id="1934"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
12
resources/icons/window-bar_maximize.svg
Normal file
12
resources/icons/window-bar_maximize.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: none;
|
||||
stroke: white;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="cls-1" x="0.5" y="0.5" width="9" height="9" />
|
||||
</svg>
|
After Width: | Height: | Size: 328 B |
11
resources/icons/window-bar_minimize.svg
Normal file
11
resources/icons/window-bar_minimize.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 10 10" style="enable-background:new 0 0 10 10;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0 {
|
||||
fill: white;
|
||||
}
|
||||
</style>
|
||||
<rect y="4.5" class="st0" width="10" height="1" />
|
||||
</svg>
|
After Width: | Height: | Size: 467 B |
16
resources/icons/window-bar_restore.svg
Normal file
16
resources/icons/window-bar_restore.svg
Normal file
|
@ -0,0 +1,16 @@
|
|||
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: none;
|
||||
stroke: white;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="cls-1" x="0.5" y="2.5" width="9" height="9" />
|
||||
<line class="cls-1" x1="2.5" y1="2.5" x2="2.5" y2="0.5" />
|
||||
<line class="cls-1" x1="12" y1="0.5" x2="2" y2="0.5" />
|
||||
<line class="cls-1" x1="11.5" y1="10" x2="11.5" />
|
||||
<line class="cls-1" x1="10" y1="9.5" x2="12" y2="9.5" />
|
||||
</svg>
|
After Width: | Height: | Size: 559 B |
|
@ -73,17 +73,26 @@ QtObject {
|
|||
function saveWindowSettings() {
|
||||
// If closed-to-tray or minimized or fullscreen, save the cached windowedVisibility
|
||||
// value instead.
|
||||
if (isHidden || isFullScreen) {
|
||||
AppSettingsManager.setValue(Settings.WindowState, priv.windowedVisibility)
|
||||
} else {
|
||||
AppSettingsManager.setValue(Settings.WindowState, visibility)
|
||||
}
|
||||
const visibilityToSave = isHidden || isFullScreen ? priv.windowedVisibility : visibility;
|
||||
|
||||
// Likewise, don't save fullscreen geometry.
|
||||
const geometry = isFullScreen ?
|
||||
priv.windowedGeometry :
|
||||
Qt.rect(appWindow.x, appWindow.y,
|
||||
appWindow.width, appWindow.height)
|
||||
appWindow.width, appWindow.height);
|
||||
|
||||
// QWK: Account for the frameless window's offset.
|
||||
if (appWindow.useFrameless) {
|
||||
if (Qt.platform.os.toString() !== "osx") {
|
||||
// Add [7, 30, 0, 0] on Windows and GNU/Linux.
|
||||
geometry.x += 7;
|
||||
geometry.y += 30;
|
||||
}
|
||||
}
|
||||
|
||||
console.debug("Saving window: " + JSON.stringify(geometry) + " " + visibilityToSave);
|
||||
|
||||
AppSettingsManager.setValue(Settings.WindowState, visibilityToSave)
|
||||
AppSettingsManager.setValue(Settings.WindowGeometry, geometry)
|
||||
}
|
||||
|
||||
|
@ -111,6 +120,8 @@ QtObject {
|
|||
const visibilityStr = AppSettingsManager.getValue(Settings.WindowState)
|
||||
var visibilitySetting = parseInt(visibilityStr)
|
||||
|
||||
console.debug("Restoring window: " + JSON.stringify(geometry) + " " + visibilitySetting)
|
||||
|
||||
// We should never restore a hidden or fullscreen state here. Default to normal
|
||||
// windowed state in such a case. This shouldn't happen.
|
||||
if (visibilitySetting === Window.Hidden || visibilitySetting === Window.FullScreen) {
|
||||
|
|
|
@ -24,29 +24,30 @@ import QtQuick.Window
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Enums 1.1
|
||||
import net.jami.Helpers 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
import "mainview"
|
||||
import "mainview/components"
|
||||
import "wizardview"
|
||||
import "commoncomponents"
|
||||
|
||||
import QWindowKit
|
||||
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
id: appWindow
|
||||
|
||||
property bool isRTL: UtilsAdapter.isRTL
|
||||
|
||||
LayoutMirroring.enabled: isRTL
|
||||
LayoutMirroring.childrenInherit: isRTL
|
||||
|
||||
enum LoadedSource {
|
||||
MainView,
|
||||
AccountMigrationView,
|
||||
None
|
||||
}
|
||||
// This needs to be set from the start.
|
||||
readonly property bool useFrameless: UtilsAdapter.getAppValue(Settings.Key.UseFramelessWindow)
|
||||
|
||||
onActiveFocusItemChanged: {
|
||||
focusOverlay.margin = -5;
|
||||
|
@ -70,7 +71,7 @@ ApplicationWindow {
|
|||
sourceComponent: GenericErrorsRow {
|
||||
id: genericError
|
||||
text: CurrentAccount.enabled ? JamiStrings.noNetworkConnectivity : JamiStrings.disabledAccount
|
||||
height: visible? JamiTheme.chatViewHeaderPreferredHeight : 0
|
||||
height: visible? JamiTheme.qwkTitleBarHeight : 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,37 +89,31 @@ ApplicationWindow {
|
|||
border.color: JamiTheme.tintedBlue
|
||||
}
|
||||
|
||||
property ApplicationWindow appWindow: root
|
||||
property LayoutManager layoutManager: LayoutManager {
|
||||
appContainer: appContainer
|
||||
}
|
||||
property ViewManager viewManager: ViewManager {
|
||||
}
|
||||
property ViewCoordinator viewCoordinator: ViewCoordinator {
|
||||
viewManager: root.viewManager
|
||||
// Used to manage full screen mode and save/restore window geometry.
|
||||
LayoutManager {
|
||||
id: layoutManager
|
||||
appContainer: fullscreenContainer
|
||||
}
|
||||
|
||||
// Used to manage dynamic view loading and unloading.
|
||||
ViewManager {
|
||||
id: viewManager
|
||||
}
|
||||
|
||||
// Used to manage the view stack and the current view.
|
||||
ViewCoordinator {
|
||||
id: viewCoordinator
|
||||
}
|
||||
|
||||
// Used to prevent the window from being visible until the
|
||||
// window geometry has been restored and the view stack has
|
||||
// been loaded.
|
||||
property bool windowSettingsLoaded: false
|
||||
|
||||
// This setting can be used to block a loading Jami instance
|
||||
// from showNormal() and showMaximized() when starting minimized.
|
||||
property bool allowVisibleWindow: true
|
||||
|
||||
function checkLoadedSource() {
|
||||
var sourceString = mainApplicationLoader.source.toString();
|
||||
if (sourceString === JamiQmlUtils.mainViewLoadPath)
|
||||
return MainApplicationWindow.LoadedSource.MainView;
|
||||
return MainApplicationWindow.LoadedSource.None;
|
||||
}
|
||||
|
||||
function startClient() {
|
||||
setMainLoaderSource(JamiQmlUtils.mainViewLoadPath);
|
||||
}
|
||||
|
||||
function setMainLoaderSource(source) {
|
||||
if (checkLoadedSource() === MainApplicationWindow.LoadedSource.MainView) {
|
||||
cleanupMainView();
|
||||
}
|
||||
mainApplicationLoader.setSource(source);
|
||||
}
|
||||
|
||||
function cleanupMainView() {
|
||||
// Save the main view window size if loading anything else.
|
||||
layoutManager.saveWindowSettings();
|
||||
|
@ -139,88 +134,148 @@ ApplicationWindow {
|
|||
|
||||
title: JamiStrings.appTitle
|
||||
|
||||
visible: mainApplicationLoader.status === Loader.Ready && windowSettingsLoaded && allowVisibleWindow
|
||||
visible: mainViewLoader.status === Loader.Ready && windowSettingsLoaded && allowVisibleWindow
|
||||
|
||||
// To facilitate reparenting of the callview during
|
||||
// fullscreen mode, we need QQuickItem based object.
|
||||
Item {
|
||||
id: appContainer
|
||||
Connections {
|
||||
id: connectionMigrationEnded
|
||||
|
||||
anchors.fill: parent
|
||||
target: CurrentAccountToMigrate
|
||||
|
||||
function onAccountNeedsMigration(accountId) {
|
||||
viewCoordinator.present("AccountMigrationView");
|
||||
}
|
||||
|
||||
function onAllMigrationsFinished() {
|
||||
viewCoordinator.dismiss("AccountMigrationView");
|
||||
viewCoordinator.present("WelcomePage");
|
||||
}
|
||||
}
|
||||
|
||||
function initMainView(view) {
|
||||
console.info("Initializing main view");
|
||||
|
||||
// Main window, load any valid app settings, and allow the
|
||||
// layoutManager to handle as much as possible.
|
||||
layoutManager.restoreWindowSettings();
|
||||
|
||||
// QWK: setup
|
||||
if (useFrameless) {
|
||||
windowAgent.setTitleBar(titleBar);
|
||||
// Now register the system buttons (non-macOS).
|
||||
if (sysBtnsLoader.item) {
|
||||
const sysBtns = sysBtnsLoader.item;
|
||||
windowAgent.setSystemButton(WindowAgent.Minimize, sysBtns.minButton);
|
||||
windowAgent.setSystemButton(WindowAgent.Maximize, sysBtns.maxButton);
|
||||
windowAgent.setSystemButton(WindowAgent.Close, sysBtns.closeButton);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the viewCoordinator's root item.
|
||||
viewCoordinator.init(view);
|
||||
|
||||
// Navigate to something.
|
||||
if (UtilsAdapter.getAccountListSize() > 0) {
|
||||
// Already have an account.
|
||||
if (CurrentAccountToMigrate.accountToMigrateListSize > 0)
|
||||
// Do we need to migrate any accounts?
|
||||
viewCoordinator.present("AccountMigrationView");
|
||||
else
|
||||
// Okay now just start the client normally.
|
||||
viewCoordinator.present("WelcomePage");
|
||||
} else {
|
||||
// No account, so start the wizard.
|
||||
viewCoordinator.present("WizardView");
|
||||
}
|
||||
|
||||
// Set up the event filter for macOS.
|
||||
if (Qt.platform.os.toString() === "osx") {
|
||||
MainApplication.setEventFilter();
|
||||
}
|
||||
|
||||
// Quiet check for updates on start if set to.
|
||||
if (Qt.platform.os.toString() === "windows") {
|
||||
if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) {
|
||||
AppVersionManager.checkForUpdates(true);
|
||||
AppVersionManager.setAutoUpdateCheck(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle a start URI if set as start option.
|
||||
MainApplication.handleUriAction();
|
||||
|
||||
// This will allow visible to become true if not starting minimized.
|
||||
windowSettingsLoaded = true;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// QWK: setup
|
||||
if (useFrameless) {
|
||||
windowAgent.setup(appWindow);
|
||||
}
|
||||
|
||||
mainViewLoader.active = true;
|
||||
|
||||
// Dbus error handler for Linux.
|
||||
if (Qt.platform.os.toString() !== "windows" && Qt.platform.os.toString() !== "osx")
|
||||
DBusErrorHandler.setActive(true);
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: mainApplicationLoader
|
||||
|
||||
id: mainViewLoader
|
||||
active: false
|
||||
source: "qrc:/mainview/MainView.qml"
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
onLoaded: initMainView(item)
|
||||
}
|
||||
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
// Use this as a parent for fullscreen items.
|
||||
Item {
|
||||
id: fullscreenContainer
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Connections {
|
||||
id: connectionMigrationEnded
|
||||
// QWK: Provide spacing for widgets that may be occluded by the system buttons.
|
||||
QtObject {
|
||||
id: qwkSystemButtonSpacing
|
||||
readonly property bool isMacOS: Qt.platform.os.toString() === "osx"
|
||||
readonly property bool isFullscreen: layoutManager.isFullScreen
|
||||
// macOS buttons are on the left.
|
||||
readonly property real left: useFrameless && isMacOS && viewCoordinator.isInSinglePaneMode ? 80 : 0
|
||||
// Windows and Linux buttons are on the right.
|
||||
readonly property real right: useFrameless && !isMacOS && !isFullscreen ? sysBtnsLoader.width + 24 : 0
|
||||
}
|
||||
|
||||
target: CurrentAccountToMigrate
|
||||
|
||||
function onAccountNeedsMigration(accountId) {
|
||||
viewCoordinator.present("AccountMigrationView");
|
||||
}
|
||||
|
||||
function onAllMigrationsFinished() {
|
||||
viewCoordinator.dismiss("AccountMigrationView");
|
||||
startClient();
|
||||
}
|
||||
// QWK: Window Title bar
|
||||
Item {
|
||||
id: titleBar
|
||||
height: JamiTheme.qwkTitleBarHeight
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
// Set `visible = false` when loading a new QML file.
|
||||
onSourceChanged: windowSettingsLoaded = false
|
||||
|
||||
onLoaded: {
|
||||
if (UtilsAdapter.getAccountListSize() === 0) {
|
||||
layoutManager.restoreWindowSettings();
|
||||
if (!viewCoordinator.rootView)
|
||||
// Set the viewCoordinator's root item.
|
||||
viewCoordinator.init(item);
|
||||
viewCoordinator.present("WizardView");
|
||||
} else {
|
||||
// Main window, load any valid app settings, and allow the
|
||||
// layoutManager to handle as much as possible.
|
||||
layoutManager.restoreWindowSettings();
|
||||
|
||||
// Present the welcome view once the viewCoordinator is setup.
|
||||
viewCoordinator.initialized.connect(function () {
|
||||
viewCoordinator.preload("SidePanel");
|
||||
viewCoordinator.preload("SettingsSidePanel");
|
||||
viewCoordinator.present("WelcomePage");
|
||||
viewCoordinator.preload("ConversationView");
|
||||
});
|
||||
if (!viewCoordinator.rootView)
|
||||
// Set the viewCoordinator's root item.
|
||||
viewCoordinator.init(item);
|
||||
if (CurrentAccountToMigrate.accountToMigrateListSize > 0)
|
||||
viewCoordinator.present("AccountMigrationView");
|
||||
// On Windows and Linux, use custom system buttons.
|
||||
Loader {
|
||||
id: sysBtnsLoader
|
||||
active: Qt.platform.os.toString() !== "osx" && useFrameless
|
||||
height: titleBar.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
// Note: leave these margins, they prevent image scaling artifacts
|
||||
topMargin: 1
|
||||
rightMargin: 1
|
||||
}
|
||||
if (Qt.platform.os.toString() === "osx") {
|
||||
MainApplication.setEventFilter();
|
||||
}
|
||||
|
||||
// This will trigger `visible = true`.
|
||||
windowSettingsLoaded = true;
|
||||
|
||||
// Quiet check for updates on start if set to.
|
||||
if (Qt.platform.os.toString() === "windows") {
|
||||
if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) {
|
||||
AppVersionManager.checkForUpdates(true);
|
||||
AppVersionManager.setAutoUpdateCheck(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle a start URI if set as start option.
|
||||
MainApplication.handleUriAction();
|
||||
source: "qrc:/commoncomponents/QWKSystemButtonGroup.qml"
|
||||
}
|
||||
}
|
||||
|
||||
// QWK: Main interop component.
|
||||
WindowAgent {
|
||||
id: windowAgent
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: LRCInstance
|
||||
|
||||
|
@ -339,11 +394,5 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
onClosing: root.close()
|
||||
|
||||
Component.onCompleted: {
|
||||
startClient();
|
||||
if (Qt.platform.os.toString() !== "windows" && Qt.platform.os.toString() !== "osx")
|
||||
DBusErrorHandler.setActive(true);
|
||||
}
|
||||
onClosing: appWindow.close()
|
||||
}
|
||||
|
|
|
@ -24,14 +24,6 @@ import "commoncomponents"
|
|||
QtObject {
|
||||
id: root
|
||||
|
||||
required property QtObject viewManager
|
||||
|
||||
signal initialized
|
||||
|
||||
function requestAppWindowWizardView() {
|
||||
viewCoordinator.present("WizardView");
|
||||
}
|
||||
|
||||
// A map of view names to file paths for QML files that define each view.
|
||||
property variant resources: {
|
||||
"SidePanel": "mainview/components/SidePanel.qml",
|
||||
|
@ -47,12 +39,44 @@ QtObject {
|
|||
// The `main` view of the application window.
|
||||
property StackView rootView
|
||||
|
||||
property var currentViewName: rootView && rootView.currentItem && rootView.currentItem.objectName || null
|
||||
readonly property Item currentView: rootView && rootView.currentItem || null
|
||||
readonly property var currentViewName: currentView && currentView.objectName || null
|
||||
readonly property bool isDualPane: currentView && currentView instanceof DualPaneView
|
||||
readonly property bool isInSinglePaneMode: !isDualPane || currentView.isSinglePane
|
||||
readonly property bool isRTL: Qt.application.layoutDirection === Qt.RightToLeft
|
||||
// A list of the current visible views. This could be a single view or two views in
|
||||
// dual pane mode. The list is ordered [minor, major] where major is the view on the
|
||||
// right side when not in RTL and should represent the main or content-type view.
|
||||
readonly property var visibleViews: {
|
||||
if (!currentView)
|
||||
return []
|
||||
if (isDualPane) {
|
||||
if (isInSinglePaneMode)
|
||||
return [currentView.rightPaneItem]
|
||||
return [currentView.leftPaneItem, currentView.rightPaneItem]
|
||||
}
|
||||
return [currentView]
|
||||
}
|
||||
// Aggregate this info and expose it as a single string for convenience.
|
||||
// JSON indented by 2 spaces.
|
||||
readonly property string currentViewInfo: {
|
||||
var info = {
|
||||
currentViewName: currentViewName,
|
||||
isDualPane: isDualPane,
|
||||
isInSinglePaneMode: isInSinglePaneMode,
|
||||
visibleViews: visibleViews.map(function(view) {
|
||||
return view && view.objectName || null;
|
||||
}),
|
||||
visibleViewWidths: visibleViews.map(function(view) {
|
||||
return view && view.width || null;
|
||||
}),
|
||||
};
|
||||
return JSON.stringify(info, null, 2);
|
||||
}
|
||||
|
||||
function init(mainStackView) {
|
||||
rootView = Qt.createQmlObject(`import QtQuick; import QtQuick.Controls
|
||||
StackView { anchors.fill: parent }`, mainStackView);
|
||||
initialized();
|
||||
}
|
||||
|
||||
function deinit() {
|
||||
|
@ -171,6 +195,8 @@ QtObject {
|
|||
var objectName = view ? view.objectName : obj.objectName;
|
||||
if (!viewManager.destroyView(resources[objectName])) {
|
||||
print("could not destroy view:", objectName);
|
||||
} else {
|
||||
print("destroyed view:", objectName);
|
||||
}
|
||||
} else
|
||||
view.dismissed();
|
||||
|
@ -197,8 +223,20 @@ QtObject {
|
|||
}
|
||||
}
|
||||
|
||||
function getView(viewName) {
|
||||
return viewManager.getView(viewName);
|
||||
function getView(viewName, forceCreate = false) {
|
||||
// If the view is already loaded, return it.
|
||||
var view = viewManager.getView(viewName);
|
||||
if (view)
|
||||
return view;
|
||||
if (!forceCreate)
|
||||
return null;
|
||||
// Otherwise, create it.
|
||||
view = viewManager.createView(resources[viewName], null);
|
||||
if (!view) {
|
||||
console.log("Failed to load view: " + viewName);
|
||||
return null;
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
// Load a view without presenting it.
|
||||
|
|
|
@ -33,6 +33,14 @@
|
|||
extern const QString defaultDownloadPath;
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Define USE_FRAMELESS_WINDOW_DEFAULT based on the platform
|
||||
#ifdef Q_OS_LINUX
|
||||
#define USE_FRAMELESS_WINDOW_DEFAULT false
|
||||
#else
|
||||
#define USE_FRAMELESS_WINDOW_DEFAULT true
|
||||
#endif
|
||||
|
||||
// Common key-value pairs for both APPSTORE and non-APPSTORE builds
|
||||
#define COMMON_KEYS \
|
||||
X(MinimizeOnClose, false) \
|
||||
|
@ -66,7 +74,8 @@ extern const QString defaultDownloadPath;
|
|||
X(ChatViewEnterIsNewLine, false) \
|
||||
X(ShowSendOption, false) \
|
||||
X(EnablePtt, false) \
|
||||
X(PttKeys, 32)
|
||||
X(PttKeys, 32) \
|
||||
X(UseFramelessWindow, USE_FRAMELESS_WINDOW_DEFAULT)
|
||||
#ifdef APPSTORE
|
||||
#define KEYS COMMON_KEYS
|
||||
#else
|
||||
|
|
|
@ -35,10 +35,12 @@ Rectangle {
|
|||
signal dismissed
|
||||
|
||||
Component.onCompleted: {
|
||||
console.debug("Created", objectName);
|
||||
if (managed)
|
||||
presented();
|
||||
}
|
||||
Component.onDestruction: {
|
||||
console.debug("Destroyed", objectName);
|
||||
if (managed)
|
||||
dismissed();
|
||||
}
|
||||
|
|
82
src/app/commoncomponents/QWKButton.qml
Normal file
82
src/app/commoncomponents/QWKButton.qml
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
width: height * 0.9156626506024096
|
||||
leftInset: 0
|
||||
topInset: 0
|
||||
rightInset: 0
|
||||
bottomInset: 0
|
||||
padding: 0
|
||||
|
||||
function calculateLuminance(color) {
|
||||
return 0.299 * color.r + 0.587 * color.g + 0.114 * color.b;
|
||||
}
|
||||
|
||||
property alias source: image.source
|
||||
contentItem: Item {
|
||||
Image {
|
||||
id: image
|
||||
anchors.centerIn: parent
|
||||
mipmap: true
|
||||
width: 12
|
||||
height: 12
|
||||
layer.enabled: true
|
||||
layer.effect: ColorOverlay {
|
||||
color: {
|
||||
// We may force this color to be white, when the background is dark.
|
||||
var backgroundIsDark = calculateLuminance(control.background.color) > 0.25;
|
||||
// This includes when we are in a call (which has a dark background).
|
||||
backgroundIsDark = backgroundIsDark || CurrentConversation.hasCall;
|
||||
return backgroundIsDark ? "white" : JamiTheme.primaryForegroundColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property color baseColor: {
|
||||
// Avoid transparent if the background is dark i.e. in a call.
|
||||
if (CurrentConversation.hasCall)
|
||||
return Qt.rgba(1, 1, 1, 0.5);
|
||||
return JamiTheme.darkTheme ? Qt.rgba(1, 1, 1, 0.15) : Qt.rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
readonly property color pressedColor: {
|
||||
const darker = Qt.darker(baseColor, 1.3);
|
||||
return Qt.rgba(darker.r, darker.g, darker.b, baseColor.a * 1.3);
|
||||
}
|
||||
background: Rectangle {
|
||||
color: {
|
||||
if (!control.enabled)
|
||||
return "gray";
|
||||
if (control.pressed)
|
||||
return control.pressedColor;
|
||||
if (control.hovered)
|
||||
return control.baseColor;
|
||||
return "transparent";
|
||||
}
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
}
|
||||
}
|
39
src/app/commoncomponents/QWKSetParentHitTestVisible.qml
Normal file
39
src/app/commoncomponents/QWKSetParentHitTestVisible.qml
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
// Wait for it's parent to be created, then set it to be hit test visible.
|
||||
// This avoids having to edit Component.onCompleted of the parent.
|
||||
// Note: this is experimental. TBD if this is a good way to do this.
|
||||
// This technique makes it clear and simple to implement, but may have
|
||||
// side effects beyond just adding a dummy item component.
|
||||
// Best alternatives:
|
||||
// - Wrap the parent in a custom component that is hit test visible.
|
||||
// - Edit the parent's Component.onCompleted to set it to be hit test visible.
|
||||
Component.onCompleted: Qt.callLater(function() {
|
||||
if (appWindow && appWindow.useFrameless)
|
||||
windowAgent.setHitTestVisible(parent, true);
|
||||
});
|
||||
|
||||
// Likewise, wait for it's parent to be destroyed, then set it to be hit test invisible.
|
||||
Component.onDestruction: {
|
||||
if (appWindow && appWindow.useFrameless)
|
||||
windowAgent.setHitTestVisible(parent, false);
|
||||
}
|
||||
}
|
60
src/app/commoncomponents/QWKSystemButtonGroup.qml
Normal file
60
src/app/commoncomponents/QWKSystemButtonGroup.qml
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
import QWindowKit
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
property alias minButton: minButton
|
||||
property alias maxButton: maxButton
|
||||
property alias closeButton: closeButton
|
||||
|
||||
component SystemButton : QWKButton {
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
visible: appWindow.visibility !== Window.FullScreen
|
||||
|
||||
SystemButton {
|
||||
id: minButton
|
||||
source: JamiResources.window_bar_minimize_svg
|
||||
onClicked: appWindow.showMinimized()
|
||||
}
|
||||
|
||||
SystemButton {
|
||||
id: maxButton
|
||||
source: appWindow.visibility === Window.Maximized ?
|
||||
JamiResources.window_bar_restore_svg :
|
||||
JamiResources.window_bar_maximize_svg
|
||||
onClicked: appWindow.visibility === Window.Maximized ?
|
||||
appWindow.showNormal() :
|
||||
appWindow.showMaximized()
|
||||
}
|
||||
|
||||
SystemButton {
|
||||
id: closeButton
|
||||
source: JamiResources.window_bar_close_svg
|
||||
baseColor: "#e81123"
|
||||
onClicked: appWindow.close()
|
||||
}
|
||||
}
|
|
@ -1,10 +1,53 @@
|
|||
import QtQuick
|
||||
/*
|
||||
* Copyright (C) 2022-2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
Rectangle {
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Page {
|
||||
id: root
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
property color color: "transparent"
|
||||
|
||||
// QWK: Title bar spacing for macOS and single pane mode.
|
||||
// Not using topMargin here on purpose, to make is simple to
|
||||
// keep the theme coloring without wrapping components that
|
||||
// derive from SidePanelBase.
|
||||
header: Rectangle {
|
||||
id: titleBarSpacer
|
||||
height: {
|
||||
if (!appWindow.useFrameless)
|
||||
return 0;
|
||||
var extraHeight = 0;
|
||||
if (Qt.platform.os.toString() === "osx")
|
||||
extraHeight = 24;
|
||||
else if (viewCoordinator.isInSinglePaneMode)
|
||||
extraHeight = titleBar.height;
|
||||
return extraHeight;
|
||||
}
|
||||
color: root.color
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: root.color
|
||||
}
|
||||
|
||||
// Override these if needed.
|
||||
property var select: function () {}
|
||||
property var deselect: function () {}
|
||||
|
|
|
@ -24,9 +24,6 @@ import net.jami.Enums 1.1
|
|||
|
||||
Item {
|
||||
property string qmlFilePrefix: "file:/"
|
||||
|
||||
readonly property string mainViewLoadPath: "qrc:/mainview/MainView.qml"
|
||||
readonly property string wizardViewLoadPath: "qrc:/wizardview/WizardView.qml"
|
||||
readonly property string base64StringTitle: "data:image/png;base64,"
|
||||
|
||||
property var accountCreationInputParaObject: ({})
|
||||
|
@ -81,8 +78,7 @@ Item {
|
|||
function onDonationCampaignSettingsChanged() {
|
||||
// Changing any of the donation campaign settings will trigger a recompute
|
||||
// of the banner visibility.
|
||||
updateIsDonationBannerVisible();
|
||||
}
|
||||
updateIsDonationBannerVisible(); }
|
||||
}
|
||||
|
||||
function updateIsDonationBannerVisible() {
|
||||
|
|
|
@ -477,6 +477,7 @@ Item {
|
|||
property string enableNotifications: qsTr("Enable notifications")
|
||||
property string showNotifications: qsTr("Show notifications")
|
||||
property string keepMinimized: qsTr("Minimize on close")
|
||||
property string useNativeWindowFrame: qsTr("Use native window frame (requires restart)")
|
||||
property string tipRunStartup: qsTr("Run at system startup")
|
||||
property string runStartup: qsTr("Launch at startup")
|
||||
property string downloadFolder: qsTr("Choose download directory")
|
||||
|
|
|
@ -474,7 +474,6 @@ Item {
|
|||
// MessageWebView
|
||||
property real chatViewHairLineSize: 1
|
||||
property real chatViewMaximumWidth: 900
|
||||
property real chatViewHeaderPreferredHeight: 50
|
||||
property real chatViewFooterPreferredHeight: 35
|
||||
property real chatViewFooterMaximumHeight: 315
|
||||
property real chatViewFooterRowSpacing: 4
|
||||
|
@ -710,4 +709,7 @@ Item {
|
|||
property color chatSettingButtonBackgroundColor: darkTheme ? "#303030" : "#F0EFEF"
|
||||
property color chatSettingButtonBorderColor: darkTheme ? "#03B9E9" : "#005699"
|
||||
property color chatSettingButtonTextColor: textColor
|
||||
|
||||
// QWK
|
||||
property real qwkTitleBarHeight: 50
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include "systemtray.h"
|
||||
#include "videoprovider.h"
|
||||
|
||||
#include <QWKQuick/qwkquickglobal.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
|
@ -138,7 +140,6 @@ ScreenInfo::onPhysicalDotsPerInchChanged()
|
|||
|
||||
MainApplication::MainApplication(int& argc, char** argv)
|
||||
: QApplication(argc, argv)
|
||||
, isCleanupped(false)
|
||||
{
|
||||
const char* qtVersion = qVersion();
|
||||
if (strncmp(qtVersion, QT_VERSION_STR, strnlen(qtVersion, sizeof qtVersion)) != 0) {
|
||||
|
@ -166,8 +167,6 @@ MainApplication::MainApplication(int& argc, char** argv)
|
|||
// the logging features.
|
||||
qInstallMessageHandler(messageHandler);
|
||||
|
||||
QObject::connect(this, &QApplication::aboutToQuit, this, &MainApplication::cleanup);
|
||||
|
||||
qCInfo(app_) << "Using Qt runtime version:" << qtVersion;
|
||||
}
|
||||
|
||||
|
@ -184,6 +183,8 @@ MainApplication::init()
|
|||
// performing unnecessary tasks, like initializing the webengine.
|
||||
engine_.reset(new QQmlApplicationEngine(this));
|
||||
|
||||
QWK::registerTypes(engine_.get());
|
||||
|
||||
connectivityMonitor_ = new ConnectivityMonitor(this);
|
||||
settingsManager_ = new AppSettingsManager(this);
|
||||
systemTray_ = new SystemTray(settingsManager_, this);
|
||||
|
@ -466,17 +467,6 @@ MainApplication::initSystray()
|
|||
systemTray_->show();
|
||||
}
|
||||
|
||||
void
|
||||
MainApplication::cleanup()
|
||||
{
|
||||
// In Qt 6.5, QApplication::exit(0) will signal aboutToQuit, and aboutToQuit is connected to cleanup
|
||||
// TODO: delete cleanup.
|
||||
if (!isCleanupped) {
|
||||
isCleanupped = true;
|
||||
QApplication::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainApplication::setEventFilter()
|
||||
{
|
||||
|
|
|
@ -108,7 +108,6 @@ private:
|
|||
void setApplicationFont();
|
||||
void initQmlLayer();
|
||||
void initSystray();
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
std::map<Option, QVariant> runOptions_;
|
||||
|
@ -123,6 +122,4 @@ private:
|
|||
AppSettingsManager* settingsManager_;
|
||||
|
||||
ScreenInfo screenInfo_;
|
||||
|
||||
bool isCleanupped;
|
||||
};
|
||||
|
|
|
@ -45,9 +45,11 @@ ListSelectionView {
|
|||
|
||||
color: JamiTheme.transparentColor
|
||||
|
||||
leftPaneItem: viewCoordinator.getView("SidePanel")
|
||||
leftPaneItem: viewCoordinator.getView("SidePanel", true)
|
||||
|
||||
rightPaneItem: StackLayout {
|
||||
objectName: "ConversationLayout"
|
||||
|
||||
currentIndex: !CurrentConversation.hasCall ? 0 : 1
|
||||
onCurrentIndexChanged: chatView.parent = currentIndex === 1 ? callStackView.chatViewContainer : chatViewContainer
|
||||
|
||||
|
|
|
@ -36,13 +36,8 @@ import "js/keyboardshortcuttablecreation.js" as KeyboardShortcutTableCreation
|
|||
|
||||
Rectangle {
|
||||
id: mainView
|
||||
|
||||
objectName: "mainView"
|
||||
|
||||
// To calculate tab bar bottom border hidden rect left margin.
|
||||
property int tabBarLeftMargin: 8
|
||||
property int tabButtonShrinkSize: 8
|
||||
|
||||
property string currentConvId: CurrentConversation.id
|
||||
onCurrentConvIdChanged: {
|
||||
if (currentConvId !== '') {
|
||||
|
|
|
@ -115,8 +115,8 @@ Rectangle {
|
|||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
|
||||
Layout.maximumHeight: JamiTheme.chatViewHeaderPreferredHeight
|
||||
Layout.preferredHeight: JamiTheme.qwkTitleBarHeight
|
||||
Layout.maximumHeight: JamiTheme.qwkTitleBarHeight
|
||||
Layout.minimumWidth: JamiTheme.mainViewPaneMinWidth
|
||||
|
||||
DropArea {
|
||||
|
@ -180,14 +180,14 @@ Rectangle {
|
|||
ConversationErrorsRow {
|
||||
id: errorRect
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
|
||||
Layout.preferredHeight: JamiTheme.qwkTitleBarHeight
|
||||
visible: false
|
||||
}
|
||||
|
||||
NotificationArea {
|
||||
id: notificationArea
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: JamiTheme.chatViewHeaderPreferredHeight
|
||||
Layout.preferredHeight: JamiTheme.qwkTitleBarHeight
|
||||
visible: CurrentConversation.activeCalls.length > 0 && !root.inCallView
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
property bool interactionButtonsVisibility: {
|
||||
readonly property bool interactionButtonsVisibility: {
|
||||
if (CurrentConversation.inCall)
|
||||
return false;
|
||||
if (LRCInstance.currentAccountType === Profile.Type.SIP)
|
||||
|
@ -74,16 +74,17 @@ Rectangle {
|
|||
id: messagingHeaderRectRowLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 8
|
||||
// QWK: spacing
|
||||
anchors.leftMargin: qwkSystemButtonSpacing.left
|
||||
anchors.rightMargin: 10 + qwkSystemButtonSpacing.right
|
||||
spacing: 16
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: backToWelcomeViewButton
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.leftMargin: 8
|
||||
|
||||
//preferredSize: 24
|
||||
mirror: UtilsAdapter.isRTL
|
||||
|
||||
source: JamiResources.back_24dp_svg
|
||||
|
@ -106,10 +107,11 @@ Rectangle {
|
|||
|
||||
color: JamiTheme.transparentColor
|
||||
|
||||
ColumnLayout {
|
||||
ColumnLayout { QWKSetParentHitTestVisible {}
|
||||
id: userNameOrIdColumnLayout
|
||||
objectName: "userNameOrIdColumnLayout"
|
||||
|
||||
anchors.fill: parent
|
||||
height: parent.height
|
||||
|
||||
spacing: 0
|
||||
|
||||
|
@ -144,63 +146,30 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
Searchbar {
|
||||
id: rowSearchBar
|
||||
|
||||
reductionEnabled: true
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
Layout.preferredHeight: 30
|
||||
Layout.preferredWidth: 40 + (isOpen ? JamiTheme.searchbarSize : 0)
|
||||
colorSearchBar: JamiTheme.backgroundColor
|
||||
|
||||
hoverButtonRadius: JamiTheme.chatViewHeaderButtonRadius
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
}
|
||||
}
|
||||
|
||||
visible: root.swarmDetailsVisibility
|
||||
|
||||
onSearchBarTextChanged: function (text) {
|
||||
MessagesAdapter.searchbarPrompt = text;
|
||||
}
|
||||
|
||||
onSearchClicked: extrasPanel.switchToPanel(ChatView.MessagesResearchPanel)
|
||||
|
||||
Shortcut {
|
||||
sequence: "Ctrl+Shift+F"
|
||||
context: Qt.ApplicationShortcut
|
||||
enabled: rowSearchBar.visible
|
||||
onActivated: {
|
||||
rowSearchBar.openSearchBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: startAAudioCallButton
|
||||
|
||||
visible: interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
|
||||
visible: interactionButtonsVisibility &&
|
||||
(!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
|
||||
source: JamiResources.place_audiocall_24dp_svg
|
||||
toolTipText: JamiStrings.placeAudioCall
|
||||
|
||||
onClicked: CallAdapter.placeAudioOnlyCall()
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: startAVideoCallButton
|
||||
|
||||
visible: CurrentAccount.videoEnabled_Video && interactionButtonsVisibility && (!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
|
||||
visible: interactionButtonsVisibility &&
|
||||
CurrentAccount.videoEnabled_Video &&
|
||||
(!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
|
||||
source: JamiResources.videocam_24dp_svg
|
||||
toolTipText: JamiStrings.placeVideoCall
|
||||
|
||||
onClicked: CallAdapter.placeCall()
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: addParticipantsButton
|
||||
|
||||
checkable: true
|
||||
|
@ -212,7 +181,7 @@ Rectangle {
|
|||
onClicked: extrasPanel.switchToPanel(ChatView.AddMemberPanel)
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: selectPluginButton
|
||||
|
||||
visible: PluginAdapter.chatHandlersListCount && interactionButtonsVisibility
|
||||
|
@ -222,7 +191,7 @@ Rectangle {
|
|||
onClicked: pluginSelector()
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: sendContactRequestButton
|
||||
objectName: "sendContactRequestButton"
|
||||
|
||||
|
@ -230,16 +199,39 @@ Rectangle {
|
|||
source: JamiResources.add_people_24dp_svg
|
||||
toolTipText: JamiStrings.addToConversations
|
||||
|
||||
onClicked: CurrentConversation.isBanned ? MessagesAdapter.unbanConversation(CurrentConversation.id) : MessagesAdapter.sendConversationRequest()
|
||||
onClicked: CurrentConversation.isBanned ?
|
||||
MessagesAdapter.unbanConversation(CurrentConversation.id) :
|
||||
MessagesAdapter.sendConversationRequest()
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: searchMessagesButton
|
||||
objectName: "searchMessagesButton"
|
||||
|
||||
checkable: true
|
||||
checked: extrasPanel.isOpen(ChatView.MessagesResearchPanel)
|
||||
visible: root.swarmDetailsVisibility
|
||||
source: JamiResources.ic_baseline_search_24dp_svg
|
||||
toolTipText: JamiStrings.search
|
||||
|
||||
onClicked: extrasPanel.switchToPanel(ChatView.MessagesResearchPanel)
|
||||
|
||||
Shortcut {
|
||||
sequence: "Ctrl+Shift+F"
|
||||
context: Qt.ApplicationShortcut
|
||||
enabled: parent.visible
|
||||
onActivated: extrasPanel.switchToPanel(ChatView.MessagesResearchPanel)
|
||||
}
|
||||
}
|
||||
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: detailsButton
|
||||
objectName: "detailsButton"
|
||||
|
||||
checkable: true
|
||||
checked: extrasPanel.isOpen(ChatView.SwarmDetailsPanel)
|
||||
visible: interactionButtonsVisibility && (swarmDetailsVisibility || LRCInstance.currentAccountType === Profile.Type.SIP) // TODO if SIP not a request
|
||||
visible: interactionButtonsVisibility &&
|
||||
(swarmDetailsVisibility || LRCInstance.currentAccountType === Profile.Type.SIP) // TODO if SIP not a request
|
||||
source: JamiResources.swarm_details_panel_svg
|
||||
toolTipText: JamiStrings.details
|
||||
|
||||
|
|
|
@ -127,9 +127,12 @@ Item {
|
|||
id: overlayUpperPartRect
|
||||
|
||||
anchors.top: parent.top
|
||||
|
||||
width: parent.width
|
||||
height: 50
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
// QWK: spacing
|
||||
anchors.leftMargin: qwkSystemButtonSpacing.left
|
||||
anchors.rightMargin: qwkSystemButtonSpacing.right
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -26,13 +26,33 @@ import net.jami.Constants 1.1
|
|||
import "../../commoncomponents"
|
||||
import "../../settingsview/components"
|
||||
|
||||
Rectangle {
|
||||
Page {
|
||||
id: root
|
||||
|
||||
color: JamiTheme.chatviewBgColor
|
||||
header: Item {
|
||||
height: 45
|
||||
Searchbar {
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
clearText();
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
onSearchBarTextChanged: function (text) {
|
||||
MessagesAdapter.searchbarPrompt = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 10
|
||||
|
||||
TabBar {
|
||||
id: researchTabBar
|
||||
|
@ -88,7 +108,7 @@ Rectangle {
|
|||
Rectangle {
|
||||
id: view
|
||||
|
||||
color: JamiTheme.chatviewBgColor
|
||||
color: JamiTheme.backgroundColor
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
|
|
|
@ -41,9 +41,11 @@ DualPaneView {
|
|||
splitViewStateKey: "Main"
|
||||
inhibits: ["ConversationView"]
|
||||
|
||||
leftPaneItem: viewCoordinator.getView("SidePanel")
|
||||
leftPaneItem: viewCoordinator.getView("SidePanel", true)
|
||||
rightPaneItem: Rectangle {
|
||||
id: root
|
||||
objectName: "NewSwarmLayout"
|
||||
|
||||
color: JamiTheme.chatviewBgColor
|
||||
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -26,31 +26,19 @@ Rectangle {
|
|||
|
||||
signal searchBarTextChanged(string text)
|
||||
signal returnPressedWhileSearching
|
||||
signal searchClicked
|
||||
|
||||
property bool reductionEnabled: false
|
||||
property alias textContent: textArea.text
|
||||
property alias placeHolderText: textArea.placeholderText
|
||||
|
||||
property real hoverButtonRadius: JamiTheme.chatViewHeaderButtonRadius
|
||||
|
||||
property var colorSearchBar: JamiTheme.secondaryBackgroundColor
|
||||
|
||||
property string currentConversationId: CurrentConversation.id
|
||||
|
||||
property bool isOpen: reductionEnabled ? extrasPanel.isOpen(ChatView.MessagesResearchPanel) : true
|
||||
onIsOpenChanged: {
|
||||
if (isOpen)
|
||||
textArea.forceActiveFocus();
|
||||
}
|
||||
|
||||
function clearText() {
|
||||
textArea.clear();
|
||||
textArea.forceActiveFocus();
|
||||
}
|
||||
|
||||
radius: JamiTheme.primaryRadius
|
||||
color: isOpen ? colorSearchBar : "transparent"
|
||||
color: JamiTheme.secondaryBackgroundColor
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
|
@ -64,32 +52,14 @@ Rectangle {
|
|||
lineEditObj: textArea
|
||||
}
|
||||
|
||||
PushButton {
|
||||
ResponsiveImage {
|
||||
id: startSearch
|
||||
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 10
|
||||
hoverEnabled: reductionEnabled
|
||||
enabled: reductionEnabled
|
||||
radius: hoverButtonRadius
|
||||
hoveredColor: JamiTheme.hoveredButtonColor
|
||||
source: JamiResources.ic_baseline_search_24dp_svg
|
||||
toolTipText: JamiStrings.search
|
||||
normalColor: JamiTheme.primaryBackgroundColor
|
||||
imageColor: {
|
||||
if (reductionEnabled) {
|
||||
if (hovered) {
|
||||
JamiTheme.chatviewButtonColor;
|
||||
} else {
|
||||
JamiTheme.chatViewFooterImgColor;
|
||||
}
|
||||
} else {
|
||||
JamiTheme.chatviewButtonColor;
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: root.searchClicked()
|
||||
color: JamiTheme.chatviewButtonColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -100,21 +70,7 @@ Rectangle {
|
|||
anchors.right: root.right
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
color: "transparent"
|
||||
|
||||
opacity: isOpen
|
||||
visible: opacity
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
}
|
||||
}
|
||||
|
||||
width: isOpen ? JamiTheme.searchbarSize : 0
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
}
|
||||
}
|
||||
width: JamiTheme.searchbarSize
|
||||
|
||||
TextField {
|
||||
id: textArea
|
||||
|
|
|
@ -173,7 +173,7 @@ SidePanelBase {
|
|||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
|
||||
header: AccountComboBox {
|
||||
header: AccountComboBox { QWKSetParentHitTestVisible {}
|
||||
id: accountComboBox
|
||||
Shortcut {
|
||||
sequence: "Ctrl+J"
|
||||
|
|
|
@ -35,7 +35,7 @@ ListSelectionView {
|
|||
color: JamiTheme.secondaryBackgroundColor
|
||||
|
||||
onPresented: LRCInstance.deselectConversation()
|
||||
leftPaneItem: viewCoordinator.getView("SidePanel")
|
||||
leftPaneItem: viewCoordinator.getView("SidePanel", true)
|
||||
|
||||
property variant uiCustomization: CurrentAccount.uiCustomization
|
||||
|
||||
|
@ -120,6 +120,8 @@ ListSelectionView {
|
|||
|
||||
rightPaneItem: JamiFlickable {
|
||||
id: root
|
||||
objectName: "WelcomeLayout"
|
||||
|
||||
anchors.fill: parent
|
||||
property int thresholdSize: 700
|
||||
property int thresholdHeight: 570
|
||||
|
|
|
@ -263,7 +263,13 @@ SidePanelBase {
|
|||
|
||||
background: null
|
||||
|
||||
header: AccountComboBox {
|
||||
header: AccountComboBox { QWKSetParentHitTestVisible {}
|
||||
id: accountComboBox
|
||||
Shortcut {
|
||||
sequence: "Ctrl+J"
|
||||
context: Qt.ApplicationShortcut
|
||||
onActivated: accountComboBox.togglePopup()
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
|
|
|
@ -56,7 +56,7 @@ ListSelectionView {
|
|||
splitViewStateKey: "Main"
|
||||
inhibits: ["ConversationView"]
|
||||
|
||||
leftPaneItem: viewCoordinator.getView("SettingsSidePanel")
|
||||
leftPaneItem: viewCoordinator.getView("SettingsSidePanel", true)
|
||||
|
||||
Component.onCompleted: {
|
||||
leftPaneItem.updateModel();
|
||||
|
@ -76,7 +76,7 @@ ListSelectionView {
|
|||
// Currently needed when changing the show link preview setting.
|
||||
CurrentConversation.reloadInteractions();
|
||||
if (UtilsAdapter.getAccountListSize() === 0) {
|
||||
viewCoordinator.requestAppWindowWizardView();
|
||||
viewCoordinator.present("WizardView");
|
||||
} else {
|
||||
AccountAdapter.changeAccount(0);
|
||||
}
|
||||
|
@ -85,8 +85,7 @@ ListSelectionView {
|
|||
property int selectedMenu: index
|
||||
|
||||
rightPaneItem: StackView {
|
||||
id: settingsView
|
||||
objectName: "settingsView"
|
||||
objectName: "SettingsLayout"
|
||||
|
||||
property var currentIndex: selectedMenu !== -1 ? selectedMenu : 0
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -237,6 +237,15 @@ SettingsPageBase {
|
|||
}
|
||||
}
|
||||
|
||||
ToggleSwitch {
|
||||
id: useNativeWindowFrameCheckBox
|
||||
Layout.fillWidth: true
|
||||
|
||||
checked: !UtilsAdapter.getAppValue(Settings.Key.UseFramelessWindow)
|
||||
labelText: JamiStrings.useNativeWindowFrame
|
||||
onSwitchToggled: UtilsAdapter.setAppValue(Settings.Key.UseFramelessWindow, !checked)
|
||||
}
|
||||
|
||||
MaterialButton {
|
||||
id: defaultSettings
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ RowLayout {
|
|||
signal backArrowClicked
|
||||
spacing: 10
|
||||
|
||||
BackButton {
|
||||
BackButton { QWKSetParentHitTestVisible {}
|
||||
id: backToSettingsMenuButton
|
||||
|
||||
Layout.preferredWidth: JamiTheme.preferredFieldHeight
|
||||
|
|
|
@ -25,11 +25,13 @@ import "../../commoncomponents"
|
|||
|
||||
JamiSplitView {
|
||||
id: root
|
||||
|
||||
required property Item flickableContent
|
||||
property real contentFlickableWidth: Math.min(JamiTheme.maximumWidthSettingsView, settingsPage.width - 2 * JamiTheme.preferredSettingsMarginSize)
|
||||
property alias title: settingsPage.title
|
||||
property color backgroundColor: JamiTheme.secondaryBackgroundColor
|
||||
property alias pageContainer: settingsPage
|
||||
|
||||
Page {
|
||||
id: settingsPage
|
||||
SplitView.maximumWidth: root.width
|
||||
|
|
|
@ -108,7 +108,7 @@ SettingsPageBase {
|
|||
}
|
||||
|
||||
ToggleSwitch {
|
||||
id: applicationOnStartUpCheckBox
|
||||
id: runOnStartUpCheckBox
|
||||
Layout.fillWidth: true
|
||||
|
||||
checked: UtilsAdapter.checkStartupLink()
|
||||
|
|
|
@ -94,6 +94,8 @@ UtilsAdapter::setAppValue(const Settings::Key key, const QVariant& value)
|
|||
Q_EMIT chatviewPositionChanged();
|
||||
else if (key == Settings::Key::AppTheme)
|
||||
Q_EMIT appThemeChanged();
|
||||
else if (key == Settings::Key::UseFramelessWindow)
|
||||
Q_EMIT useFramelessWindowChanged();
|
||||
#ifndef APPSTORE
|
||||
// Any donation campaign-related keys can trigger a donation campaign check
|
||||
else if (key == Settings::Key::IsDonationVisible
|
||||
|
|
|
@ -172,6 +172,7 @@ Q_SIGNALS:
|
|||
void showExperimentalCallSwarm();
|
||||
void changeLanguage();
|
||||
void donationCampaignSettingsChanged();
|
||||
void useFramelessWindowChanged();
|
||||
|
||||
private:
|
||||
QClipboard* clipboard_;
|
||||
|
|
|
@ -107,7 +107,7 @@ Item {
|
|||
property real windowSize: windowPreferedSize > JamiTheme.minimumMapWidth ? windowPreferedSize : JamiTheme.minimumMapWidth
|
||||
property real windowPreferedSize: root.maxWidth > root.maxHeight ? root.maxHeight / 3 : root.maxWidth / 3
|
||||
property real xPos: 0
|
||||
property real yPos: root.isUnpin ? 0 : JamiTheme.chatViewHeaderPreferredHeight
|
||||
property real yPos: root.isUnpin ? 0 : JamiTheme.qwkTitleBarHeight
|
||||
|
||||
states: [
|
||||
State {
|
||||
|
@ -125,7 +125,7 @@ Item {
|
|||
target: mapObject
|
||||
parent: parentPin
|
||||
x: xPos
|
||||
y: JamiTheme.chatViewHeaderPreferredHeight
|
||||
y: JamiTheme.qwkTitleBarHeight
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -55,10 +55,7 @@ BaseView {
|
|||
|
||||
function onCloseWizardView() {
|
||||
root.dismiss();
|
||||
viewCoordinator.preload("SidePanel");
|
||||
viewCoordinator.preload("SettingsSidePanel");
|
||||
viewCoordinator.present("WelcomePage");
|
||||
viewCoordinator.preload("ConversationView");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -402,7 +402,7 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: backButton
|
||||
|
||||
objectName: "createAccountPageBackButton"
|
||||
|
|
|
@ -382,7 +382,7 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: backButton
|
||||
|
||||
objectName: "welcomePageBackButton"
|
||||
|
|
|
@ -90,7 +90,7 @@ public Q_SLOTS:
|
|||
|
||||
// Create 2 Account
|
||||
QSignalSpy accountStatusChangedSpy(&lrcInstance_->accountModel(),
|
||||
&AccountModel::accountStatusChanged);
|
||||
&AccountModel::accountStatusChanged);
|
||||
|
||||
QSignalSpy accountAddedSpy(&lrcInstance_->accountModel(), &AccountModel::accountAdded);
|
||||
aliceId = lrcInstance_->accountModel().createNewAccount(profile::Type::JAMI, "Alice");
|
||||
|
|
|
@ -3,8 +3,22 @@
|
|||
<file>src/tst_WizardView.qml</file>
|
||||
<file>src/tst_NewSwarmPage.qml</file>
|
||||
<file>src/tst_MessageOptions.qml</file>
|
||||
<file>src/tst_OngoingCallPage.qml</file>
|
||||
<file>src/tst_MainView.qml</file>
|
||||
<file>src/tst_MaterialTextEdit.qml</file>
|
||||
<file>src/tst_ChatView.qml</file>
|
||||
<file>src/tst_ChatViewFooter.qml</file>
|
||||
<file>src/tst_CachedImage.qml</file>
|
||||
<file>src/tst_CallMessageDelegate.qml</file>
|
||||
<file>src/tst_DataTransferMessageDelegate.qml</file>
|
||||
<file>src/tst_FilesToSendContainer.qml</file>
|
||||
<file>src/tst_PresenceIndicator.qml</file>
|
||||
<file>src/tst_RecordBox.qml</file>
|
||||
<file>src/tst_SettingsSidePanel.qml</file>
|
||||
<file>src/tst_WelcomePage.qml</file>
|
||||
<file>src/resources/gif_test.gif</file>
|
||||
<file>src/resources/gz_test.gz</file>
|
||||
<file>src/resources/png_test.png</file>
|
||||
<file>src/TestWrapper.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
39
tests/qml/src/TestWrapper.qml
Normal file
39
tests/qml/src/TestWrapper.qml
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* This program 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import "../../../src/app/"
|
||||
|
||||
// The purpose of this component is to fake the ApplicationWindow and prevent
|
||||
// each UUT from having to manage its own top level app management objects
|
||||
// (currently ViewManager, ViewCoordinator, and ApplicationWindow).
|
||||
Item {
|
||||
// This will be our UUT.
|
||||
required default property var uut
|
||||
|
||||
// These are our fake app management objects. The caveat is that they
|
||||
// must be maintained in sync with the actual objects in the app for now.
|
||||
// The benefit, is that this should be the single place where we need to
|
||||
// sync them.
|
||||
property ViewManager viewManager: ViewManager {}
|
||||
property ViewCoordinator viewCoordinator: ViewCoordinator {}
|
||||
property ApplicationWindow appWindow: ApplicationWindow {
|
||||
property bool useFrameless: false
|
||||
}
|
||||
}
|
|
@ -39,9 +39,7 @@ ColumnLayout {
|
|||
id: uut
|
||||
|
||||
property ViewManager viewManager: ViewManager {}
|
||||
property ViewCoordinator viewCoordinator: ViewCoordinator {
|
||||
viewManager: uut.viewManager
|
||||
}
|
||||
property ViewCoordinator viewCoordinator: ViewCoordinator {}
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: root.width
|
||||
|
|
|
@ -32,11 +32,8 @@ OngoingCallPage {
|
|||
height: 600
|
||||
|
||||
property QtObject appWindow
|
||||
property ViewManager viewManager: ViewManager {
|
||||
}
|
||||
property ViewCoordinator viewCoordinator: ViewCoordinator {
|
||||
viewManager: uut.viewManager
|
||||
}
|
||||
property ViewManager viewManager: ViewManager {}
|
||||
property ViewCoordinator viewCoordinator: ViewCoordinator {}
|
||||
|
||||
TestCase {
|
||||
name: "Check basic visibility of action bar during a call"
|
||||
|
|
|
@ -16,40 +16,26 @@
|
|||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
import QtTest
|
||||
|
||||
import "../../../src/app/"
|
||||
import "../../../src/app/mainview/components"
|
||||
|
||||
WelcomePage {
|
||||
id: uut
|
||||
TestWrapper {
|
||||
uut: WelcomePage {
|
||||
TestCase {
|
||||
name: "Open 'About Jami' popup"
|
||||
|
||||
width: 800
|
||||
height: 600
|
||||
function test_openAboutPopup() {
|
||||
var aboutJamiButton = findChild(uut, "aboutJami")
|
||||
aboutJamiButton.clicked()
|
||||
|
||||
// The appWindow, viewManager and viewCoordinator properties
|
||||
// are required in order for the "aboutJami" button to work.
|
||||
property Item appWindow: uut
|
||||
property ViewManager viewManager: ViewManager {
|
||||
}
|
||||
property ViewCoordinator viewCoordinator: ViewCoordinator {
|
||||
viewManager: uut.viewManager
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "Open 'About Jami' popup"
|
||||
|
||||
function test_openAboutPopup() {
|
||||
compare(viewManager.viewCount(), 0)
|
||||
|
||||
var aboutJamiButton = findChild(uut, "aboutJami")
|
||||
aboutJamiButton.clicked()
|
||||
|
||||
compare(viewManager.viewCount(), 1)
|
||||
|
||||
var aboutJamiPopup = viewManager.getView("AboutPopUp")
|
||||
verify(aboutJamiPopup !== null)
|
||||
compare(aboutJamiPopup.visible, true)
|
||||
var aboutJamiPopup = viewManager.getView("AboutPopUp")
|
||||
verify(aboutJamiPopup !== null, "About Jami popup should be created")
|
||||
compare(aboutJamiPopup.visible, true, "About Jami popup should be visible")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue