mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-10-30 07:53:33 +08:00
Compare commits
66 Commits
nightly/20
...
stable/202
| Author | SHA1 | Date | |
|---|---|---|---|
| f513358236 | |||
| 3c00829afb | |||
| 84a59889e3 | |||
| 6c35561817 | |||
| 6ffdda7b81 | |||
| a51078c900 | |||
| 76a710e2ab | |||
| bc324aa8bb | |||
| 5c772960bc | |||
| da2acefced | |||
| 524c9b0ed4 | |||
| 8677349c4a | |||
| 905b2e858e | |||
| 6fc2e75a33 | |||
| c738caa3a4 | |||
| cb13d4f771 | |||
| 436e11a6a4 | |||
| 869aef8929 | |||
| e0f939318e | |||
| 136dea011f | |||
| af09269d81 | |||
| 5a5ef4711d | |||
| 1dd745d446 | |||
| 3dd2d26d86 | |||
| eb6b6a2b93 | |||
| b15d692a0e | |||
| e76bcbd555 | |||
| b5a979e6b1 | |||
| 898444dd3c | |||
| 99f246016d | |||
| e24f3d91e8 | |||
| 3a7850b398 | |||
| 1e1750024b | |||
| 2ba53a2e40 | |||
| ceec1f95b9 | |||
| 1ac3db4f33 | |||
| 93d3d18c7b | |||
| d4b7891f48 | |||
| d7c294edd0 | |||
| 945cfe176d | |||
| fa3a153896 | |||
| e9106b2bcc | |||
| a967518d45 | |||
| 7ebed53e97 | |||
| ca05963c40 | |||
| df98c6c3fd | |||
| 05501e33e9 | |||
| 91780ae400 | |||
| 4900ca9f1b | |||
| 0f965aae28 | |||
| f8c29fc4a1 | |||
| 2e1889caf1 | |||
| 8070d1bfc2 | |||
| 4ee1a309a1 | |||
| 0333016c44 | |||
| 32b7525ee3 | |||
| 32b941ab96 | |||
| e4932abd39 | |||
| 718d1d266d | |||
| 8d55f352b4 | |||
| 2a72da564e | |||
| 88d0539085 | |||
| 4b1c299a1d | |||
| 479da3ca54 | |||
| 02140a11c4 | |||
| b5dbe23c17 |
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -31,3 +31,7 @@
|
||||
path = 3rdparty/zxing-cpp
|
||||
url = https://github.com/nu-book/zxing-cpp.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/hunspell"]
|
||||
path = 3rdparty/hunspell
|
||||
url = https://gitlab.savoirfairelinux.com/jami/hunspell.git
|
||||
ignore = dirty
|
||||
|
||||
1
3rdparty/hunspell
vendored
Submodule
1
3rdparty/hunspell
vendored
Submodule
Submodule 3rdparty/hunspell added at 749cd84a0b
105
CMakeLists.txt
105
CMakeLists.txt
@ -84,10 +84,8 @@ if(WIN32)
|
||||
if(BETA)
|
||||
message(STATUS "Beta config enabled")
|
||||
add_definitions(-DBETA)
|
||||
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/Beta)
|
||||
else()
|
||||
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/Release)
|
||||
endif()
|
||||
set(JAMI_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/x64/${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@ -246,7 +244,7 @@ set(CMAKE_MODULE_PATH
|
||||
${CMAKE_MODULE_PATH} "${EXTRAS_DIR}/build/cmake/modules")
|
||||
find_package(LibJami REQUIRED)
|
||||
if(LIBJAMI_FOUND)
|
||||
include_directories(${LIBJAMI_INCLUDE_DIRS})
|
||||
include_directories(${LIBJAMI_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
include(FindPython3)
|
||||
@ -255,7 +253,7 @@ set(PYTHON_EXEC ${Python3_EXECUTABLE})
|
||||
|
||||
# Versioning and build ID generation
|
||||
set(VERSION_FILE ${CMAKE_CURRENT_BINARY_DIR}/version_info.cpp)
|
||||
# Touch the file to make sure it exists at configure time as
|
||||
# Touch the file to ensure it exists at configure time as
|
||||
# we add it to the target_sources below.
|
||||
file(TOUCH ${VERSION_FILE})
|
||||
add_custom_target(
|
||||
@ -266,6 +264,7 @@ add_custom_target(
|
||||
-DAPP_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
|
||||
-DCORE_SOURCE_DIR=${DAEMON_DIR}
|
||||
-DCPP_INT_FILE=${VERSION_INFO_DIR}/version_info.cpp.in
|
||||
-DBUILD_VERSION=${BUILD_VERSION}
|
||||
-P ${CMAKE_SCRIPTS_DIR}/generate_version_info.cmake
|
||||
)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${VERSION_INFO_DIR})
|
||||
@ -347,6 +346,8 @@ set(COMMON_SOURCES
|
||||
${APP_SRC_DIR}/conversationlistmodel.cpp
|
||||
${APP_SRC_DIR}/searchresultslistmodel.cpp
|
||||
${APP_SRC_DIR}/calloverlaymodel.cpp
|
||||
${APP_SRC_DIR}/spellcheckdictionarylistmodel.cpp
|
||||
${APP_SRC_DIR}/spellcheckadapter.cpp
|
||||
${APP_SRC_DIR}/filestosendlistmodel.cpp
|
||||
${APP_SRC_DIR}/wizardviewstepmodel.cpp
|
||||
${APP_SRC_DIR}/avatarregistry.cpp
|
||||
@ -361,13 +362,13 @@ set(COMMON_SOURCES
|
||||
${APP_SRC_DIR}/currentcall.cpp
|
||||
${APP_SRC_DIR}/messageparser.cpp
|
||||
${APP_SRC_DIR}/previewengine.cpp
|
||||
${APP_SRC_DIR}/imagedownloader.cpp
|
||||
${APP_SRC_DIR}/filedownloader.cpp
|
||||
${APP_SRC_DIR}/pluginversionmanager.cpp
|
||||
${APP_SRC_DIR}/connectioninfolistmodel.cpp
|
||||
${APP_SRC_DIR}/pluginversionmanager.cpp
|
||||
${APP_SRC_DIR}/linkdevicemodel.cpp
|
||||
${APP_SRC_DIR}/qrcodescannermodel.cpp
|
||||
)
|
||||
${APP_SRC_DIR}/spellchecker.cpp)
|
||||
|
||||
set(COMMON_HEADERS
|
||||
${APP_SRC_DIR}/global.h
|
||||
@ -377,7 +378,6 @@ set(COMMON_HEADERS
|
||||
${APP_SRC_DIR}/appversionmanager.h
|
||||
${APP_SRC_DIR}/utils.h
|
||||
${APP_SRC_DIR}/bannedlistmodel.h
|
||||
${APP_SRC_DIR}/version.h
|
||||
${APP_SRC_DIR}/accountlistmodel.h
|
||||
${APP_SRC_DIR}/instancemanager.h
|
||||
${APP_SRC_DIR}/connectivitymonitor.h
|
||||
@ -419,6 +419,8 @@ set(COMMON_HEADERS
|
||||
${APP_SRC_DIR}/conversationlistmodel.h
|
||||
${APP_SRC_DIR}/searchresultslistmodel.h
|
||||
${APP_SRC_DIR}/calloverlaymodel.h
|
||||
${APP_SRC_DIR}/spellcheckdictionarylistmodel.h
|
||||
${APP_SRC_DIR}/spellcheckadapter.h
|
||||
${APP_SRC_DIR}/filestosendlistmodel.h
|
||||
${APP_SRC_DIR}/wizardviewstepmodel.h
|
||||
${APP_SRC_DIR}/avatarregistry.h
|
||||
@ -433,7 +435,7 @@ set(COMMON_HEADERS
|
||||
${APP_SRC_DIR}/currentcall.h
|
||||
${APP_SRC_DIR}/messageparser.h
|
||||
${APP_SRC_DIR}/htmlparser.h
|
||||
${APP_SRC_DIR}/imagedownloader.h
|
||||
${APP_SRC_DIR}/filedownloader.h
|
||||
${APP_SRC_DIR}/pluginversionmanager.h
|
||||
${APP_SRC_DIR}/connectioninfolistmodel.h
|
||||
${APP_SRC_DIR}/pttlistener.h
|
||||
@ -441,7 +443,7 @@ set(COMMON_HEADERS
|
||||
${APP_SRC_DIR}/crashreporter.h
|
||||
${APP_SRC_DIR}/linkdevicemodel.h
|
||||
${APP_SRC_DIR}/qrcodescannermodel.h
|
||||
)
|
||||
${APP_SRC_DIR}/spellchecker.h)
|
||||
|
||||
# For libavutil/avframe.
|
||||
set(LIBJAMI_CONTRIB_DIR "${DAEMON_DIR}/contrib")
|
||||
@ -469,6 +471,25 @@ if(ENABLE_CRASHREPORTS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
# hunspell
|
||||
pkg_search_module(hunspell IMPORTED_TARGET hunspell)
|
||||
if(hunspell_FOUND)
|
||||
message(STATUS "hunspell found")
|
||||
set(HUNSPELL_LIBRARIES PkgConfig::hunspell)
|
||||
else()
|
||||
message(STATUS "hunspell not found - building hunspell")
|
||||
|
||||
set(HUNSPELL_DIR ${PROJECT_SOURCE_DIR}/3rdparty/hunspell)
|
||||
|
||||
# Build using the submodule and its CMakeLists.txt
|
||||
add_subdirectory(${HUNSPELL_DIR} hunspell_build EXCLUDE_FROM_ALL)
|
||||
|
||||
set(HUNSPELL_INCLUDE_DIR ${HUNSPELL_DIR}/src)
|
||||
set(HUNSPELL_LIBRARIES hunspell::hunspell)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(WINDOWS_SYS_LIBS
|
||||
windowsapp.lib
|
||||
@ -531,8 +552,6 @@ elseif (NOT APPLE)
|
||||
${APP_SRC_DIR}/screencastportal.h)
|
||||
list(APPEND QT_MODULES DBus)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_check_modules(GLIB REQUIRED glib-2.0)
|
||||
if(GLIB_FOUND)
|
||||
add_definitions(${GLIB_CFLAGS_OTHER})
|
||||
@ -615,6 +634,13 @@ else() # APPLE
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Adding HUNSPELL_INCLUDE_DIR" ${HUNSPELL_INCLUDE_DIR})
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${HUNSPELL_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/hunspell/src)
|
||||
|
||||
message(STATUS "Adding HUNSPELL_LIBRARIES" ${HUNSPELL_INCLUDE_DIR})
|
||||
list(APPEND CLIENT_LIBS ${HUNSPELL_LIBRARIES})
|
||||
|
||||
# Qt find package
|
||||
if(QT6_VER AND QT6_PATH)
|
||||
message(STATUS "Using custom Qt version")
|
||||
@ -668,20 +694,34 @@ add_subdirectory(3rdparty/SortFilterProxyModel)
|
||||
set(SFPM_OBJECTS $<TARGET_OBJECTS:SortFilterProxyModel>)
|
||||
|
||||
# md4c
|
||||
set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Don't build md2html executable" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Don't build shared md4c library" FORCE)
|
||||
add_subdirectory(3rdparty/md4c EXCLUDE_FROM_ALL)
|
||||
list(APPEND CLIENT_LINK_DIRS ${MD4C_BINARY_DIR}/src)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${MD4C_SOURCE_DIR}/src)
|
||||
list(APPEND CLIENT_LIBS md4c-html)
|
||||
find_package(md4c)
|
||||
if(md4c_FOUND)
|
||||
message(STATUS "Using system-provided md4c-html")
|
||||
list(APPEND CLIENT_LIBS md4c::md4c-html)
|
||||
else()
|
||||
message("Using bundled md4c-html library")
|
||||
set(BUILD_MD2HTML_EXECUTABLE OFF CACHE BOOL "Don't build md2html executable" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Don't build shared md4c library" FORCE)
|
||||
add_subdirectory(3rdparty/md4c EXCLUDE_FROM_ALL)
|
||||
list(APPEND CLIENT_LINK_DIRS ${MD4C_BINARY_DIR}/src)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${MD4C_SOURCE_DIR}/src)
|
||||
list(APPEND CLIENT_LIBS md4c-html)
|
||||
endif()
|
||||
|
||||
# tidy-html5
|
||||
set(BUILD_SHARED_LIB OFF CACHE BOOL "Don't build shared tidy library" FORCE)
|
||||
set(SUPPORT_CONSOLE_APP OFF CACHE BOOL "Don't build tidy console app" FORCE)
|
||||
add_subdirectory(3rdparty/tidy-html5 EXCLUDE_FROM_ALL)
|
||||
list(APPEND CLIENT_LINK_DIRS ${tidy_BINARY_DIR}/Release)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${tidy_SOURCE_DIR}/include)
|
||||
list(APPEND CLIENT_LIBS tidy-static)
|
||||
pkg_check_modules(tidy IMPORTED_TARGET tidy)
|
||||
if(tidy_FOUND)
|
||||
message(STATUS "Using system-provided tidy")
|
||||
list(APPEND CLIENT_LIBS PkgConfig::tidy)
|
||||
else()
|
||||
message("Using bundled tidy library")
|
||||
set(BUILD_SHARED_LIB OFF CACHE BOOL "Don't build shared tidy library" FORCE)
|
||||
set(SUPPORT_CONSOLE_APP OFF CACHE BOOL "Don't build tidy console app" FORCE)
|
||||
add_subdirectory(3rdparty/tidy-html5 EXCLUDE_FROM_ALL)
|
||||
list(APPEND CLIENT_LINK_DIRS ${tidy_BINARY_DIR}/Release)
|
||||
list(APPEND CLIENT_INCLUDE_DIRS ${tidy_SOURCE_DIR}/include)
|
||||
list(APPEND CLIENT_LIBS tidy-static)
|
||||
endif()
|
||||
|
||||
# ZXing-cpp configuration
|
||||
set(BUILD_EXAMPLES OFF CACHE BOOL "")
|
||||
@ -701,9 +741,12 @@ qt_add_executable(
|
||||
${COMMON_SOURCES}
|
||||
${QML_RESOURCES}
|
||||
${QML_RESOURCES_QML}
|
||||
${SFPM_OBJECTS})
|
||||
${SFPM_OBJECTS}
|
||||
src/app/spellcheckadapter.h src/app/spellcheckadapter.cpp)
|
||||
|
||||
# Make sure we can find the generated version file
|
||||
#add_dependencies(${PROJECT_NAME} hunspell)
|
||||
|
||||
# Ensure the generated version file can be found.
|
||||
add_dependencies(${PROJECT_NAME} generate_version_info)
|
||||
|
||||
foreach(MODULE ${QT_MODULES})
|
||||
@ -797,14 +840,19 @@ elseif (NOT APPLE)
|
||||
PRIVATE
|
||||
JAMI_INSTALL_PREFIX="${JAMI_DATA_PREFIX}")
|
||||
|
||||
target_compile_definitions(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
HUNSPELL_INSTALL_DIR="${HUNSPELL_DICT_DIR}")
|
||||
|
||||
# Logos
|
||||
install(
|
||||
FILES resources/images/jami.svg
|
||||
FILES resources/images/net.jami.Jami.svg
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps)
|
||||
|
||||
install(
|
||||
FILES resources/images/jami-48px.png
|
||||
FILES resources/images/net.jami.Jami-48px.png
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps
|
||||
RENAME jami.png)
|
||||
|
||||
@ -982,5 +1030,6 @@ qt_finalize_executable(${PROJECT_NAME})
|
||||
# tests
|
||||
if(BUILD_TESTING)
|
||||
message("Add Jami tests")
|
||||
enable_testing()
|
||||
add_subdirectory(${TESTS_DIR})
|
||||
endif()
|
||||
|
||||
@ -189,7 +189,7 @@ Only 64-bit MSVC build can be compiled.
|
||||
|
||||
- Download [Qt (Open Source)](https://www.qt.io/download-open-source?hsCtaTracking=9f6a2170-a938-42df-a8e2-a9f0b1d6cdce%7C6cb0de4f-9bb5-4778-ab02-bfb62735f3e5)
|
||||
|
||||
- Using the online installer, install the following Qt 6.6.1 components:
|
||||
- Using the online installer, install the following Qt 6.6.2 components:
|
||||
|
||||
- Git 2.10.2
|
||||
- MSVC 2019 64-bit
|
||||
@ -244,6 +244,7 @@ Only 64-bit MSVC build can be compiled.
|
||||
```bash
|
||||
python build.py --install --qt <path-to-qt-bin-folder> (e.g. C:/Qt/6.6.2/msvc2019_64)
|
||||
```
|
||||
> **CMake** Note: The build script does not specify what CMake generator should be used. This means CMake will search the system for the appropriate generator, which might not always select the right one if, for instance, Ninja is installed. To resolve that, the CMAKE_GENERATOR environment variable can be used, set to "Visual Studio 16 2019" or "Visual Studio 19 2022" depending on the installed Visual Studio version.
|
||||
|
||||
> **SDK** Note:
|
||||
> Jami can be build with more recent Windows SDK than the one specified in the table above. However, if your have another version than SDK 10.0.18362.0 installed, you need to identify it according to the example below. And you still need to have the required version in addition to the one you chose.
|
||||
|
||||
@ -4,10 +4,5 @@
|
||||
<?define ExeName="Jami" ?>
|
||||
<?define AppName="Jami" ?>
|
||||
<?define Manufacturer="Savoir-Faire Linux"?>
|
||||
|
||||
<?if $(var.Configuration) = Release ?>
|
||||
<?define ReleaseDir="..\x64\Release"?>
|
||||
<?else?>
|
||||
<?define ReleaseDir="..\x64\Beta"?>
|
||||
<?endif ?>
|
||||
</Include>
|
||||
|
||||
@ -16,9 +16,9 @@
|
||||
<InstallerPlatform>x64</InstallerPlatform>
|
||||
<DefineSolutionProperties>false</DefineSolutionProperties>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||
<PropertyGroup>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<IntermediateOutputPath>obj\Release\</IntermediateOutputPath>
|
||||
<DefineConstants>AppHarvestPath=..\x64\Release;CrtHarvestPath=$(VC_CRT_Dir)</DefineConstants>
|
||||
<SuppressPdbOutput>True</SuppressPdbOutput>
|
||||
<CompilerAdditionalOptions>
|
||||
@ -26,16 +26,6 @@
|
||||
<WixVariables>
|
||||
</WixVariables>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Beta|x64' ">
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||
<DefineConstants>AppHarvestPath=..\x64\Beta;CrtHarvestPath=$(VC_CRT_Dir)</DefineConstants>
|
||||
<SuppressPdbOutput>True</SuppressPdbOutput>
|
||||
<CompilerAdditionalOptions>
|
||||
</CompilerAdditionalOptions>
|
||||
<WixVariables>
|
||||
</WixVariables>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Product.wxs" />
|
||||
<Compile Include="AppComponents.wxs" />
|
||||
@ -70,7 +60,7 @@
|
||||
<Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
|
||||
</Target>
|
||||
<Target Name="BeforeBuild">
|
||||
<HeatDirectory Directory="..\x64\$(Configuration)"
|
||||
<HeatDirectory Directory="..\x64\Release"
|
||||
PreprocessorVariable="var.AppHarvestPath"
|
||||
OutputFile="AppComponents.wxs"
|
||||
ComponentGroupName="AppHeatGenerated"
|
||||
|
||||
8
build.py
8
build.py
@ -112,7 +112,7 @@ ZYPPER_CLIENT_DEPENDENCIES = [
|
||||
'qt6-svg-devel', 'qt6-multimedia-devel', 'qt6-multimedia-imports',
|
||||
'qt6-declarative-devel', 'qt6-qmlcompiler-private-devel',
|
||||
'qt6-quickcontrols2-devel', 'qt6-shadertools-devel',
|
||||
'qrencode-devel', 'NetworkManager-devel'
|
||||
'qrencode-devel', 'NetworkManager-devel', 'hunspell-devel', 'libhunspell-devel'
|
||||
]
|
||||
|
||||
ZYPPER_QT_WEBENGINE = [
|
||||
@ -139,7 +139,7 @@ DNF_CLIENT_DEPENDENCIES = [
|
||||
'libnotify-devel',
|
||||
'qt6-qtbase-devel',
|
||||
'qt6-qtsvg-devel', 'qt6-qtmultimedia-devel', 'qt6-qtdeclarative-devel',
|
||||
'qrencode-devel', 'NetworkManager-libnm-devel'
|
||||
'qrencode-devel', 'NetworkManager-libnm-devel', 'hunspell-devel', 'libhunspell-devel'
|
||||
]
|
||||
|
||||
DNF_QT_WEBENGINE = ['qt6-qtwebengine-devel']
|
||||
@ -171,7 +171,7 @@ APT_CLIENT_DEPENDENCIES = [
|
||||
'qml6-module-qtquick-dialogs', 'qml6-module-qtquick-layouts',
|
||||
'qml6-module-qtquick-shapes', 'qml6-module-qtquick-window',
|
||||
'qml6-module-qtquick-templates', 'qml6-module-qt-labs-platform',
|
||||
'libqrencode-dev', 'libnm-dev'
|
||||
'libqrencode-dev', 'libnm-dev', 'hunspell', 'libhunspell-dev'
|
||||
]
|
||||
|
||||
APT_QT_WEBENGINE = [
|
||||
@ -194,7 +194,7 @@ PACMAN_CLIENT_DEPENDENCIES = [
|
||||
'qt6-declarative', 'qt6-5compat', 'qt6-multimedia',
|
||||
'qt6-networkauth', 'qt6-shadertools',
|
||||
'qt6-svg', 'qt6-tools',
|
||||
'qrencode', 'libnm'
|
||||
'qrencode', 'libnm', 'hunspell'
|
||||
]
|
||||
|
||||
PACMAN_QT_WEBENGINE = ['qt6-webengine']
|
||||
|
||||
2
daemon
2
daemon
Submodule daemon updated: 95832cfc9d...1969975c2c
@ -24,6 +24,7 @@ set(CMAKE_CURRENT_BINARY_DIR ${APP_BINARY_DIR})
|
||||
# Generate the version string for the application and core
|
||||
configure_version_string(${APP_SOURCE_DIR} APP_VERSION_STRING)
|
||||
configure_version_string(${CORE_SOURCE_DIR} CORE_VERSION_STRING)
|
||||
set(BUILD_VERSION_STRING ${BUILD_VERSION})
|
||||
|
||||
# Get output file names with the .in extension removed
|
||||
get_filename_component(VERSION_CPP_FILENAME ${CPP_INT_FILE} NAME_WE)
|
||||
|
||||
@ -16,30 +16,19 @@
|
||||
|
||||
# Once done, this find module will set:
|
||||
#
|
||||
# LIBJAMI_INCLUDE_DIRS - libjami include directories
|
||||
# LIBJAMI_INCLUDE_DIR - libjami include directory
|
||||
# LIBJAMI_FOUND - whether it was able to find the include directories
|
||||
# LIBJAMI_LIB - path to libjami or libring library
|
||||
|
||||
set(LIBJAMI_FOUND true)
|
||||
|
||||
if(WITH_DAEMON_SUBMODULE)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${DAEMON_DIR}/src/jami)
|
||||
set(LIBJAMI_INCLUDE_DIR ${DAEMON_DIR}/src/jami)
|
||||
else()
|
||||
if(EXISTS ${LIBJAMI_INCLUDE_DIR}/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${LIBJAMI_INCLUDE_DIR})
|
||||
elseif(EXISTS ${LIBJAMI_BUILD_DIR}/jami/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${LIBJAMI_BUILD_DIR}/jami)
|
||||
elseif(EXISTS ${RING_INCLUDE_DIR}/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${RING_INCLUDE_DIR})
|
||||
elseif(EXISTS ${RING_BUILD_DIR}/jami/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${RING_BUILD_DIR}/jami)
|
||||
elseif(EXISTS ${CMAKE_INSTALL_PREFIX}/include/jami/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include/jami)
|
||||
elseif(EXISTS ${CMAKE_INSTALL_PREFIX}/daemon/include/jami/jami.h)
|
||||
set(LIBJAMI_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/daemon/include/jami)
|
||||
else()
|
||||
find_path(LIBJAMI_INCLUDE_DIR jami.h PATH_SUFFIXES jami)
|
||||
if(NOT LIBJAMI_INCLUDE_DIR)
|
||||
message(STATUS "Jami daemon headers not found!
|
||||
Set -DLIBJAMI_BUILD_DIR or -DCMAKE_INSTALL_PREFIX")
|
||||
To build using the daemon git submodule, set -DWITH_DAEMON_SUBMODULE")
|
||||
set(LIBJAMI_FOUND false)
|
||||
endif()
|
||||
endif()
|
||||
@ -121,5 +110,5 @@ endif()
|
||||
# Restore the original value of CMAKE_FIND_LIBRARY_SUFFIXES.
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_orig})
|
||||
|
||||
message(STATUS "Jami daemon headers are in " ${LIBJAMI_INCLUDE_DIRS})
|
||||
message(STATUS "Jami daemon headers are in " ${LIBJAMI_INCLUDE_DIR})
|
||||
message(STATUS "Jami daemon library is at " ${LIBJAMI_LIB})
|
||||
|
||||
@ -4,11 +4,24 @@ ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV QT_QUICK_BACKEND software
|
||||
ENV QT_QPA_PLATFORM offscreen
|
||||
|
||||
RUN apt-get clean
|
||||
RUN apt-get update && \
|
||||
apt-get install -y devscripts equivs
|
||||
apt-get install -y --no-install-recommends ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Use only the custom Savoir-faire Linux Ubuntu mirror
|
||||
RUN rm -f /etc/apt/sources.list /etc/apt/sources.list.d/* && \
|
||||
echo "deb http://gpl.savoirfairelinux.net/pub/mirrors/ubuntu jammy main restricted universe multiverse" > /etc/apt/sources.list && \
|
||||
echo "deb http://gpl.savoirfairelinux.net/pub/mirrors/ubuntu jammy-updates main restricted universe multiverse" >> /etc/apt/sources.list && \
|
||||
echo "deb http://gpl.savoirfairelinux.net/pub/mirrors/ubuntu jammy-security main restricted universe multiverse" >> /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
devscripts \
|
||||
equivs \
|
||||
gnupg \
|
||||
dirmngr \
|
||||
curl
|
||||
|
||||
RUN apt install gnupg dirmngr ca-certificates curl --no-install-recommends
|
||||
RUN curl -s https://dl.jami.net/public-key.gpg | tee /usr/share/keyrings/jami-archive-keyring.gpg > /dev/null
|
||||
RUN sh -c "echo 'deb [signed-by=/usr/share/keyrings/jami-archive-keyring.gpg] https://dl.jami.net/internal/ubuntu_22.04/ jami main' > /etc/apt/sources.list.d/jami.list"
|
||||
RUN apt-get update && apt-get install libqt-jami -y
|
||||
@ -69,7 +82,8 @@ RUN apt-get install -y pandoc \
|
||||
libcppunit-dev \
|
||||
googletest \
|
||||
libgtest-dev \
|
||||
wget
|
||||
wget && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install a recent version of CMake
|
||||
ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
<id>net.jami.daemon</id>
|
||||
</requires>
|
||||
<launchable type="desktop-id">net.jami.Jami.desktop</launchable>
|
||||
<icon type="stock">jami</icon>
|
||||
<icon type="stock">net.jami.Jami</icon>
|
||||
<url type="homepage">https://jami.net/</url>
|
||||
<url type="bugtracker">https://git.jami.net/savoirfairelinux/jami-client-qt/issues</url>
|
||||
<url type="faq">https://docs.jami.net/user/faq.html</url>
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
[Desktop Entry]
|
||||
Name=Jami
|
||||
GenericName=Jami
|
||||
Comment=Privacy-oriented voice, video, chat, and conference platform
|
||||
Comment[hu]=Adatvédelem-orientált hang-, video-, csevegés- és konferenciaplatform
|
||||
Comment[ru]=Jami — приложение для защищённой связи с распределённой архитектурой
|
||||
Comment=Share, freely and privately
|
||||
Comment[hu]=Megosztás, szabadon és bizalmasan
|
||||
Exec=jami %u
|
||||
Icon=jami
|
||||
Icon=net.jami.Jami
|
||||
StartupNotify=true
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;Telephony;
|
||||
Categories=Chat;FileTransfer;InstantMessaging;Network;P2P;Telephony;VideoConference;
|
||||
Keywords=Qt;chat;talk;im;message;voip;
|
||||
MimeType=x-scheme-handler/jami;
|
||||
MimeType=x-scheme-handler/jami;
|
||||
|
||||
@ -2,87 +2,101 @@
|
||||
<!-- Copyright (C) 2015-2025 Savoir-faire Linux Inc. -->
|
||||
<component type="desktop-application">
|
||||
<id>net.jami.Jami</id>
|
||||
<metadata_license>CC-BY-SA-3.0</metadata_license>
|
||||
<project_license>GPL-3.0+</project_license>
|
||||
<name>Jami</name>
|
||||
|
||||
<summary>Privacy-oriented voice, video, chat, and conference platform</summary>
|
||||
<summary xml:lang="hu">Adatvédelem-orientált hang-, video-, csevegés- és konferenciaplatform</summary>
|
||||
<icon type="stock">jami</icon>
|
||||
<summary>Share, freely and privately</summary>
|
||||
<summary xml:lang="hu">Megosztás, szabadon és bizalmasan</summary>
|
||||
|
||||
<icon type="stock">net.jami.Jami</icon>
|
||||
|
||||
<metadata_license>CC-BY-SA-3.0</metadata_license>
|
||||
<project_license>GPL-3.0+</project_license>
|
||||
|
||||
<developer id="net.jami">
|
||||
<name>Savoir-faire Linux Inc.</name>
|
||||
</developer>
|
||||
|
||||
<releases>
|
||||
<release version="20250613.0" date="2025-06-13">
|
||||
<url type="details">https://git.jami.net/savoirfairelinux/jami-client-qt/-/wikis/Changelog#nightlystable-20250613</url>
|
||||
</release>
|
||||
</releases>
|
||||
|
||||
<branding>
|
||||
<color type="primary" scheme_preference="light">#03b9e9</color>
|
||||
<color type="primary" scheme_preference="dark">#005699</color>
|
||||
</branding>
|
||||
|
||||
<!-- https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-description -->
|
||||
<description>
|
||||
<p>Jami, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users.</p>
|
||||
<p xml:lang="hu">A Jami, egy GNU-csomag, egy univerzális és elosztott társ-társ kommunikációra szolgáló szoftver, amely tiszteletben tartja a felhasználók szabadságát és magánéletét.</p>
|
||||
<p>Jami is the simplest and easiest way to connect with people (and devices) with instant messaging, audio and video calls over the Internet and LAN/WAN intranets.</p>
|
||||
<p xml:lang="hu">A Jami a legegyszerűbb és legegyszerűbb módja annak, hogy azonnali üzenetküldéssel, hang- és videohívásokkal kapcsolódjon az emberekhez (és eszközökhöz) az interneten és a LAN/WAN intraneteken keresztül.</p>
|
||||
|
||||
<p><em>Jami</em>, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users.</p>
|
||||
<p xml:lang="hu">A <em>Jami</em>, egy GNU-csomag, egy univerzális és elosztott társ-társ kommunikációt szolgáló szoftver, amely tiszteletben tartja a felhasználók szabadságát és magánéletét.</p>
|
||||
|
||||
<p>Jami is the simplest and easiest way to connect with people (and devices) with instant messaging, audio, and video calls over the Internet and LAN/WAN intranets.</p>
|
||||
<p xml:lang="hu">A Jami a legegyszerűbb és legegyszerűbb módja annak, hogy azonnali üzenetküldéssel, hang- és videohívásokkal kapcsolódjon az emberekhez (és eszközökhöz) az interneten és a LAN/WAN intraneten keresztül.</p>
|
||||
|
||||
<p>Jami is a free/libre, end-to-end encrypted, and private communication platform.</p>
|
||||
<p xml:lang="hu">A Jami egy ingyenes, teljes körűen titkosított és privát kommunikációs platform.</p>
|
||||
<p>Jami – which used to be known as Ring – is also an open-source alternative (to Facebook Messenger, Signal, Skype, Teams, Telegram, TikTok, Viber, WhatsApp, Zoom) that prioritizes the privacy of its users.</p>
|
||||
<p xml:lang="hu">A Jami – amelyet korábban Ring néven ismertek – egy nyílt forráskódú alternatíva is (a Facebook Messenger, a Signal, a Skype, a Teams, a Telegram, a TikTok, a Viber, a WhatsApp, a Zoom számára), amely előtérbe helyezi a felhasználók magánéletét.</p>
|
||||
<p>Jami has a professional-looking design and is available for a wide range of platforms. Unlike the alternatives, calls using Jami are directly between users as it does not use servers to handle calls.</p>
|
||||
<p xml:lang="hu">A Jami professzionális megjelenésű, és platformok széles skálájához elérhető. Az alternatívákkal ellentétben a Jami-t használó hívások közvetlenül a felhasználók között zajlanak, mivel nem használ kiszolgálókat a hívások kezelésére.</p>
|
||||
<p>This gives the greatest privacy as the distributed nature of Jami means your calls are only between participants.</p>
|
||||
|
||||
<p>Jami is open-source software that prioritizes user privacy.</p>
|
||||
<p xml:lang="hu">A Jami nyílt forráskódú szoftver, amely előnyben részesíti a felhasználók adatvédelmét.</p>
|
||||
|
||||
<p>Jami has a professional-looking design and is available for a wide range of platforms. Unlike the alternatives, Jami calls are directly between users, as it does not use servers to handle calls.</p>
|
||||
<p xml:lang="hu">A Jami professzionális megjelenésű, és platformok széles skálájához elérhető. Az alternatívákkal ellentétben a Jami közvetlenül a felhasználók között zajlik, mivel nem használ kiszolgálókat a hívások kezelésére.</p>
|
||||
|
||||
<p>This gives the greatest privacy, as the distributed nature of Jami means your calls are only between participants.</p>
|
||||
<p xml:lang="hu">Ez biztosítja a legnagyobb magánéletet, mivel a Jami elosztott jellege azt jelenti, hogy a hívások csak a résztvevők között zajlanak.</p>
|
||||
<p>One-to-one and group conversations with Jami are enhanced with: instant messaging; audio and video calling; recording and sending audio and video messages; file transfers; screen sharing; and, location sharing.</p>
|
||||
<p xml:lang="hu">A Jamival folytatott személyes és csoportos beszélgetéseket a következők javítják: azonnali üzenetküldés; hang- és videohívások; hang- és videoüzenetek rögzítése és küldése; fájlátvitel; képernyőmegosztás; és helymegosztás.</p>
|
||||
|
||||
<p>One-to-one and group conversations with Jami are enhanced with instant messaging, audio and video calling, recording and sending audio and video messages, file transfers, screen sharing, and location sharing.</p>
|
||||
<p xml:lang="hu">A Jamival folytatott személyes és csoportos beszélgetéseket az azonnali üzenetküldés, hang- és videohívások, hang- és videoüzenetek rögzítése és küldése, fájlátvitel, képernyőmegosztás és helymegosztás továbbfejlesztik.</p>
|
||||
|
||||
<p>Jami can also function as a SIP client.</p>
|
||||
<p xml:lang="hu">A Jami SIP-ügyfélként is működhet.</p>
|
||||
<p>Jami has multiple extensions available: Audio Filter; Auto Answer; Green Screen; Watermark; and, Whisper Transcript.</p>
|
||||
<p xml:lang="hu">A Jami-nek több bővítménye is elérhető: hangszűrő; automatikus válasz; zöld képernyő; vízjel; és, suttogó átirat.</p>
|
||||
<p>Jami can be easily deployed in organizations with the “Jami Account Management Server” (JAMS), allowing users to connect with their corporate credentials or create local accounts. JAMS allows you to manage your own Jami community while taking advantage of Jami’s distributed network architecture.</p>
|
||||
<p xml:lang="hu">A Jami könnyen telepíthető a szervezetekben a JAMS (Jami Account Management Server – Jami fiókkezelő kiszolgáló), amely lehetővé teszi a felhasználók számára, hogy csatlakozzanak vállalati hitelesítő adataikhoz, vagy helyi fiókokat hozzanak létre. A JAMS lehetővé teszi saját Jami közösségének kezelését, miközben kihasználja a Jami elosztott hálózati architektúráját.</p>
|
||||
<p>Jami is available for GNU/Linux, Windows, macOS, iOS, Android, and Android TV, making Jami an interoperable and cross-platform communication framework.</p>
|
||||
<p xml:lang="hu">A Jami elérhető GNU/Linux, Windows, macOS, iOS, Android és Android TV rendszereken, így a Jami egy interoperábilis és platformok közötti kommunikációs keretrendszer.</p>
|
||||
<p>Manage multiple SIP accounts, Jami accounts and JAMS accounts with the Jami client installed on one or multiple devices.</p>
|
||||
<p xml:lang="hu">Kezeljen több SIP-fiókot, Jami-fiókot és JAMS-fiókot az egy vagy több eszközre telepített Jami-ügyféllel.</p>
|
||||
<p>Jami is free, unlimited, private, advertising free, compatible, fast, autonomous, and anonymous.</p>
|
||||
|
||||
<p>Multiple Jami extensions are available: Audio Filter, Auto Answer, Green Screen, Segmentation, Watermark, and Whisper Transcript.</p>
|
||||
<p xml:lang="hu">Több Jami bővítmény érhető el: hangszűrő, automatikus válasz, zöld képernyő, szegmentálás, vízjel és suttogó átirat.</p>
|
||||
|
||||
<p>Jami can be easily deployed in organizations with the <em>JAMS (Jami Account Management Server)</em>, allowing users to connect with their corporate credentials or create local accounts. JAMS allows you to manage your own Jami community while taking advantage of Jami’s distributed network architecture.</p>
|
||||
<p xml:lang="hu">A Jami könnyen telepíthető a szervezetekben a <em>JAMS (Jami Account Management Server — JAMS-kezelő-kiszolgáló)</em> segítségével, amely lehetővé teszi a felhasználók számára, hogy csatlakozzanak vállalati hitelesítő adataikhoz, vagy helyi fiókokat hozzanak létre. A JAMS lehetővé teszi a saját Jami közösség kezelését, miközben kihasználja a Jami elosztott hálózati architektúráját.</p>
|
||||
|
||||
<p>Jami is available for GNU/Linux, Windows, macOS, iOS, Android, Android TV, and web browsers, making Jami an interoperable and cross-platform communication framework.</p>
|
||||
<p xml:lang="hu">A Jami elérhető GNU/Linux, Windows, macOS, iOS, Android, Android TV és webböngészők számára, így a Jami interoperábilis és platformok közötti kommunikációs keretrendszerré válik.</p>
|
||||
|
||||
<p>Manage multiple SIP accounts, Jami accounts, and JAMS accounts with the Jami client installed on one or multiple devices.</p>
|
||||
<p xml:lang="hu">Több SIP-, Jami- és JAMS-fiók kezelése az egy vagy több eszközre telepített a Jami-alkalmazással.</p>
|
||||
|
||||
<p>Jami is free, unlimited, private, advertising‑free, compatible, fast, autonomous, and anonymous.</p>
|
||||
<p xml:lang="hu">A Jami ingyenes, korlátlan, privát, reklámmentes, kompatibilis, gyors, autonóm és névtelen.</p>
|
||||
<p>Learn more about:</p>
|
||||
<p xml:lang="hu">További tájékoztatás:</p>
|
||||
<ul>
|
||||
<li>Jami: https://jami.net/</li>
|
||||
<li xml:lang="hu">Jami: https://jami.net/hu/</li>
|
||||
<li>Jami extensions: https://jami.net/extensions/</li>
|
||||
<li xml:lang="hu">Jami-bővítmények: https://jami.net/hu/extensions/</li>
|
||||
<li>“Jami Account Management Server” (JAMS): https://jami.biz/</li>
|
||||
<li xml:lang="hu">JAMS (Jami Account Management Server – Jami fiókkezelő kiszolgáló): https://jami.biz/</li>
|
||||
<li>Jami documentation: https://docs.jami.net/</li>
|
||||
<li xml:lang="hu">Jami-dokumentáció: https://docs.jami.net/hu/</li>
|
||||
</ul>
|
||||
<p>Follow us for more:</p>
|
||||
<p xml:lang="hu">Kövess minket a továbbiakért:</p>
|
||||
<ul>
|
||||
<li>Mastodon: https://mstdn.io/@Jami</li>
|
||||
<li xml:lang="hu">Mastodon: https://mstdn.io/@Jami</li>
|
||||
<li>X: https://x.com/jami_social</li>
|
||||
<li xml:lang="hu">X: https://x.com/jami_social</li>
|
||||
<li>YouTube: https://www.youtube.com/@jami9311</li>
|
||||
<li xml:lang="hu">YouTube: https://www.youtube.com/@jami9311</li>
|
||||
</ul>
|
||||
<p>We’d love to hear from you! Join the Jami community:</p>
|
||||
<p xml:lang="hu">Szívesen hallanánk felőled! Csatlakozzon a Jami közösséghez:</p>
|
||||
<ul>
|
||||
<li>Contribute: https://jami.net/contribute/</li>
|
||||
<li xml:lang="hu">Közreműködés: https://jami.net/hu/contribute/</li>
|
||||
<li>Forum: https://forum.jami.net/</li>
|
||||
<li xml:lang="hu">Fórum: https://forum.jami.net/</li>
|
||||
</ul>
|
||||
<p>Build with Jami on your IoT project: re-use the universal communications technology of Jami with its portable library on your system of choice.</p>
|
||||
<p xml:lang="hu">Építsen a Jamival IoT-projektjére: használja újra a Jami univerzális kommunikációs technológiáját a hordozható könyvtárával a választott rendszerén.</p>
|
||||
|
||||
<p>Build IoT projects with Jami. Re-use the universal communications technology of Jami with its portable library on your system of choice.</p>
|
||||
<p xml:lang="hu">Építsen IoT-projekteket Jamival. Használja újra a Jami univerzális kommunikációs technológiáját annak hordozható könyvtárával az Ön által választott rendszeren.</p>
|
||||
|
||||
<p>Jami for Android TV is tested on NVIDIA SHIELD TV with Logitech cameras.</p>
|
||||
<p xml:lang="hu">A Jami for Android TV-t Logitech kamerákkal ellátott NVIDIA SHIELD TV-n tesztelték.</p>
|
||||
<p>Jami is published under the GPL license, version 3 or higher.</p>
|
||||
<p xml:lang="hu">A Jami a GPL licenc 3-as vagy újabb verziója alatt jelent meg.</p>
|
||||
<p>Copyright © Savoir-faire Linux Inc.</p>
|
||||
<p xml:lang="hu">Szerzői jog © Savoir-faire Linux Inc.</p>
|
||||
<p xml:lang="hu">A Jami Androidra TV-t Logitech kamerákkal ellátott NVIDIA SHIELD TV-n tesztelték.</p>
|
||||
|
||||
<p>Jami is published under the GPL license, version 3 or higher. Copyright © Savoir-faire Linux Inc.</p>
|
||||
<p xml:lang="hu">A Jami a GPL-licenc 3-as vagy újabb verziója alatt jelent meg. Szerzői jog © Savoir-faire Linux Inc.</p>
|
||||
|
||||
</description>
|
||||
|
||||
<url type="homepage">https://jami.net/</url>
|
||||
<requires>
|
||||
<id>net.jami.daemon</id>
|
||||
</requires>
|
||||
|
||||
<url type="bugtracker">https://git.jami.net/savoirfairelinux/jami-client-qt/issues</url>
|
||||
<url type="faq">https://docs.jami.net/user/faq.html</url>
|
||||
<url type="help">https://forum.jami.net/</url>
|
||||
<url type="homepage">https://jami.net/</url>
|
||||
<url type="donation">https://jami.net/whydonate/</url>
|
||||
<url type="translate">https://www.transifex.com/savoirfairelinux/jami</url>
|
||||
<url type="faq">https://docs.jami.net/user/faq.html</url>
|
||||
<url type="translate">https://explore.transifex.com/savoirfairelinux/jami/</url>
|
||||
<url type="contribute">https://jami.net/contribute/</url>
|
||||
<url type="help">https://docs.jami.net/</url>
|
||||
<url type="vcs-browser">https://git.jami.net/savoirfairelinux</url>
|
||||
|
||||
<provides>
|
||||
<binary>jami</binary>
|
||||
<mediatype>x-scheme-handler/jami</mediatype>
|
||||
</provides>
|
||||
|
||||
<!-- Maximum caption length is 60 characters -->
|
||||
<!-- Officially GIF is not an allowed video format, but it appears to work nonetheless -->
|
||||
@ -121,7 +135,15 @@
|
||||
|
||||
<launchable type="desktop-id">net.jami.Jami.desktop</launchable>
|
||||
|
||||
<provides><binary>jami</binary></provides>
|
||||
<!-- https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-keywords -->
|
||||
<keywords>
|
||||
<keyword translate="no">Qt</keyword>
|
||||
<keyword>chat</keyword>
|
||||
<keyword>talk</keyword>
|
||||
<keyword>im</keyword>
|
||||
<keyword>message</keyword>
|
||||
<keyword translate="no">voip</keyword>
|
||||
</keywords>
|
||||
|
||||
<!-- https://specifications.freedesktop.org/menu-spec/latest/apa.html -->
|
||||
<!-- https://specifications.freedesktop.org/menu-spec/latest/apas02.html -->
|
||||
@ -142,5 +164,4 @@
|
||||
<content_attribute id="social-audio">intense</content_attribute>
|
||||
</content_rating>
|
||||
|
||||
<requires><id>net.jami.daemon</id></requires>
|
||||
</component>
|
||||
|
||||
7
extras/packaging/gnu-linux/Jenkinsfile
vendored
7
extras/packaging/gnu-linux/Jenkinsfile
vendored
@ -34,7 +34,8 @@ def SUBMODULES = ['daemon',
|
||||
'3rdparty/SortFilterProxyModel',
|
||||
'3rdparty/md4c',
|
||||
'3rdparty/tidy-html5',
|
||||
'3rdparty/zxing-cpp']
|
||||
'3rdparty/zxing-cpp',
|
||||
'3rdparty/hunspell']
|
||||
def TARGETS = [:]
|
||||
def REMOTE_HOST = env.SSH_HOST_DL_RING_CX
|
||||
def REMOTE_BASE_DIR = '/srv/repository/ring'
|
||||
@ -84,6 +85,10 @@ pipeline {
|
||||
|
||||
environment {
|
||||
TARBALLS = '/var/cache/jami' // set the cache directory
|
||||
BUILD_VERSION = sh(
|
||||
returnStdout: true,
|
||||
script: 'date +"%Y%m%d%H%M"'
|
||||
).trim()
|
||||
}
|
||||
|
||||
stages {
|
||||
|
||||
@ -128,7 +128,8 @@ $(RELEASE_TARBALL_FILENAME): tarballs.manifest
|
||||
./3rdparty/SortFilterProxyModel \
|
||||
./3rdparty/md4c \
|
||||
./3rdparty/tidy-html5 \
|
||||
./3rdparty/zxing-cpp; do \
|
||||
./3rdparty/zxing-cpp \
|
||||
./3rdparty/hunspell; do \
|
||||
(cd "$$m" && git archive --prefix "$$m/" HEAD \
|
||||
| tar xf - -C $(TMPDIR)/$(RELEASE_DIRNAME)); \
|
||||
done
|
||||
@ -168,11 +169,10 @@ DISTRIBUTIONS := \
|
||||
ubuntu_24.04 \
|
||||
ubuntu_24.10 \
|
||||
ubuntu_25.04 \
|
||||
fedora_39 \
|
||||
fedora_40 \
|
||||
fedora_41 \
|
||||
fedora_42 \
|
||||
alma_9 \
|
||||
opensuse-leap_15.5 \
|
||||
alma_10 \
|
||||
opensuse-leap_15.6 \
|
||||
snap
|
||||
|
||||
@ -192,6 +192,7 @@ $(1)-docker-image-name := jami-packaging-$(1)
|
||||
$(1)-docker-image-file := .docker-image-$$($(1)-docker-image-name)
|
||||
$(1)-docker-run-command := docker run \
|
||||
--rm --privileged --security-opt apparmor=docker-default \
|
||||
-e BUILD_VERSION=${BUILD_VERSION} \
|
||||
-e RELEASE_VERSION="$(RELEASE_VERSION)" \
|
||||
-e RELEASE_DIRNAME="$(RELEASE_DIRNAME)" \
|
||||
-e RELEASE_TARBALL_FILENAME="$(RELEASE_TARBALL_FILENAME)" \
|
||||
@ -251,7 +252,7 @@ define guix-pack-command
|
||||
guix pack -C xz -f $(1) -m $(CURDIR)/extras/packaging/gnu-linux/guix/guix-pack-manifest.scm -v3 \
|
||||
-S /usr/bin/jami=bin/jami \
|
||||
-S /usr/share/applications/net.jami.Jami.desktop=share/applications/net.jami.Jami.desktop \
|
||||
-S /usr/share/icons/hicolor/scalable/apps/jami.svg=share/icons/hicolor/scalable/apps/jami.svg \
|
||||
-S /usr/share/icons/hicolor/scalable/apps/net.jami.Jami.svg=share/icons/hicolor/scalable/apps/net.jami.Jami.svg \
|
||||
-S /usr/share/icons/hicolor/48x48/apps/jami.png=share/icons/hicolor/48x48/apps/jami.png \
|
||||
-S /usr/share/metainfo/net.jami.Jami.metainfo.xml=share/metainfo/net.jami.Jami.metainfo.xml \
|
||||
-S /usr/share/swcatalog/xml/jami.xml=share/swcatalog/xml/jami.xml \
|
||||
|
||||
@ -1,105 +1,93 @@
|
||||
FROM fedora:39
|
||||
|
||||
RUN dnf clean all
|
||||
RUN dnf update -y
|
||||
|
||||
FROM almalinux:10
|
||||
RUN dnf clean all && dnf update -y
|
||||
RUN dnf install -y epel-release
|
||||
RUN dnf install -y 'dnf-command(config-manager)'
|
||||
RUN dnf config-manager --set-enabled crb
|
||||
RUN dnf config-manager --set-enabled appstream
|
||||
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
|
||||
dnf install -y mock
|
||||
|
||||
RUN dnf groupinstall -y "X Software Development"
|
||||
|
||||
RUN yum install -y xorg-x11-xauth
|
||||
RUN dnf install -y \
|
||||
git \
|
||||
rpm-build \
|
||||
tar \
|
||||
make \
|
||||
alsa-lib-devel \
|
||||
astyle \
|
||||
autoconf \
|
||||
automake \
|
||||
nasm \
|
||||
speexdsp-devel \
|
||||
pulseaudio-libs-devel \
|
||||
libcanberra-devel \
|
||||
libcurl-devel \
|
||||
libtool \
|
||||
mesa-libgbm-devel \
|
||||
mesa-dri-drivers \
|
||||
bison \
|
||||
check \
|
||||
chrpath \
|
||||
clang15-devel \
|
||||
cmake \
|
||||
cryptopp-devel \
|
||||
cups-devel \
|
||||
dbus-devel \
|
||||
expat-devel \
|
||||
pcre-devel \
|
||||
yaml-cpp-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
yasm \
|
||||
speex-devel \
|
||||
gsm-devel \
|
||||
chrpath \
|
||||
check \
|
||||
astyle \
|
||||
uuid-c++-devel \
|
||||
gettext-devel \
|
||||
gcc-c++ \
|
||||
which \
|
||||
alsa-lib-devel \
|
||||
systemd-devel \
|
||||
libuuid-devel \
|
||||
uuid-devel \
|
||||
gnutls-devel \
|
||||
nettle-devel \
|
||||
opus-devel \
|
||||
patch \
|
||||
jsoncpp-devel \
|
||||
libnatpmp-devel \
|
||||
webkitgtk4-devel \
|
||||
cryptopp-devel \
|
||||
libva-devel \
|
||||
libvdpau-devel \
|
||||
msgpack-devel \
|
||||
NetworkManager-libnm-devel \
|
||||
openssl-devel \
|
||||
clutter-devel \
|
||||
clutter-gtk-devel \
|
||||
libappindicator-gtk3-devel \
|
||||
libnotify-devel \
|
||||
libupnp-devel \
|
||||
qrencode-devel \
|
||||
libargon2-devel \
|
||||
libsndfile-devel \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
clang \
|
||||
clang-devel \
|
||||
llvm-devel \
|
||||
nodejs \
|
||||
flex \
|
||||
fmt-devel \
|
||||
gcc-c++ \
|
||||
gettext-devel \
|
||||
git \
|
||||
gnutls-devel \
|
||||
gperf \
|
||||
gsm-devel \
|
||||
gstreamer1 gstreamer1-devel \
|
||||
gstreamer1-plugins-bad-free-devel \
|
||||
gstreamer1-plugins-base-devel \
|
||||
gstreamer1-plugins-good \
|
||||
gstreamer1-plugins-bad-free-devel \
|
||||
nss-devel \
|
||||
jsoncpp-devel \
|
||||
libX11-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
libXrender-devel \
|
||||
libappindicator-gtk3-devel \
|
||||
libargon2-devel \
|
||||
libcanberra-devel \
|
||||
libcurl-devel \
|
||||
libdrm \
|
||||
libnatpmp-devel \
|
||||
libnotify \
|
||||
libnotify-devel \
|
||||
libsndfile-devel \
|
||||
libstdc++-static \
|
||||
libtool \
|
||||
libupnp-devel \
|
||||
libuuid-devel \
|
||||
libva-devel \
|
||||
libvdpau-devel \
|
||||
libxcb* \
|
||||
libxkb* \
|
||||
libX11-devel \
|
||||
vulkan-devel \
|
||||
libXrender-devel \
|
||||
xcb-util-* \
|
||||
xz \
|
||||
xkeyboard-config \
|
||||
libnotify \
|
||||
wget \
|
||||
libstdc++-static \
|
||||
sqlite-devel \
|
||||
perl-generators \
|
||||
perl-English \
|
||||
libxshmfence-devel \
|
||||
llvm15-devel \
|
||||
make \
|
||||
mesa-dri-drivers \
|
||||
mesa-libgbm-devel \
|
||||
msgpack-devel \
|
||||
nasm \
|
||||
nettle-devel \
|
||||
NetworkManager-libnm-devel \
|
||||
ninja-build \
|
||||
clang \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3.10 \
|
||||
cups-devel \
|
||||
pipewire-devel
|
||||
|
||||
nodejs \
|
||||
nss-devel \
|
||||
openssl-devel \
|
||||
opus-devel \
|
||||
pcre2-devel \
|
||||
perl-English \
|
||||
perl-generators \
|
||||
pipewire-devel \
|
||||
pulseaudio-libs-devel \
|
||||
python3-html5lib \
|
||||
qrencode-devel \
|
||||
speex-devel \
|
||||
speexdsp-devel \
|
||||
sqlite-devel \
|
||||
systemd-devel \
|
||||
uuid-devel \
|
||||
vulkan-devel \
|
||||
webkitgtk6.0-devel \
|
||||
wget \
|
||||
which \
|
||||
xcb-util-* \
|
||||
xkeyboard-config \
|
||||
yaml-cpp-devel \
|
||||
yasm
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
@ -70,9 +70,8 @@ RUN dnf install -y \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
clang \
|
||||
clang-devel \
|
||||
llvm-devel \
|
||||
clang16-devel \
|
||||
llvm16-devel \
|
||||
nodejs \
|
||||
flex \
|
||||
gstreamer1 gstreamer1-devel \
|
||||
@ -96,7 +95,6 @@ RUN dnf install -y \
|
||||
perl-English \
|
||||
libxshmfence-devel \
|
||||
ninja-build \
|
||||
clang \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3-html5lib \
|
||||
|
||||
@ -11,14 +11,6 @@ RUN apt-get update && \
|
||||
libdbus-1-dev \
|
||||
wget
|
||||
|
||||
# As of January 2024, the default compiler on Debian unstable is GCC 13.2.0, which
|
||||
# is unable to build one of Qt 6.6.1's dependencies, see:
|
||||
# https://github.com/qt/qtquick3d-assimp/commit/253f8bfa621a9fa6cd2c36291cdaa8c60c99322c
|
||||
# The linked commit above fixes the problem and is included in more recent versions of Qt.
|
||||
# For now, we use GCC 12 as a temporary workaround:
|
||||
ADD extras/packaging/gnu-linux/scripts/install-gcc-debian.sh /opt/install-gcc-debian.sh
|
||||
RUN /opt/install-gcc-debian.sh 12
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/prebuild-package-debian.sh /opt/prebuild-package-debian.sh
|
||||
|
||||
COPY extras/packaging/gnu-linux/rules/debian-qt/control /tmp/builddeps/debian/control
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
FROM fedora:40
|
||||
FROM fedora:42
|
||||
|
||||
RUN dnf clean all
|
||||
RUN dnf update -y
|
||||
@ -6,12 +6,10 @@ RUN dnf update -y
|
||||
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
|
||||
dnf install -y mock
|
||||
|
||||
RUN dnf groupinstall -y "X Software Development"
|
||||
RUN dnf group install -y x-software-development
|
||||
|
||||
RUN dnf install -y \
|
||||
git \
|
||||
rpm-build \
|
||||
tar \
|
||||
make \
|
||||
autoconf \
|
||||
automake \
|
||||
@ -22,13 +20,10 @@ RUN dnf install -y \
|
||||
libcurl-devel \
|
||||
libtool \
|
||||
mesa-libgbm-devel \
|
||||
mesa-dri-drivers \
|
||||
dbus-devel \
|
||||
expat-devel \
|
||||
pcre-devel \
|
||||
yaml-cpp-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
yasm \
|
||||
speex-devel \
|
||||
gsm-devel \
|
||||
@ -37,16 +32,15 @@ RUN dnf install -y \
|
||||
astyle \
|
||||
uuid-c++-devel \
|
||||
gettext-devel \
|
||||
gcc-c++ \
|
||||
gcc14 \
|
||||
gcc14-c++ \
|
||||
which \
|
||||
alsa-lib-devel \
|
||||
systemd-devel \
|
||||
libuuid-devel \
|
||||
uuid-devel \
|
||||
gnutls-devel \
|
||||
nettle-devel \
|
||||
opus-devel \
|
||||
patch \
|
||||
jsoncpp-devel \
|
||||
libnatpmp-devel \
|
||||
webkitgtk4-devel \
|
||||
@ -64,28 +58,20 @@ RUN dnf install -y \
|
||||
qrencode-devel \
|
||||
libargon2-devel \
|
||||
libsndfile-devel \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
clang \
|
||||
clang-devel \
|
||||
llvm-devel \
|
||||
clang18-devel \
|
||||
llvm18-devel \
|
||||
nodejs \
|
||||
flex \
|
||||
gstreamer1 gstreamer1-devel \
|
||||
gstreamer1-plugins-base-devel \
|
||||
gstreamer1-plugins-good \
|
||||
gstreamer1-plugins-bad-free-devel \
|
||||
nss-devel \
|
||||
libxcb* \
|
||||
libxkb* \
|
||||
libX11-devel \
|
||||
vulkan-devel \
|
||||
libXrender-devel \
|
||||
xcb-util-* \
|
||||
xz \
|
||||
xkeyboard-config \
|
||||
libnotify \
|
||||
wget \
|
||||
libstdc++-static \
|
||||
sqlite-devel \
|
||||
@ -93,13 +79,19 @@ RUN dnf install -y \
|
||||
perl-English \
|
||||
libxshmfence-devel \
|
||||
ninja-build \
|
||||
clang \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3.10 \
|
||||
cups-devel \
|
||||
pipewire-devel
|
||||
|
||||
# Use GCC 14 instead of GCC 15 (the default on Fedora 42)
|
||||
# because Qt 6.6.3 fails to build when using the latter.
|
||||
RUN rm /usr/bin/gcc /usr/bin/g++ /usr/bin/c++ && \
|
||||
ln -s /usr/bin/gcc-14 /usr/bin/gcc && \
|
||||
ln -s /usr/bin/g++-14 /usr/bin/g++ && \
|
||||
ln -s /usr/bin/g++-14 /usr/bin/c++
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
@ -1,117 +0,0 @@
|
||||
FROM opensuse/leap:15.5
|
||||
|
||||
RUN zypper --gpg-auto-import-keys refresh
|
||||
|
||||
RUN zypper --non-interactive install -y \
|
||||
dnf \
|
||||
dnf-command\(builddep\) \
|
||||
rpmdevtools \
|
||||
Mesa-dri-devel Mesa-dri \
|
||||
git \
|
||||
gcc10 \
|
||||
gcc10-c++ \
|
||||
rpm-build \
|
||||
tar \
|
||||
make \
|
||||
autoconf \
|
||||
automake \
|
||||
nasm \
|
||||
speexdsp-devel \
|
||||
libpulse-devel \
|
||||
libcanberra-devel \
|
||||
libcurl-devel \
|
||||
libtool \
|
||||
pcre-devel \
|
||||
yaml-cpp-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
yasm \
|
||||
speex-devel \
|
||||
libgsm-devel \
|
||||
chrpath \
|
||||
check \
|
||||
astyle \
|
||||
gettext-devel \
|
||||
which \
|
||||
alsa-lib-devel \
|
||||
systemd-devel \
|
||||
libuuid-devel \
|
||||
uuid-devel \
|
||||
libopus-devel \
|
||||
patch \
|
||||
jsoncpp-devel \
|
||||
webkit2gtk3-devel \
|
||||
libcryptopp-devel \
|
||||
libva-devel \
|
||||
libvdpau-devel \
|
||||
msgpack-c-devel \
|
||||
msgpack-cxx-devel \
|
||||
clutter-devel \
|
||||
openssl-devel \
|
||||
clutter-gtk-devel \
|
||||
libnma-devel \
|
||||
libcryptopp-devel \
|
||||
libexpat-devel \
|
||||
gnome-icon-theme-symbolic \
|
||||
libgsm-devel \
|
||||
gtk3-devel \
|
||||
libappindicator-devel \
|
||||
sqlite-devel \
|
||||
ffmpeg-4-libavutil-devel \
|
||||
gtk3-devel\
|
||||
qrencode-devel \
|
||||
python310 \
|
||||
python3-python-dateutil \
|
||||
python3-html5lib \
|
||||
libsndfile-devel \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
flex \
|
||||
ffmpeg ffmpeg-devel \
|
||||
nodejs20 \
|
||||
mozilla-nss-devel \
|
||||
python-xml \
|
||||
python3-six \
|
||||
python3-importlib-metadata \
|
||||
libxcb* \
|
||||
libxkb* \
|
||||
libX11-devel \
|
||||
libXrender-devel \
|
||||
libfreetype6 \
|
||||
xcb-util-image-devel \
|
||||
xcb-util-keysyms-devel \
|
||||
xcb-util-renderutil-devel \
|
||||
xcb-util-wm-devel \
|
||||
xorg-x11-devel \
|
||||
xz \
|
||||
xkeyboard-config \
|
||||
libnotify \
|
||||
argon2-devel \
|
||||
libxshmfence-devel \
|
||||
xproto-devel \
|
||||
xcb-proto-devel \
|
||||
xcb-* \
|
||||
xorg-* \
|
||||
vulkan-devel \
|
||||
ninja \
|
||||
gstreamer-devel \
|
||||
gstreamer-plugins-good \
|
||||
gstreamer-plugins-bad-devel \
|
||||
gstreamer-plugins-base-devel \
|
||||
cmake \
|
||||
wget \
|
||||
pipewire-devel
|
||||
|
||||
# openSUSE Leap 15.5 comes with Python 3.6 by default,
|
||||
# but we need at least 3.7 to compile Qt 6.6.1
|
||||
RUN rm /usr/bin/python3 && ln -s /usr/bin/python3.10 /usr/bin/python3
|
||||
|
||||
RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 50
|
||||
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 50
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
ENV CC=gcc
|
||||
ENV CXX=g++
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
@ -36,12 +36,6 @@ RUN curl -L $(curl -H 'X-Ubuntu-Series: 16' 'https://api.snapcraft.io/api/v1/sna
|
||||
RUN mkdir -p /snap/snapcraft
|
||||
RUN unsquashfs -d /snap/snapcraft/current snapcraft.snap
|
||||
|
||||
# Fix Python3 installation: Make sure we use the interpreter from
|
||||
# the snapcraft snap:
|
||||
RUN unlink /snap/snapcraft/current/usr/bin/python3
|
||||
RUN ln -s /snap/snapcraft/current/usr/bin/python3.* /snap/snapcraft/current/usr/bin/python3
|
||||
RUN echo /snap/snapcraft/current/lib/python3.*/site-packages >> /snap/snapcraft/current/usr/lib/python3/dist-packages/site-packages.pth
|
||||
|
||||
# Create a snapcraft runner
|
||||
RUN mkdir -p /snap/bin
|
||||
RUN echo "#!/bin/sh" > /snap/bin/snapcraft
|
||||
|
||||
@ -24,7 +24,10 @@
|
||||
(list
|
||||
;; Minimal requirements of the daemon contrib build system.
|
||||
"coreutils"
|
||||
"gcc-toolchain"
|
||||
;; When using GCC 15, Jami fails to link with errors like:
|
||||
;; ld: CMakeFiles/jami.dir/src/app/main.cpp.o:(.rodata+0x0):
|
||||
;; multiple definition of `QtPrivate::IsFloatType_v<_Float16>'
|
||||
"gcc-toolchain@14"
|
||||
"git-minimal"
|
||||
"grep"
|
||||
"gzip"
|
||||
@ -42,18 +45,12 @@
|
||||
"alsa-lib"
|
||||
"autoconf"
|
||||
"automake"
|
||||
"asio"
|
||||
"bash"
|
||||
"bzip2"
|
||||
"cmake"
|
||||
"dbus"
|
||||
;; Bundled because broken with GCC 7 upstream (unmaintained). When
|
||||
;; attempting to use it, it would cause confusing errors such as
|
||||
;; "ld: ../src/.libs/libring.a(libupnpcontrol_la-upnp_context.o): in
|
||||
;; function `jami::upnp::UPnPContext::updateMappingList(bool)':
|
||||
;; upnp_context.cpp:(.text+0xa4be): undefined reference to
|
||||
;; `std::__cxx11::basic_ostringstream<char, std::char_traits<char>,
|
||||
;; std::allocator<char> >::basic_ostringstream()'
|
||||
;;"dbus-c++" ;for dbusxx-xml2cpp
|
||||
;;"dhtnet" ;bundled because tightly coupled
|
||||
"diffutils"
|
||||
"doxygen"
|
||||
"eudev" ;udev library
|
||||
@ -67,6 +64,7 @@
|
||||
"gsm"
|
||||
"gtk-doc"
|
||||
"http-parser"
|
||||
"jack@0"
|
||||
"jsoncpp"
|
||||
"libarchive"
|
||||
"libgit2"
|
||||
@ -84,16 +82,24 @@
|
||||
"patch"
|
||||
"pcre"
|
||||
"perl"
|
||||
"pipewire"
|
||||
;;"pjproject" ;bundled because patched
|
||||
"pulseaudio"
|
||||
"sdbus-c++@1"
|
||||
"speex"
|
||||
"speexdsp"
|
||||
"webrtc-audio-processing@0"
|
||||
"which"
|
||||
"yaml-cpp"
|
||||
"yasm"
|
||||
|
||||
;; For the Qt client.
|
||||
"glib"
|
||||
"hunspell"
|
||||
"libnotify"
|
||||
"libxcb"
|
||||
"libxkbcommon"
|
||||
"md4c"
|
||||
"network-manager" ;libnm
|
||||
"qrencode"
|
||||
"qtbase"
|
||||
@ -103,10 +109,13 @@
|
||||
"qtnetworkauth"
|
||||
"qtpositioning"
|
||||
"qtsvg"
|
||||
"qwindowkit"
|
||||
"qttools"
|
||||
"qtwebchannel"
|
||||
"qtwebengine"
|
||||
"tidy-html"
|
||||
"vulkan-headers"
|
||||
"zxing-cpp"
|
||||
|
||||
;; For tests and debugging.
|
||||
"file"
|
||||
|
||||
@ -261,7 +261,7 @@ Build-Depends: debhelper (>= 9),
|
||||
gperf,
|
||||
khronos-api,
|
||||
# libasound2-dev [linux-any],
|
||||
libavcodec-dev (>= 7:3.4.8~),
|
||||
libavcodec-dev (>= 7:3.4.8~) | libavcodec-extra-dev (>= 7:3.4.8~),
|
||||
libavformat-dev (>= 7:3.4.8~),
|
||||
libavutil-dev (>= 7:3.4.8~),
|
||||
libcap-dev [linux-any],
|
||||
|
||||
@ -30,7 +30,7 @@ Build-Depends: debhelper (>= 9),
|
||||
libspeex-dev,
|
||||
libspeexdsp-dev,
|
||||
uuid-dev,
|
||||
libavcodec-dev,
|
||||
libavcodec-dev | libavcodec-extra-dev,
|
||||
libavutil-dev,
|
||||
libavformat-dev,
|
||||
libswscale-dev,
|
||||
|
||||
@ -74,7 +74,7 @@ override_dh_auto_build:
|
||||
--disable-gsm \
|
||||
--disable-speexdsp \
|
||||
--disable-natpmp \
|
||||
--enable-gnutls $(BUNDLED_PKGS) && \
|
||||
$(BUNDLED_PKGS) && \
|
||||
make list && \
|
||||
make -j$(NO_CPUS) V=1
|
||||
cd daemon && \
|
||||
@ -91,6 +91,7 @@ override_dh_auto_build:
|
||||
mkdir build && \
|
||||
cd build && \
|
||||
cmake \
|
||||
-DBUILD_VERSION=$(BUILD_VERSION) \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DLIBJAMI_BUILD_DIR=$(CURDIR)/daemon/src \
|
||||
-DENABLE_LIBWRAP=true \
|
||||
|
||||
@ -49,7 +49,7 @@ BuildRequires: libXfixes-devel
|
||||
BuildRequires: libuuid-devel
|
||||
BuildRequires: libva-devel
|
||||
BuildRequires: libvdpau-devel
|
||||
BuildRequires: pcre-devel
|
||||
BuildRequires: (pcre-devel or pcre2-devel)
|
||||
BuildRequires: pipewire-devel
|
||||
BuildRequires: uuid-devel
|
||||
BuildRequires: yaml-cpp-devel
|
||||
|
||||
@ -31,6 +31,7 @@ URL: https://jami.net/
|
||||
Source: jami-libqt-%{version}.tar.xz
|
||||
Patch0: 0001-fix-gcc14.patch
|
||||
Patch1: 0002-qtwebengine-add-missing-chromium-dependencies.patch
|
||||
Patch2: 0003-fix-embree-linking-errors.patch
|
||||
|
||||
%global gst 0.10
|
||||
%if 0%{?fedora} || 0%{?rhel} > 7
|
||||
@ -68,6 +69,7 @@ This package contains Qt libraries for Jami.
|
||||
%setup -n qt-everywhere-src-%{version}
|
||||
%patch -P 0 -p1
|
||||
%patch -P 1 -p1
|
||||
%patch -P 2 -p1
|
||||
|
||||
%build
|
||||
echo "Building Qt using %{job_count} parallel jobs"
|
||||
|
||||
@ -1,16 +1,7 @@
|
||||
%define name jami
|
||||
%define version RELEASE_VERSION
|
||||
%define release 0
|
||||
|
||||
# The AppStream 1.0 spec says that the catalog file must be put in /usr/share/swcatalog/xml
|
||||
# (see https://www.freedesktop.org/software/appstream/docs/chap-CatalogData.html).
|
||||
#
|
||||
# However, openSUSE Leap still uses the legacy path /usr/share/app-info/xmls as of version 15.5.
|
||||
%if 0%{?sle_version} && 0%{?sle_version} <= 150500
|
||||
%define appstream_catalog_dir /share/app-info/xmls
|
||||
%else
|
||||
%define appstream_catalog_dir /share/swcatalog/xml
|
||||
%endif
|
||||
|
||||
# Exclude vendored Qt6 from dependency generator
|
||||
%define __requires_exclude ^libQt6.*$
|
||||
@ -82,6 +73,7 @@ cd %{_builddir}/jami-%{version} && \
|
||||
-DAPPSTREAM_CATALOG_DIR=%{appstream_catalog_dir} \
|
||||
-DWITH_DAEMON_SUBMODULE=true \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_VERSION=${BUILD_VERSION} \
|
||||
..
|
||||
make -C %{_builddir}/jami-%{version}/build %{_smp_mflags} V=2
|
||||
|
||||
@ -93,7 +85,7 @@ DESTDIR=%{buildroot} make -C %{_builddir}/jami-%{version}/build install V=2
|
||||
%{_bindir}/jami
|
||||
%{_datadir}/applications/net.jami.Jami.desktop
|
||||
%{_datadir}/jami/net.jami.Jami.desktop
|
||||
%{_datadir}/icons/hicolor/scalable/apps/jami.svg
|
||||
%{_datadir}/icons/hicolor/scalable/apps/net.jami.Jami.svg
|
||||
%{_datadir}/icons/hicolor/48x48/apps/jami.png
|
||||
%{_datadir}/pixmaps/jami.xpm
|
||||
%{_datadir}/metainfo/net.jami.Jami.metainfo.xml
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
From 709d0b0cf45b920f63960a70725138dbaf7ec721 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
|
||||
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
|
||||
Date: Wed, 18 Jun 2025 15:53:55 -0400
|
||||
Subject: [PATCH] Fix embree linking errors
|
||||
|
||||
Patch taken from:
|
||||
https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=37bd373cd33c36f8dd44e71be25fa6ea24cf4588
|
||||
---
|
||||
qtquick3d/src/3rdparty/embree/CMakeLists.txt | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/qtquick3d/src/3rdparty/embree/CMakeLists.txt b/qtquick3d/src/3rdparty/embree/CMakeLists.txt
|
||||
index cf27196de2..332bbd17ca 100644
|
||||
--- a/qtquick3d/src/3rdparty/embree/CMakeLists.txt
|
||||
+++ b/qtquick3d/src/3rdparty/embree/CMakeLists.txt
|
||||
@@ -62,6 +62,9 @@ if (IOS)
|
||||
endif()
|
||||
|
||||
# Use SSE2 only, ignore AVX/SSE4.2 for now
|
||||
+if (TEST_architecture_arch STREQUAL x86_64)
|
||||
+ qt_internal_extend_target(BundledEmbree COMPILE_OPTIONS -mno-avx -mno-sse4.2)
|
||||
+endif()
|
||||
qt_internal_extend_target(BundledEmbree DEFINES
|
||||
EMBREE_TARGET_SSE2
|
||||
__SSE2__
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@ -270,7 +270,7 @@ parts:
|
||||
ls
|
||||
snapcraftctl pull
|
||||
sed -i -E 's|(tmpName) << (PACKAGE_NAME << "_shm_")|\1 << "snap.jami." << \2|' ./daemon/src/media/video/sinkclient.cpp
|
||||
sed -i -E 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/jami.svg|' extras/data/net.jami.Jami.desktop
|
||||
sed -i -E 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/net.jami.Jami.svg|' extras/data/net.jami.Jami.desktop
|
||||
override-build: |
|
||||
$SNAPCRAFT_PART_BUILD/extras/packaging/gnu-linux/scripts/install-pipewire-from-source.sh
|
||||
|
||||
@ -293,7 +293,8 @@ parts:
|
||||
cmake .. -DENABLE_LIBWRAP=true \
|
||||
-DLIBJAMI_BUILD_DIR=$SNAPCRAFT_PART_BUILD/daemon/src \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_VERSION=BUILD_VERSION_PLACEHOLDER
|
||||
make -j$SNAPCRAFT_PARALLEL_BUILD_COUNT
|
||||
DESTDIR=$SNAPCRAFT_PART_INSTALL make install
|
||||
build-packages:
|
||||
|
||||
@ -101,14 +101,14 @@ if [ ! -f "${RPM_PATH}" ]; then
|
||||
# Cache the built Qt RPM package.
|
||||
if [[ "${DISTRIBUTION:0:4}" == "rhel" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el8.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_39" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc39.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_40" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc40.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_41" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc41.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_42" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc42.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "alma_9" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el9.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "alma_10" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el10.x86_64.rpm "${RPM_PATH}"
|
||||
else
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-*.rpm "${RPM_PATH}"
|
||||
fi
|
||||
@ -132,7 +132,7 @@ rpmbuild --define "debug_package %{nil}" -ba jami-libclient.spec
|
||||
rpmbuild --define "debug_package %{nil}" -ba jami-qt.spec
|
||||
|
||||
# Build the Qt client.
|
||||
rpmbuild --define "debug_package %{nil}" -ba jami.spec
|
||||
rpmbuild --define "debug_package %{nil}" --define "BUILD_VERSION ${BUILD_VERSION}" -ba jami.spec
|
||||
|
||||
# Move the built packages to the output directory.
|
||||
mv /root/rpmbuild/RPMS/*/* /opt/output
|
||||
|
||||
@ -29,6 +29,9 @@ cp -r extras/packaging/gnu-linux/rules/snap/${SNAP_PKG_NAME}/snapcraft.yaml .
|
||||
# set the version and tarball filename
|
||||
sed -i "s/RELEASE_VERSION/${RELEASE_VERSION}/g" snapcraft.yaml
|
||||
|
||||
# set the build version of the app
|
||||
sed -i "s/BUILD_VERSION_PLACEHOLDER/${BUILD_VERSION}/g" snapcraft.yaml
|
||||
|
||||
snapcraft # requires snapcraft >= 4.8
|
||||
|
||||
# move the built snap to output
|
||||
|
||||
@ -212,6 +212,7 @@ def init_submodules():
|
||||
"3rdparty/md4c",
|
||||
"3rdparty/tidy-html5",
|
||||
"3rdparty/zxing-cpp",
|
||||
"3rdparty/hunspell",
|
||||
]
|
||||
if execute_cmd(["git", "submodule", "update", "--init" ] + submodules,
|
||||
False):
|
||||
@ -262,7 +263,7 @@ def cmake_build(config_str, env_vars, cmake_build_dir):
|
||||
return True
|
||||
|
||||
|
||||
def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None):
|
||||
def build(config_str, qt_dir, tests, build_version, enable_crash_reports, crash_report_url=None):
|
||||
"""Use cmake to build the project."""
|
||||
print("Building with Qt at " + qt_dir)
|
||||
|
||||
@ -293,6 +294,9 @@ def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None
|
||||
else:
|
||||
cmake_options.append("-DENABLE_CRASHREPORTS=OFF")
|
||||
|
||||
if build_version:
|
||||
cmake_options.append("-DBUILD_VERSION=" + build_version)
|
||||
|
||||
# Make sure the build directory exists.
|
||||
if not os.path.exists(build_dir):
|
||||
os.makedirs(build_dir)
|
||||
@ -306,11 +310,11 @@ def build(config_str, qt_dir, tests, enable_crash_reports, crash_report_url=None
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def deploy_runtimes(config_str, qt_dir):
|
||||
def deploy_runtimes(qt_dir):
|
||||
"""Deploy the dependencies to the runtime directory."""
|
||||
print("Deploying runtime dependencies")
|
||||
|
||||
runtime_dir = os.path.join(repo_root_dir, "x64", config_str)
|
||||
runtime_dir = os.path.join(repo_root_dir, "x64", "Release")
|
||||
stamp_file = os.path.join(runtime_dir, ".deploy.stamp")
|
||||
if os.path.exists(stamp_file):
|
||||
return
|
||||
@ -469,6 +473,8 @@ def parse_args():
|
||||
help='Sets the Qt root path')
|
||||
parser.add_argument(
|
||||
"-a", "--arch", default="x64", help="Sets the build architecture")
|
||||
parser.add_argument(
|
||||
"--build-version", help="Sets the build version string used for defining app build version")
|
||||
parser.add_argument(
|
||||
"-t", "--tests", action="store_true", help="Build and run tests")
|
||||
parser.add_argument(
|
||||
@ -551,10 +557,11 @@ def main():
|
||||
def do_build(do_tests):
|
||||
if not parsed_args.skip_build:
|
||||
build(config_str, parsed_args.qt, do_tests,
|
||||
parsed_args.build_version,
|
||||
parsed_args.enable_crash_reports,
|
||||
parsed_args.crash_report_url)
|
||||
if not parsed_args.skip_deploy:
|
||||
deploy_runtimes(config_str, parsed_args.qt)
|
||||
deploy_runtimes(parsed_args.qt)
|
||||
|
||||
if parsed_args.subcommand == "pack":
|
||||
do_build(False)
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
382
resources/misc/available_dictionaries.json
Normal file
382
resources/misc/available_dictionaries.json
Normal file
@ -0,0 +1,382 @@
|
||||
{
|
||||
"af_ZA": {
|
||||
"nativeName": "Afrikaans (Suid-Afrika)",
|
||||
"path": "af_ZA/af_ZA"
|
||||
},
|
||||
"an_ES": {
|
||||
"nativeName": "aragonés (España)",
|
||||
"path": "an_ES/an_ES"
|
||||
},
|
||||
"ar": {
|
||||
"nativeName": "العربية",
|
||||
"path": "ar/ar"
|
||||
},
|
||||
"as_IN": {
|
||||
"nativeName": "অসমীয়া (भारत)",
|
||||
"path": "as_IN/as_IN"
|
||||
},
|
||||
"be-official": {
|
||||
"nativeName": "беларуская",
|
||||
"path": "be_BY/be-official"
|
||||
},
|
||||
"bg_BG": {
|
||||
"nativeName": "български",
|
||||
"path": "bg_BG/bg_BG"
|
||||
},
|
||||
"bn_BD": {
|
||||
"nativeName": "বাঙ্গালি (বাংলাদেশ)",
|
||||
"path": "bn_BD/bn_BD"
|
||||
},
|
||||
"bn_IN": {
|
||||
"nativeName": "বাঙ্গালি (ভারত)",
|
||||
"path": "bn_BD/bn_IN"
|
||||
},
|
||||
"bo": {
|
||||
"nativeName": "བོད་སྐད་",
|
||||
"path": "bo/bo"
|
||||
},
|
||||
"br_FR": {
|
||||
"nativeName": "breton (France)",
|
||||
"path": "br_FR/br_FR"
|
||||
},
|
||||
"bs_BA": {
|
||||
"nativeName": "bosanski",
|
||||
"path": "bs_BA/bs_BA"
|
||||
},
|
||||
"cs_CZ": {
|
||||
"nativeName": "čeština",
|
||||
"path": "cs_CZ/cs_CZ"
|
||||
},
|
||||
"da_DK": {
|
||||
"nativeName": "dansk",
|
||||
"path": "da_DK/da_DK"
|
||||
},
|
||||
"de_AT_frami": {
|
||||
"nativeName": "Deutsch (Österreich)",
|
||||
"path": "de/de_AT_frami"
|
||||
},
|
||||
"de_CH_frami": {
|
||||
"nativeName": "Deutsch (Schweiz)",
|
||||
"path": "de/de_CH_frami"
|
||||
},
|
||||
"de_DE_frami": {
|
||||
"nativeName": "Deutsch (Deutschland)",
|
||||
"path": "de/de_DE_frami"
|
||||
},
|
||||
"el_GR": {
|
||||
"nativeName": "Ελληνικά",
|
||||
"path": "el_GR/el_GR"
|
||||
},
|
||||
"en_AU": {
|
||||
"nativeName": "English (Australia)",
|
||||
"path": "en/en_AU"
|
||||
},
|
||||
"en_CA": {
|
||||
"nativeName": "English (Canada)",
|
||||
"path": "en/en_CA"
|
||||
},
|
||||
"en_GB": {
|
||||
"nativeName": "English (British)",
|
||||
"path": "en/en_GB"
|
||||
},
|
||||
"en_US": {
|
||||
"nativeName": "English (American)",
|
||||
"path": "en/en_US"
|
||||
},
|
||||
"en_ZA": {
|
||||
"nativeName": "English (Suid-Afrika)",
|
||||
"path": "en/en_ZA"
|
||||
},
|
||||
"eo": {
|
||||
"nativeName": "esperanto",
|
||||
"path": "eo/eo"
|
||||
},
|
||||
"es_AR": {
|
||||
"nativeName": "español (Argentina)",
|
||||
"path": "es/es_AR"
|
||||
},
|
||||
"es_BO": {
|
||||
"nativeName": "español (Bolivia)",
|
||||
"path": "es/es_BO"
|
||||
},
|
||||
"es_CL": {
|
||||
"nativeName": "español (Chile)",
|
||||
"path": "es/es_CL"
|
||||
},
|
||||
"es_CO": {
|
||||
"nativeName": "español (Colombia)",
|
||||
"path": "es/es_CO"
|
||||
},
|
||||
"es_CR": {
|
||||
"nativeName": "español (Costa Rica)",
|
||||
"path": "es/es_CR"
|
||||
},
|
||||
"es_CU": {
|
||||
"nativeName": "español (Cuba)",
|
||||
"path": "es/es_CU"
|
||||
},
|
||||
"es_DO": {
|
||||
"nativeName": "español (República Dominicana)",
|
||||
"path": "es/es_DO"
|
||||
},
|
||||
"es_EC": {
|
||||
"nativeName": "español (Ecuador)",
|
||||
"path": "es/es_EC"
|
||||
},
|
||||
"es_ES": {
|
||||
"nativeName": "español (España)",
|
||||
"path": "es/es_ES"
|
||||
},
|
||||
"es_GQ": {
|
||||
"nativeName": "español (Guinea Ecuatorial)",
|
||||
"path": "es/es_GQ"
|
||||
},
|
||||
"es_GT": {
|
||||
"nativeName": "español (Guatemala)",
|
||||
"path": "es/es_GT"
|
||||
},
|
||||
"es_HN": {
|
||||
"nativeName": "español (Honduras)",
|
||||
"path": "es/es_HN"
|
||||
},
|
||||
"es_MX": {
|
||||
"nativeName": "español (México)",
|
||||
"path": "es/es_MX"
|
||||
},
|
||||
"es_NI": {
|
||||
"nativeName": "español (Nicaragua)",
|
||||
"path": "es/es_NI"
|
||||
},
|
||||
"es_PA": {
|
||||
"nativeName": "español (Panamá)",
|
||||
"path": "es/es_PA"
|
||||
},
|
||||
"es_PE": {
|
||||
"nativeName": "español (Perú)",
|
||||
"path": "es/es_PE"
|
||||
},
|
||||
"es_PH": {
|
||||
"nativeName": "español (Pilipinas)",
|
||||
"path": "es/es_PH"
|
||||
},
|
||||
"es_PR": {
|
||||
"nativeName": "español (Puerto Rico)",
|
||||
"path": "es/es_PR"
|
||||
},
|
||||
"es_PY": {
|
||||
"nativeName": "español (Paraguai)",
|
||||
"path": "es/es_PY"
|
||||
},
|
||||
"es_SV": {
|
||||
"nativeName": "español (El Salvador)",
|
||||
"path": "es/es_SV"
|
||||
},
|
||||
"es_US": {
|
||||
"nativeName": "español (United States)",
|
||||
"path": "es/es_US"
|
||||
},
|
||||
"es_UY": {
|
||||
"nativeName": "español (Uruguay)",
|
||||
"path": "es/es_UY"
|
||||
},
|
||||
"es_VE": {
|
||||
"nativeName": "español (Venezuela)",
|
||||
"path": "es/es_VE"
|
||||
},
|
||||
"et_EE": {
|
||||
"nativeName": "eesti",
|
||||
"path": "et_EE/et_EE"
|
||||
},
|
||||
"fa-IR": {
|
||||
"nativeName": "فارسی",
|
||||
"path": "fa_IR/fa-IR"
|
||||
},
|
||||
"fr": {
|
||||
"nativeName": "français",
|
||||
"path": "fr_FR/fr"
|
||||
},
|
||||
"gd_GB": {
|
||||
"nativeName": "Gàidhlig",
|
||||
"path": "gd_GB/gd_GB"
|
||||
},
|
||||
"gl_ES": {
|
||||
"nativeName": "galego",
|
||||
"path": "gl/gl_ES"
|
||||
},
|
||||
"gu_IN": {
|
||||
"nativeName": "ગુજરાતી",
|
||||
"path": "gu_IN/gu_IN"
|
||||
},
|
||||
"he_IL": {
|
||||
"nativeName": "עברית",
|
||||
"path": "he_IL/he_IL"
|
||||
},
|
||||
"hi_IN": {
|
||||
"nativeName": "हिन्दी",
|
||||
"path": "hi_IN/hi_IN"
|
||||
},
|
||||
"hr_HR": {
|
||||
"nativeName": "hrvatski",
|
||||
"path": "hr_HR/hr_HR"
|
||||
},
|
||||
"hu_HU": {
|
||||
"nativeName": "magyar",
|
||||
"path": "hu_HU/hu_HU"
|
||||
},
|
||||
"id_ID": {
|
||||
"nativeName": "Indonesian",
|
||||
"path": "id/id_ID"
|
||||
},
|
||||
"is": {
|
||||
"nativeName": "íslenska",
|
||||
"path": "is/is"
|
||||
},
|
||||
"it_IT": {
|
||||
"nativeName": "italiano",
|
||||
"path": "it_IT/it_IT"
|
||||
},
|
||||
"kmr_Latn": {
|
||||
"nativeName": "Northern Kurdish",
|
||||
"path": "kmr_Latn/kmr_Latn"
|
||||
},
|
||||
"kn_IN": {
|
||||
"nativeName": "ಕನ್ನಡ",
|
||||
"path": "kn_IN/kn_IN"
|
||||
},
|
||||
"ko_KR": {
|
||||
"nativeName": "한국어",
|
||||
"path": "ko_KR/ko_KR"
|
||||
},
|
||||
"lo_LA": {
|
||||
"nativeName": "ລາວ",
|
||||
"path": "lo_LA/lo_LA"
|
||||
},
|
||||
"lt": {
|
||||
"nativeName": "lietuvių",
|
||||
"path": "lt_LT/lt"
|
||||
},
|
||||
"lv_LV": {
|
||||
"nativeName": "latviešu",
|
||||
"path": "lv_LV/lv_LV"
|
||||
},
|
||||
"mn_MN": {
|
||||
"nativeName": "монгол",
|
||||
"path": "mn_MN/mn_MN"
|
||||
},
|
||||
"mr_IN": {
|
||||
"nativeName": "मराठी",
|
||||
"path": "mr_IN/mr_IN"
|
||||
},
|
||||
"nb_NO": {
|
||||
"nativeName": "norsk bokmål",
|
||||
"path": "no/nb_NO"
|
||||
},
|
||||
"ne_NP": {
|
||||
"nativeName": "नेपाली",
|
||||
"path": "ne_NP/ne_NP"
|
||||
},
|
||||
"nl_NL": {
|
||||
"nativeName": "Nederlands",
|
||||
"path": "nl_NL/nl_NL"
|
||||
},
|
||||
"nn_NO": {
|
||||
"nativeName": "norsk nynorsk",
|
||||
"path": "no/nn_NO"
|
||||
},
|
||||
"oc_FR": {
|
||||
"nativeName": "occitan",
|
||||
"path": "oc_FR/oc_FR"
|
||||
},
|
||||
"or_IN": {
|
||||
"nativeName": "ଓଡ଼ିଆ",
|
||||
"path": "or_IN/or_IN"
|
||||
},
|
||||
"pa_IN": {
|
||||
"nativeName": "ਪੰਜਾਬੀ",
|
||||
"path": "pa_IN/pa_IN"
|
||||
},
|
||||
"pl_PL": {
|
||||
"nativeName": "polski",
|
||||
"path": "pl_PL/pl_PL"
|
||||
},
|
||||
"pt_BR": {
|
||||
"nativeName": "português (Brasil)",
|
||||
"path": "pt_BR/pt_BR"
|
||||
},
|
||||
"pt_PT": {
|
||||
"nativeName": "português europeu (Portugal)",
|
||||
"path": "pt_PT/pt_PT"
|
||||
},
|
||||
"ro_RO": {
|
||||
"nativeName": "română",
|
||||
"path": "ro/ro_RO"
|
||||
},
|
||||
"ru_RU": {
|
||||
"nativeName": "русский",
|
||||
"path": "ru_RU/ru_RU"
|
||||
},
|
||||
"sa_IN": {
|
||||
"nativeName": "संस्कृत भाषा",
|
||||
"path": "sa_IN/sa_IN"
|
||||
},
|
||||
"si_LK": {
|
||||
"nativeName": "සිංහල",
|
||||
"path": "si_LK/si_LK"
|
||||
},
|
||||
"sk_SK": {
|
||||
"nativeName": "slovenčina",
|
||||
"path": "sk_SK/sk_SK"
|
||||
},
|
||||
"sl_SI": {
|
||||
"nativeName": "slovenščina",
|
||||
"path": "sl_SI/sl_SI"
|
||||
},
|
||||
"sq_AL": {
|
||||
"nativeName": "shqip",
|
||||
"path": "sq_AL/sq_AL"
|
||||
},
|
||||
"sr": {
|
||||
"nativeName": "српски",
|
||||
"path": "sr/sr"
|
||||
},
|
||||
"sr-Latn": {
|
||||
"nativeName": "srpski",
|
||||
"path": "sr/sr-Latn"
|
||||
},
|
||||
"sv_FI": {
|
||||
"nativeName": "svenska (Finland)",
|
||||
"path": "sv_SE/sv_FI"
|
||||
},
|
||||
"sv_SE": {
|
||||
"nativeName": "svenska (Sverige)",
|
||||
"path": "sv_SE/sv_SE"
|
||||
},
|
||||
"sw_TZ": {
|
||||
"nativeName": "Kiswahili",
|
||||
"path": "sw_TZ/sw_TZ"
|
||||
},
|
||||
"ta_IN": {
|
||||
"nativeName": "தமிழ்",
|
||||
"path": "ta_IN/ta_IN"
|
||||
},
|
||||
"te_IN": {
|
||||
"nativeName": "తెలుగు",
|
||||
"path": "te_IN/te_IN"
|
||||
},
|
||||
"th_TH": {
|
||||
"nativeName": "ไทย",
|
||||
"path": "th_TH/th_TH"
|
||||
},
|
||||
"tr_TR": {
|
||||
"nativeName": "Türkçe",
|
||||
"path": "tr_TR/tr_TR"
|
||||
},
|
||||
"uk_UA": {
|
||||
"nativeName": "українська",
|
||||
"path": "uk_UA/uk_UA"
|
||||
},
|
||||
"vi_VN": {
|
||||
"nativeName": "Tiếng Việt",
|
||||
"path": "vi/vi_VN"
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,6 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtWebEngine
|
||||
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Enums 1.1
|
||||
@ -253,9 +252,15 @@ QtObject {
|
||||
isCallFullscreen = fullScreenItems
|
||||
.filter(o => o.item.objectName === "callViewLoader")
|
||||
.length
|
||||
isWebFullscreen = fullScreenItems
|
||||
.filter(o => o.item instanceof WebEngineView)
|
||||
.length
|
||||
isWebFullscreen = WITH_WEBENGINE ? fullScreenItems
|
||||
.filter(o => o.item && (
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.mediaPreview ||
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.videoPreview ||
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.map ||
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.general ||
|
||||
o.item.objectName === JamiQmlUtils.webEngineNames.emojiPicker
|
||||
))
|
||||
.length : 0
|
||||
}
|
||||
|
||||
// Listen for a hangup combined with a fullscreen call state and
|
||||
|
||||
@ -37,18 +37,24 @@ ApplicationWindow {
|
||||
property bool isRTL: UtilsAdapter.isRTL
|
||||
LayoutMirroring.enabled: isRTL
|
||||
LayoutMirroring.childrenInherit: isRTL
|
||||
property var raiseWhenCalled: AppSettingsManager.getValue(Settings.RaiseWhenCalled)
|
||||
|
||||
onActiveFocusItemChanged: {
|
||||
focusOverlay.margin = -5;
|
||||
if (activeFocusItem && ((activeFocusItem.focusReason === Qt.TabFocusReason) || (activeFocusItem.focusReason === Qt.BacktabFocusReason))) {
|
||||
if (activeFocusItem.focusOnChild) {
|
||||
focusOverlay.parent = activeFocusItem.parent;
|
||||
} else if (activeFocusItem.dontShowFocusState) {
|
||||
focusOverlay.parent = null;
|
||||
if (activeFocusItem) {
|
||||
const goodReasonToChangeFocus = activeFocusItem instanceof ItemDelegate || ((activeFocusItem.focusReason === Qt.TabFocusReason) || (activeFocusItem.focusReason === Qt.BacktabFocusReason));
|
||||
if (goodReasonToChangeFocus) {
|
||||
if (activeFocusItem.focusOnChild) {
|
||||
focusOverlay.parent = activeFocusItem.parent;
|
||||
} else if (activeFocusItem.dontShowFocusState) {
|
||||
focusOverlay.parent = null;
|
||||
} else {
|
||||
if (activeFocusItem.showFocusMargin)
|
||||
focusOverlay.margin = 0;
|
||||
focusOverlay.parent = activeFocusItem;
|
||||
}
|
||||
} else {
|
||||
if (activeFocusItem.showFocusMargin)
|
||||
focusOverlay.margin = 0;
|
||||
focusOverlay.parent = activeFocusItem;
|
||||
focusOverlay.parent = null;
|
||||
}
|
||||
} else {
|
||||
focusOverlay.parent = null;
|
||||
@ -291,6 +297,26 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: UtilsAdapter
|
||||
function onRaiseWhenCalledChanged() {
|
||||
raiseWhenCalled = AppSettingsManager.getValue(Settings.RaiseWhenCalled);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: CallAdapter
|
||||
|
||||
function onCallStatusChanged(index, accountId, convUid) {
|
||||
//If we are starting a call with raiseWhenCalled activated
|
||||
if (raiseWhenCalled && index === Call.Status.INCOMING_RINGING) {
|
||||
appWindow.raise();
|
||||
appWindow.requestActivate();
|
||||
layoutManager.restoreApp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: MainApplication
|
||||
|
||||
|
||||
@ -34,8 +34,12 @@ AppSettingsManager::AppSettingsManager(QObject* parent)
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(Settings::Key::COUNT__); ++i) {
|
||||
auto key = static_cast<Settings::Key>(i);
|
||||
if (!settings_->contains(Settings::toString(key)))
|
||||
setValue(key, Settings::defaultValue(key));
|
||||
auto strKey= Settings::toString(key);
|
||||
// If the setting is written in the settings file and is equal to the default value,
|
||||
// remove it from the settings file.
|
||||
// This allow us to change default values without risking to remove user settings
|
||||
if ((settings_->contains(strKey)) && (settings_->value(strKey) == Settings::defaultValue(key)))
|
||||
settings_->remove(strKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -63,6 +63,8 @@ extern const QString defaultDownloadPath;
|
||||
X(WindowState, QWindow::AutomaticVisibility) \
|
||||
X(EnableExperimentalSwarm, false) \
|
||||
X(LANG, "SYSTEM") \
|
||||
X(SpellLang, {}) \
|
||||
X(EnableSpellCheck, true) \
|
||||
X(PluginStoreEndpoint, "https://plugins.jami.net") \
|
||||
X(PositionShareDuration, 15) \
|
||||
X(PositionShareLimit, true) \
|
||||
@ -74,7 +76,8 @@ extern const QString defaultDownloadPath;
|
||||
X(PttKeys, 32) \
|
||||
X(UseFramelessWindow, USE_FRAMELESS_WINDOW_DEFAULT) \
|
||||
X(EnableCrashReporting, true) \
|
||||
X(EnableAutomaticCrashReporting, false)
|
||||
X(EnableAutomaticCrashReporting, false) \
|
||||
X(RaiseWhenCalled, false)
|
||||
#if APPSTORE
|
||||
#define KEYS COMMON_KEYS
|
||||
#else
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
#include "appversionmanager.h"
|
||||
|
||||
#include "lrcinstance.h"
|
||||
#include "version.h"
|
||||
#include "version_info.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
@ -73,7 +73,7 @@ struct AppVersionManager::Impl : public QObject
|
||||
Q_EMIT parent_.updateCheckReplyReceived(false);
|
||||
return;
|
||||
}
|
||||
auto currentVersion = QString(VERSION_STRING).toULongLong();
|
||||
auto currentVersion = BUILD_VERSION_STRING.toULongLong();
|
||||
auto latestVersion = latestVersionString.toULongLong();
|
||||
const QString channelStr = isBeta ? "beta" : "stable";
|
||||
const auto newVersionFound = latestVersion > currentVersion;
|
||||
|
||||
@ -345,9 +345,11 @@ AvAdapter::shareScreenArea(unsigned x, unsigned y, unsigned width, unsigned heig
|
||||
}
|
||||
|
||||
void
|
||||
AvAdapter::shareWindow(const QString& windowProcessId, const QString& windowId)
|
||||
AvAdapter::shareWindow(const QString& windowProcessId, const QString& windowId, const int fps)
|
||||
{
|
||||
auto resource = lrcInstance_->getCurrentCallModel()->getDisplay(windowProcessId, windowId);
|
||||
auto resource = lrcInstance_->getCurrentCallModel()->getDisplay(windowProcessId,
|
||||
windowId,
|
||||
fps);
|
||||
auto callId = lrcInstance_->getCurrentCallId();
|
||||
|
||||
muteCamera_ = !isCapturing();
|
||||
@ -356,7 +358,10 @@ AvAdapter::shareWindow(const QString& windowProcessId, const QString& windowId)
|
||||
}
|
||||
|
||||
QString
|
||||
AvAdapter::getSharingResource(int screenId, const QString& windowProcessId, const QString& windowId)
|
||||
AvAdapter::getSharingResource(int screenId,
|
||||
const QString& windowProcessId,
|
||||
const QString& windowId,
|
||||
const int fps)
|
||||
{
|
||||
if (screenId == -1) {
|
||||
const auto arrangementRect = getAllScreensBoundingRect();
|
||||
@ -387,7 +392,7 @@ AvAdapter::getSharingResource(int screenId, const QString& windowProcessId, cons
|
||||
rect.height()
|
||||
* screen->devicePixelRatio());
|
||||
} else if (!windowId.isEmpty()) {
|
||||
return lrcInstance_->getCurrentCallModel()->getDisplay(windowProcessId, windowId);
|
||||
return lrcInstance_->getCurrentCallModel()->getDisplay(windowProcessId, windowId, fps);
|
||||
}
|
||||
|
||||
return "";
|
||||
|
||||
@ -96,7 +96,9 @@ protected:
|
||||
Q_INVOKABLE void shareScreenArea(unsigned x, unsigned y, unsigned width, unsigned height);
|
||||
|
||||
// Select window to display (all platforms except Wayland).
|
||||
Q_INVOKABLE void shareWindow(const QString& windowProcessId, const QString& windowId);
|
||||
Q_INVOKABLE void shareWindow(const QString& windowProcessId,
|
||||
const QString& windowId,
|
||||
const int fps = -1);
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// Share a window on Wayland.
|
||||
@ -110,7 +112,8 @@ protected:
|
||||
// Returns the screensharing resource
|
||||
Q_INVOKABLE QString getSharingResource(int screenId = -2,
|
||||
const QString& windowProcessId = "",
|
||||
const QString& key = "");
|
||||
const QString& key = "",
|
||||
const int fps = -1);
|
||||
|
||||
Q_INVOKABLE void getListWindows();
|
||||
|
||||
|
||||
@ -249,7 +249,7 @@ CallAdapter::onCallEnded(const QString& callId)
|
||||
}
|
||||
|
||||
void
|
||||
CallAdapter::onCallStatusChanged(const QString& callId, int code)
|
||||
CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId, int code)
|
||||
{
|
||||
Q_UNUSED(code)
|
||||
|
||||
@ -468,11 +468,14 @@ CallAdapter::onShowIncomingCallView(const QString& accountId, const QString& con
|
||||
showNotification(accountId, convInfo.uid);
|
||||
return;
|
||||
}
|
||||
if (!accountProperties.denySecondCall) {
|
||||
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
||||
}
|
||||
} else {
|
||||
// finally, in this case, the conversation isn't selected yet
|
||||
// and there are no other special conditions, so just select the conversation
|
||||
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
||||
}
|
||||
|
||||
// finally, in this case, the conversation isn't selected yet
|
||||
// and there are no other special conditions, so just select the conversation
|
||||
lrcInstance_->selectConversation(convInfo.uid, accountId);
|
||||
}
|
||||
|
||||
void
|
||||
@ -563,7 +566,7 @@ CallAdapter::connectCallModel(const QString& accountId)
|
||||
connect(accInfo.callModel.get(),
|
||||
&CallModel::callStatusChanged,
|
||||
this,
|
||||
QOverload<const QString&, int>::of(&CallAdapter::onCallStatusChanged),
|
||||
QOverload<const QString&, const QString&, int>::of(&CallAdapter::onCallStatusChanged),
|
||||
Qt::UniqueConnection);
|
||||
|
||||
connect(accInfo.callModel.get(),
|
||||
|
||||
@ -118,7 +118,7 @@ public Q_SLOTS:
|
||||
void onShowCallView(const QString& accountId, const QString& convUid);
|
||||
void onAccountChanged();
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId);
|
||||
void onCallStatusChanged(const QString& callId, int code);
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId, int code);
|
||||
void onCallAddedToConference(const QString& callId, const QString& conversationId, const QString& confId);
|
||||
void onCallStarted(const QString& callId);
|
||||
void onCallEnded(const QString& callId);
|
||||
|
||||
@ -135,7 +135,7 @@ PendingConferenceesListModel::connectSignals()
|
||||
callsStatusChanged_ = connect(currentCallModel,
|
||||
&CallModel::callStatusChanged,
|
||||
this,
|
||||
[this](const QString&, int) {
|
||||
[this](const QString&, const QString&, int) {
|
||||
Q_EMIT dataChanged(index(0, 0),
|
||||
index(rowCount() - 1),
|
||||
{Role::CallStatus});
|
||||
@ -401,6 +401,16 @@ CallOverlayModel::eventFilter(QObject* object, QEvent* event)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tab or BackTab key events should trigger a signal that we can use to
|
||||
// prevent the overlay from fading and to allow the user to navigate
|
||||
// through the controls.
|
||||
if (event->type() == QEvent::KeyPress && (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Tab)
|
||||
|| (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Backtab)) {
|
||||
Q_EMIT focusKeyPressed();
|
||||
// Don't absorb the event so that the focus can be changed
|
||||
// to the next or previous control.
|
||||
return false;
|
||||
}
|
||||
#ifndef HAVE_GLOBAL_PTT
|
||||
else if (event->type() == QEvent::KeyPress && listener_->getPttState()) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
|
||||
@ -140,6 +140,7 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void mouseMoved(QQuickItem* item);
|
||||
void focusKeyPressed();
|
||||
void pttKeyPressed();
|
||||
void pttKeyReleased();
|
||||
|
||||
|
||||
@ -78,8 +78,10 @@ Popup {
|
||||
contentItem: ColumnLayout {
|
||||
id: contentLayout
|
||||
|
||||
JamiPushButton {
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: closeButton
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: JamiStrings.close
|
||||
|
||||
visible: closeButtonVisible
|
||||
|
||||
@ -117,7 +119,7 @@ Popup {
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredHeight: Math.min(contentHeight, root.height)
|
||||
Layout.preferredWidth: contentItem.childrenRect.width
|
||||
Layout.preferredWidth: contentItem.childrenRect.width + ScrollBar.vertical.width
|
||||
Layout.leftMargin: popupMargins
|
||||
Layout.rightMargin: popupMargins
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
@ -23,12 +23,12 @@ import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
SBSMessageBase {
|
||||
id: root
|
||||
id: rootDelegate
|
||||
|
||||
property var confId: ConfId
|
||||
property var currentCallId: CurrentCall.id
|
||||
component JoinCallButton: MaterialButton {
|
||||
visible: root.isActive && root.currentCallId !== root.confId
|
||||
visible: rootDelegate.isActive && rootDelegate.currentCallId !== rootDelegate.confId
|
||||
toolTipText: JamiStrings.joinCall
|
||||
color: JamiTheme.blackColor
|
||||
background.opacity: hovered ? 0.2 : 0.1
|
||||
@ -40,6 +40,20 @@ SBSMessageBase {
|
||||
textRightPadding: 9
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: {
|
||||
let name = isOutgoing ? JamiStrings.inReplyToYou : UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return name + ": " + callLabel.text + " " + formattedDay;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (bubble.isEdited)
|
||||
status += JamiStrings.edited + " ";
|
||||
return status + (readers.length > 0 ? JamiStrings.readBy + " " + readers.map(function (uri) {
|
||||
return UtilsAdapter.getBestNameForUri(CurrentAccount.id, uri);
|
||||
}).join(", ") : "");
|
||||
}
|
||||
|
||||
property bool isRemoteImage
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
@ -48,17 +62,17 @@ SBSMessageBase {
|
||||
formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
|
||||
|
||||
bubble.border.color: CurrentConversation.color
|
||||
bubble.border.width: root.isActive ? 1.5 : 0
|
||||
bubble.border.width: rootDelegate.isActive ? 1.5 : 0
|
||||
bubble.color: JamiTheme.messageInBgColor
|
||||
bubble.opacity: 1
|
||||
|
||||
Connections {
|
||||
target: CurrentConversation
|
||||
enabled: root.isActive
|
||||
enabled: rootDelegate.isActive
|
||||
|
||||
function onActiveCallsChanged() {
|
||||
root.isActive = LRCInstance.indexOfActiveCall(root.confId, ActionUri, DeviceId) !== -1;
|
||||
if (root.isActive) {
|
||||
rootDelegate.isActive = LRCInstance.indexOfActiveCall(rootDelegate.confId, ActionUri, DeviceId) !== -1;
|
||||
if (rootDelegate.isActive) {
|
||||
bubble.mask.border.color = CurrentConversation.color;
|
||||
bubble.mask.border.width = 1.5;
|
||||
bubble.mask.z = -2;
|
||||
@ -66,8 +80,8 @@ SBSMessageBase {
|
||||
}
|
||||
}
|
||||
|
||||
property bool isActive: LRCInstance.indexOfActiveCall(root.confId, ActionUri, DeviceId) !== -1
|
||||
visible: isActive || root.confId === "" || Duration > 0
|
||||
property bool isActive: LRCInstance.indexOfActiveCall(rootDelegate.confId, ActionUri, DeviceId) !== -1
|
||||
visible: isActive || rootDelegate.confId === "" || Duration > 0
|
||||
|
||||
property var baseColor: JamiTheme.messageInBgColor
|
||||
|
||||
@ -76,7 +90,7 @@ SBSMessageBase {
|
||||
id: msg
|
||||
anchors.right: isOutgoing ? parent.right : undefined
|
||||
spacing: 10
|
||||
visible: root.visible
|
||||
visible: rootDelegate.visible
|
||||
|
||||
Image {
|
||||
id: statusIcon
|
||||
@ -84,10 +98,10 @@ SBSMessageBase {
|
||||
width: 10
|
||||
height: 10
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
visible: !root.isActive
|
||||
visible: !rootDelegate.isActive
|
||||
|
||||
source: {
|
||||
if (root.isOutgoing) {
|
||||
if (rootDelegate.isOutgoing) {
|
||||
if (Duration > 0)
|
||||
return "qrc:/icons/outgoing-call.svg";
|
||||
else
|
||||
@ -104,12 +118,11 @@ SBSMessageBase {
|
||||
effect: ColorOverlay {
|
||||
color: {
|
||||
if (Duration > 0)
|
||||
return UtilsAdapter.luma(root.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
|
||||
return JamiTheme.redColor
|
||||
return UtilsAdapter.luma(rootDelegate.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
|
||||
return JamiTheme.redColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
@ -120,11 +133,11 @@ SBSMessageBase {
|
||||
bottomPadding: 8
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: root.isActive && root.currentCallId !== root.confId ? 0 : root.timeWidth + 16
|
||||
Layout.leftMargin: root.isActive ? 10 : -5 /* spacing is 10 and we want 5px with icon */
|
||||
Layout.rightMargin: rootDelegate.isActive && rootDelegate.currentCallId !== rootDelegate.confId ? 0 : rootDelegate.timeWidth + 16
|
||||
Layout.leftMargin: rootDelegate.isActive ? 10 : -5 /* spacing is 10 and we want 5px with icon */
|
||||
|
||||
text: {
|
||||
if (root.isActive)
|
||||
if (rootDelegate.isActive)
|
||||
return JamiStrings.startedACall;
|
||||
return Body;
|
||||
}
|
||||
@ -136,7 +149,7 @@ SBSMessageBase {
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: Text.MarkdownText
|
||||
|
||||
color: UtilsAdapter.luma(root.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
|
||||
color: UtilsAdapter.luma(rootDelegate.baseColor) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark
|
||||
}
|
||||
|
||||
JoinCallButton {
|
||||
@ -146,7 +159,7 @@ SBSMessageBase {
|
||||
Layout.bottomMargin: 4
|
||||
|
||||
text: JamiStrings.joinWithAudio
|
||||
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId, true)
|
||||
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, rootDelegate.confId, true)
|
||||
}
|
||||
|
||||
JoinCallButton {
|
||||
@ -156,20 +169,20 @@ SBSMessageBase {
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
|
||||
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, root.confId)
|
||||
onClicked: MessagesAdapter.joinCall(ActionUri, DeviceId, rootDelegate.confId)
|
||||
Layout.rightMargin: 4
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
opacity: 0
|
||||
Behavior on opacity {
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
bubble.timestampItem.visible = !root.isActive || root.currentCallId === root.confId;
|
||||
bubble.timestampItem.visible = !rootDelegate.isActive || rootDelegate.currentCallId === rootDelegate.confId;
|
||||
opacity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,8 +21,8 @@ import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
Column {
|
||||
id: root
|
||||
Control {
|
||||
id: rootDelegate
|
||||
|
||||
property bool showTime: false
|
||||
property bool showDay: false
|
||||
@ -36,21 +36,41 @@ Column {
|
||||
height: timestampItem.height + textLabel.height
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Accessible.name: {
|
||||
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return name + ": " + Body + " " + formattedTime + " " + formattedDay;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (IsLastSent)
|
||||
status += JamiStrings.sent + " ";
|
||||
return status;
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: focusIndicator
|
||||
visible: rootDelegate.activeFocus
|
||||
border.color: JamiTheme.tintedBlue
|
||||
border.width: 2
|
||||
radius: 10
|
||||
color: "transparent"
|
||||
z: 1
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: timestampItem.height + textLabel.height
|
||||
|
||||
TimestampInfo {
|
||||
id: timestampItem
|
||||
|
||||
showDay: root.showDay
|
||||
showTime: root.showTime
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedDay
|
||||
showDay: rootDelegate.showDay
|
||||
showTime: rootDelegate.showTime
|
||||
formattedTime: rootDelegate.formattedTime
|
||||
formattedDay: rootDelegate.formattedDay
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
|
||||
}
|
||||
|
||||
Label {
|
||||
@ -67,7 +87,7 @@ Column {
|
||||
}
|
||||
}
|
||||
opacity: 0
|
||||
Behavior on opacity {
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
|
||||
@ -24,20 +24,32 @@ import net.jami.Constants 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
id: rootDelegate
|
||||
|
||||
property var mediaInfo
|
||||
property bool showTime
|
||||
property bool showDay
|
||||
property int timestamp: Timestamp
|
||||
property string formattedTime: MessagesAdapter.getFormattedTime(root.timestamp)
|
||||
property string formattedDay: MessagesAdapter.getFormattedDay(root.timestamp)
|
||||
property string formattedTime: MessagesAdapter.getFormattedTime(rootDelegate.timestamp)
|
||||
property string formattedDay: MessagesAdapter.getFormattedDay(rootDelegate.timestamp)
|
||||
|
||||
property int seq: MsgSeq.single
|
||||
property string author: Author
|
||||
property string body: Body
|
||||
property var tid: TID
|
||||
property int transferStatus: TransferStatus
|
||||
|
||||
Accessible.name: {
|
||||
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return JamiStrings.dataTransfer + name + ": " + JamiStrings.status + TransferStatus + Body + " " + formattedTime + " " + formattedDay;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (IsLastSent)
|
||||
status += JamiStrings.sent + " ";
|
||||
return status;
|
||||
}
|
||||
|
||||
onTidChanged: {
|
||||
if (tid === "") {
|
||||
sourceComponent = deletedMsgComp;
|
||||
@ -48,7 +60,7 @@ Loader {
|
||||
sourceComponent = deletedMsgComp;
|
||||
return;
|
||||
} else if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) {
|
||||
mediaInfo = MessagesAdapter.getMediaInfo(root.body);
|
||||
mediaInfo = MessagesAdapter.getMediaInfo(rootDelegate.body);
|
||||
if (Object.keys(mediaInfo).length !== 0 && WITH_WEBENGINE) {
|
||||
sourceComponent = localMediaMsgComp;
|
||||
return;
|
||||
@ -74,13 +86,13 @@ Loader {
|
||||
id: deletedItem
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
showTime: root.showTime
|
||||
seq: root.seq
|
||||
showTime: rootDelegate.showTime
|
||||
seq: rootDelegate.seq
|
||||
author: Author
|
||||
readers: Readers
|
||||
timestamp: root.timestamp
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedTime
|
||||
timestamp: rootDelegate.timestamp
|
||||
formattedTime: rootDelegate.formattedTime
|
||||
formattedDay: rootDelegate.formattedTime
|
||||
extraHeight: 0
|
||||
textContentWidth: textEditId.width
|
||||
textContentHeight: textEditId.height
|
||||
@ -122,34 +134,34 @@ Loader {
|
||||
id: dataTransferItem
|
||||
|
||||
transferId: Id
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus)
|
||||
property bool canOpen: root.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing
|
||||
property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus)
|
||||
property bool canOpen: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing
|
||||
property real maxMsgWidth: rootDelegate.width - senderMargin - 2 * hPadding - avatarBlockWidth - buttonsLoader.width - 24 - 6 - 24
|
||||
|
||||
// Timer to update the translation bar
|
||||
Loader {
|
||||
id: timerLoader
|
||||
active: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
active: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
sourceComponent: Timer {
|
||||
interval: 1000 // Update every second
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
transferStats = MessagesAdapter.getTransferStats(transferId, root.transferStatus);
|
||||
transferStats = MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
showTime: root.showTime
|
||||
seq: root.seq
|
||||
showTime: rootDelegate.showTime
|
||||
seq: rootDelegate.seq
|
||||
author: Author
|
||||
location: Body
|
||||
transferName: TransferName
|
||||
readers: Readers
|
||||
timestamp: root.timestamp
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedTime
|
||||
timestamp: rootDelegate.timestamp
|
||||
formattedTime: rootDelegate.formattedTime
|
||||
formattedDay: rootDelegate.formattedTime
|
||||
extraHeight: progressBar.visible ? 25 : 0
|
||||
|
||||
innerContent.children: [
|
||||
@ -178,7 +190,7 @@ Loader {
|
||||
Layout.margins: 8
|
||||
|
||||
sourceComponent: {
|
||||
switch (root.transferStatus) {
|
||||
switch (rootDelegate.transferStatus) {
|
||||
case Interaction.TransferStatus.TRANSFER_CREATED:
|
||||
case Interaction.TransferStatus.TRANSFER_FINISHED:
|
||||
iconSource = JamiResources.link_black_24dp_svg;
|
||||
@ -225,7 +237,7 @@ Loader {
|
||||
normalColor: JamiTheme.chatviewBgColor
|
||||
imageColor: JamiTheme.chatviewButtonColor
|
||||
onClicked: {
|
||||
if (root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
|
||||
if (rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
|
||||
MessagesAdapter.cancelFile(transferId);
|
||||
} else {
|
||||
buttonsLoader.iconSource = JamiResources.connecting_black_24dp_svg;
|
||||
@ -287,7 +299,7 @@ Loader {
|
||||
ProgressBar {
|
||||
id: progressBar
|
||||
|
||||
visible: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
visible: rootDelegate.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
height: visible * implicitHeight
|
||||
value: transferStats.progress / transferStats.totalSize
|
||||
width: transferItem.width
|
||||
@ -305,15 +317,15 @@ Loader {
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
transferId: Id
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus)
|
||||
showTime: root.showTime
|
||||
seq: root.seq
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, rootDelegate.transferStatus)
|
||||
showTime: rootDelegate.showTime
|
||||
seq: rootDelegate.seq
|
||||
author: Author
|
||||
location: Body
|
||||
transferName: TransferName
|
||||
readers: Readers
|
||||
formattedTime: MessagesAdapter.getFormattedTime(root.timestamp)
|
||||
formattedDay: MessagesAdapter.getFormattedDay(root.timestamp)
|
||||
formattedTime: MessagesAdapter.getFormattedTime(rootDelegate.timestamp)
|
||||
formattedDay: MessagesAdapter.getFormattedDay(rootDelegate.timestamp)
|
||||
|
||||
property real contentWidth
|
||||
|
||||
|
||||
370
src/app/commoncomponents/DictionaryInstallView.qml
Normal file
370
src/app/commoncomponents/DictionaryInstallView.qml
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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 QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import "../mainview/components"
|
||||
import "../settingsview/components"
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
// Search bar for filtering dictionaries
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
property int checkBoxWidth: 24
|
||||
|
||||
Component.onCompleted: Qt.callLater(dictionarySearchBar.setTextAreaFocus)
|
||||
|
||||
RowLayout {
|
||||
id: headerLayout
|
||||
width: parent.width
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
|
||||
// Header title
|
||||
Searchbar {
|
||||
id: dictionarySearchBar
|
||||
|
||||
focus: true
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 55
|
||||
|
||||
placeHolderText: JamiStrings.searchTextLanguages
|
||||
Accessible.name: JamiStrings.searchTextLanguages
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.description: JamiStrings.searchAvailableTextLanguages
|
||||
|
||||
onSearchBarTextChanged: function (text) {
|
||||
dictionaryProxyModel.combinedFilterPattern = text;
|
||||
dictionaryProxyModel.invalidate();
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: JamiStrings.showInstalledDictionaries
|
||||
color: JamiTheme.faddedLastInteractionFontColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
|
||||
Layout.rightMargin: 0
|
||||
Layout.preferredHeight: 16
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
// Checkbox to filter installed dictionaries
|
||||
CheckBox {
|
||||
id: showInstalledOnlyCheckbox
|
||||
Accessible.name: JamiStrings.showInstalledDictionaries
|
||||
Accessible.role: Accessible.CheckBox
|
||||
Accessible.description: JamiStrings.showInstalledDictionariesDescription
|
||||
checked: false
|
||||
indicator: Image {
|
||||
anchors.centerIn: parent
|
||||
layer {
|
||||
enabled: true
|
||||
effect: ColorOverlay {
|
||||
color: JamiTheme.tintedBlue
|
||||
}
|
||||
mipmap: false
|
||||
smooth: true
|
||||
}
|
||||
width: checkBoxWidth
|
||||
height: checkBoxWidth
|
||||
source: showInstalledOnlyCheckbox.checked ? JamiResources.check_box_24dp_svg : JamiResources.check_box_outline_blank_24dp_svg
|
||||
}
|
||||
|
||||
Layout.preferredWidth: 55
|
||||
Layout.preferredHeight: 55
|
||||
Layout.rightMargin: 0
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to listen for download failure and pop a simple dialog to inform the user
|
||||
Connections {
|
||||
target: SpellCheckAdapter
|
||||
function onDownloadFailed(locale) {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
"title": JamiStrings.error,
|
||||
"infoText": JamiStrings.spellCheckDownloadFailed.arg(locale),
|
||||
"buttonTitles": [JamiStrings.optionOk],
|
||||
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue],
|
||||
"buttonRoles": [DialogButtonBox.AcceptRole]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
JamiListView {
|
||||
id: spellCheckDictionaryListView
|
||||
|
||||
width: parent.width
|
||||
Layout.fillHeight: true
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
id: dictionaryProxyModel
|
||||
sourceModel: SpellCheckAdapter.getDictionaryListModel()
|
||||
|
||||
property string combinedFilterPattern
|
||||
|
||||
filters: AllOf {
|
||||
AnyOf {
|
||||
// Filter by dictionary name
|
||||
RegExpFilter {
|
||||
roleName: "Locale"
|
||||
pattern: dictionaryProxyModel.combinedFilterPattern
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
// Filter by native name
|
||||
RegExpFilter {
|
||||
roleName: "NativeName"
|
||||
pattern: dictionaryProxyModel.combinedFilterPattern
|
||||
caseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
}
|
||||
ValueFilter {
|
||||
roleName: "Installed"
|
||||
value: true
|
||||
enabled: showInstalledOnlyCheckbox.checked
|
||||
}
|
||||
}
|
||||
|
||||
sorters: [
|
||||
// Sort by locale alphabetically
|
||||
RoleSorter {
|
||||
roleName: "Locale"
|
||||
sortOrder: Qt.AscendingOrder
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
readonly property int itemMargins: 20
|
||||
topMargin: itemMargins / 2
|
||||
bottomMargin: itemMargins / 2
|
||||
|
||||
spacing: 8
|
||||
clip: true
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: dictionaryDelegate
|
||||
width: spellCheckDictionaryListView.width
|
||||
height: Math.max(JamiTheme.preferredFieldHeight, contentLayout.implicitHeight + 32)
|
||||
|
||||
background: Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width - spellCheckDictionaryListView.itemMargins
|
||||
height: parent.height
|
||||
color: JamiTheme.backgroundColor
|
||||
radius: JamiTheme.primaryRadius
|
||||
border.color: "transparent"
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 16
|
||||
|
||||
// Dictionary info
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: 16
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
id: dictionaryName
|
||||
Layout.fillWidth: true
|
||||
text: model.NativeName || ""
|
||||
color: JamiTheme.textColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
id: dictionaryLocale
|
||||
Layout.fillWidth: true
|
||||
text: model.Locale || ""
|
||||
color: JamiTheme.faddedLastInteractionFontColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize - 2
|
||||
elide: Text.ElideRight
|
||||
visible: text !== ""
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Installation status and action
|
||||
Item {
|
||||
Layout.preferredWidth: 100
|
||||
Layout.preferredHeight: 32
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rightMargin: 16
|
||||
|
||||
// Install button for available dictionaries
|
||||
MaterialButton {
|
||||
id: installButton
|
||||
anchors.centerIn: parent
|
||||
width: 100
|
||||
height: 32
|
||||
Accessible.name: dictionaryName.text + " " + JamiStrings.install
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
text: JamiStrings.install
|
||||
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize - 1
|
||||
font.weight: Font.Medium
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
KeyNavigation.tab: {
|
||||
try {
|
||||
if (model.index < dictionaryProxyModel.count - 1) {
|
||||
var nextItem = spellCheckDictionaryListView.itemAtIndex(model.index + 1);
|
||||
if (nextItem) {
|
||||
var nextButton = nextItem.findChild("installButton") || nextItem.findChild("uninstallButton");
|
||||
return nextButton || null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug("KeyNavigation error handled:", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
spellCheckDictionaryListView.positionViewAtIndex(model.index, ListView.Contain);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (model.Locale) {
|
||||
SpellCheckAdapter.installDictionary(model.Locale);
|
||||
}
|
||||
}
|
||||
|
||||
visible: !model.Downloading && !model.Installed && model.Locale !== undefined && model.Locale !== ""
|
||||
}
|
||||
|
||||
// Uninstall button for installed dictionaries (not system dictionaries)
|
||||
MaterialButton {
|
||||
id: uninstallButton
|
||||
anchors.centerIn: parent
|
||||
width: 100
|
||||
height: 32
|
||||
|
||||
Accessible.name: dictionaryName.text + " " + JamiStrings.uninstall
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
text: JamiStrings.uninstall
|
||||
color: "#ff6666"
|
||||
hoveredColor: "#ff9999"
|
||||
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize - 1
|
||||
font.weight: Font.Medium
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
KeyNavigation.tab: {
|
||||
try {
|
||||
if (model.index < dictionaryProxyModel.count - 1) {
|
||||
var nextItem = spellCheckDictionaryListView.itemAtIndex(model.index + 1);
|
||||
if (nextItem) {
|
||||
var nextButton = nextItem.findChild("installButton") || nextItem.findChild("uninstallButton");
|
||||
return nextButton || null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug("KeyNavigation error handled:", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
spellCheckDictionaryListView.positionViewAtIndex(model.index, ListView.Contain);
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (model.Locale) {
|
||||
SpellCheckAdapter.uninstallDictionary(model.Locale);
|
||||
}
|
||||
}
|
||||
|
||||
visible: !model.Downloading && model.Installed && !model.IsSystem && model.Locale !== undefined && model.Locale !== ""
|
||||
}
|
||||
|
||||
// System dictionary indicator
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: JamiStrings.systemDictionary
|
||||
color: JamiTheme.faddedLastInteractionFontColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize - 2
|
||||
visible: model.IsSystem
|
||||
}
|
||||
|
||||
// Downloading status indicator
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: model.Downloading
|
||||
running: model.Downloading
|
||||
width: 24
|
||||
height: 24
|
||||
|
||||
// Use a custom animation for better UX
|
||||
Behavior on running {
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Empty state for when no dictionaries are found
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
visible: dictionaryProxyModel.count === 0
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 16
|
||||
width: parent.width * 0.8
|
||||
|
||||
// Big books emoji
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "📚"
|
||||
font.pixelSize: 48
|
||||
opacity: 0.3
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: dictionarySearchBar.textContent.length > 0 ? JamiStrings.noDictionariesFoundFor.arg(dictionarySearchBar.textContent) : JamiStrings.noDictionariesAvailable
|
||||
color: JamiTheme.faddedLastInteractionFontColor
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@ import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
Column {
|
||||
id: root
|
||||
id: rootDelegate
|
||||
|
||||
property bool showTime: false
|
||||
property bool showDay: false
|
||||
@ -34,6 +34,18 @@ Column {
|
||||
spacing: 2
|
||||
topPadding: 12
|
||||
bottomPadding: 12
|
||||
|
||||
Accessible.name: {
|
||||
let name = UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return name + ": " + Body + " " + formattedTime + " " + formattedDay;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (IsLastSent)
|
||||
status += JamiStrings.sent + " ";
|
||||
return status;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
width: parent.width
|
||||
@ -42,10 +54,10 @@ Column {
|
||||
TimestampInfo {
|
||||
id: timestampItem
|
||||
|
||||
showDay: root.showDay
|
||||
showTime: root.showTime
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedDay
|
||||
showDay: rootDelegate.showDay
|
||||
showTime: rootDelegate.showTime
|
||||
formattedTime: rootDelegate.formattedTime
|
||||
formattedDay: rootDelegate.formattedDay
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
@ -60,7 +72,7 @@ Column {
|
||||
}
|
||||
|
||||
opacity: 0
|
||||
Behavior on opacity {
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
}
|
||||
|
||||
@ -28,6 +28,10 @@ Control {
|
||||
property string title: ""
|
||||
property string description: ""
|
||||
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: title
|
||||
Accessible.description: description
|
||||
|
||||
width: 190
|
||||
height: infos.implicitHeight
|
||||
|
||||
|
||||
@ -15,8 +15,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import QtQuick
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Enums 1.1
|
||||
import net.jami.Models 1.1
|
||||
import "contextmenu"
|
||||
import "../mainview"
|
||||
import "../mainview/components"
|
||||
|
||||
ContextMenuAutoLoader {
|
||||
id: root
|
||||
@ -27,21 +32,19 @@ ContextMenuAutoLoader {
|
||||
property var selectionEnd
|
||||
property bool customizePaste: false
|
||||
property bool selectOnly: false
|
||||
property bool spellCheckEnabled: false
|
||||
property var suggestionList
|
||||
property var menuItemsLength
|
||||
property var language
|
||||
|
||||
signal contextMenuRequirePaste
|
||||
|
||||
property list<GeneralMenuItem> menuItems: [
|
||||
GeneralMenuItem {
|
||||
id: copy
|
||||
SpellLanguageContextMenu {
|
||||
id: spellLanguageContextMenu
|
||||
active: spellCheckEnabled
|
||||
}
|
||||
|
||||
canTrigger: true
|
||||
isActif: lineEditObj.selectedText.length
|
||||
itemName: JamiStrings.copy
|
||||
hasIcon: false
|
||||
onClicked: {
|
||||
lineEditObj.copy();
|
||||
}
|
||||
},
|
||||
property list<GeneralMenuItem> menuItems: [
|
||||
GeneralMenuItem {
|
||||
id: cut
|
||||
|
||||
@ -49,9 +52,16 @@ ContextMenuAutoLoader {
|
||||
isActif: lineEditObj.selectedText.length && !selectOnly
|
||||
itemName: JamiStrings.cut
|
||||
hasIcon: false
|
||||
onClicked: {
|
||||
lineEditObj.cut();
|
||||
}
|
||||
onClicked: lineEditObj.cut()
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: copy
|
||||
|
||||
canTrigger: true
|
||||
isActif: lineEditObj.selectedText.length
|
||||
itemName: JamiStrings.copy
|
||||
hasIcon: false
|
||||
onClicked: lineEditObj.copy()
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: paste
|
||||
@ -65,9 +75,77 @@ ContextMenuAutoLoader {
|
||||
else
|
||||
lineEditObj.paste();
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: textLanguage
|
||||
canTrigger: spellCheckEnabled && SpellCheckAdapter.installedDictionaryCount > 0
|
||||
itemName: JamiStrings.textLanguage
|
||||
hasIcon: false
|
||||
onClicked: {
|
||||
spellLanguageContextMenu.openMenu();
|
||||
}
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: manageLanguages
|
||||
itemName: JamiStrings.manageDictionaries
|
||||
canTrigger: spellCheckEnabled
|
||||
hasIcon: false
|
||||
onClicked: {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/ManageDictionariesDialog.qml");
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
ListView {
|
||||
model: ListModel {
|
||||
id: suggestionListModel
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
model: suggestionListModel
|
||||
delegate: GeneralMenuItem {
|
||||
id: suggestion
|
||||
|
||||
canTrigger: true
|
||||
isActif: true
|
||||
itemName: model.name
|
||||
bold: true
|
||||
hasIcon: false
|
||||
onClicked: {
|
||||
replaceWord(model.name);
|
||||
}
|
||||
}
|
||||
|
||||
onObjectAdded: {
|
||||
menuItems.push(object);
|
||||
}
|
||||
|
||||
onObjectRemoved: {
|
||||
menuItems.splice(menuItemsLength, suggestionList.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeItems() {
|
||||
suggestionListModel.clear();
|
||||
suggestionList.length = 0;
|
||||
}
|
||||
|
||||
function addMenuItem(wordList) {
|
||||
menuItemsLength = menuItems.length; // Keep initial number of items for easier removal
|
||||
suggestionList = wordList;
|
||||
for (var i = 0; i < suggestionList.length; ++i) {
|
||||
suggestionListModel.append({
|
||||
"name": suggestionList[i]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function replaceWord(word) {
|
||||
lineEditObj.remove(selectionStart, selectionEnd);
|
||||
lineEditObj.insert(lineEditObj.cursorPosition, word);
|
||||
}
|
||||
|
||||
function openMenuAt(mouseEvent) {
|
||||
if (lineEditObj.selectedText.length === 0 && selectOnly)
|
||||
return;
|
||||
@ -85,6 +163,12 @@ ContextMenuAutoLoader {
|
||||
function onOpened() {
|
||||
lineEditObj.select(selectionStart, selectionEnd);
|
||||
}
|
||||
function onClosed() {
|
||||
if (!suggestionList || suggestionList.length === 0) {
|
||||
return;
|
||||
}
|
||||
removeItems();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: menuItemsToLoad = menuItems
|
||||
|
||||
36
src/app/commoncomponents/ManageDictionariesDialog.qml
Normal file
36
src/app/commoncomponents/ManageDictionariesDialog.qml
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import "../commoncomponents/contextmenu"
|
||||
|
||||
BaseModalDialog {
|
||||
id: root
|
||||
objectName: "manageDictionariesDialog"
|
||||
|
||||
title: JamiStrings.manageDictionaries
|
||||
|
||||
popupContent: DictionaryInstallView {
|
||||
Accessible.name: JamiStrings.manageDictionaries
|
||||
Accessible.role: Accessible.PopupMenu
|
||||
width: 400
|
||||
height: 500
|
||||
}
|
||||
}
|
||||
@ -20,14 +20,11 @@ import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.platform
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import net.jami.Models 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
import "../mainview/components"
|
||||
|
||||
|
||||
BaseModalDialog {
|
||||
id: root
|
||||
|
||||
@ -36,26 +33,27 @@ BaseModalDialog {
|
||||
property real buttonSize: 36
|
||||
property real imageSize: 25
|
||||
|
||||
|
||||
signal focusOnPreviousItem
|
||||
signal focusOnNextItem
|
||||
signal imageValidated
|
||||
signal imageTemporaryValidated
|
||||
signal imageRemoved
|
||||
signal imageTemporaryRemoved
|
||||
|
||||
function startBooth() {
|
||||
recordBox.openRecorder(true)
|
||||
recordBox.openRecorder(true);
|
||||
}
|
||||
|
||||
function stopBooth(){
|
||||
recordBox.closeRecorder()
|
||||
function stopBooth() {
|
||||
recordBox.closeRecorder();
|
||||
}
|
||||
|
||||
function focusOnNextPhotoBoothItem () {
|
||||
takePhotoButton.forceActiveFocus()
|
||||
function focusOnNextPhotoBoothItem() {
|
||||
takePhotoButton.forceActiveFocus();
|
||||
}
|
||||
|
||||
function focusOnPreviousPhotoBoothItem () {
|
||||
importButton.forceActiveFocus()
|
||||
function focusOnPreviousPhotoBoothItem() {
|
||||
importButton.forceActiveFocus();
|
||||
}
|
||||
|
||||
title: JamiStrings.selectImage
|
||||
@ -69,171 +67,165 @@ BaseModalDialog {
|
||||
isPhoto: true
|
||||
visible: false
|
||||
|
||||
onValidatePhoto: function(photo) {
|
||||
if (!root.newItem)
|
||||
AccountAdapter.setCurrentAccountAvatarBase64(photo)
|
||||
else{
|
||||
onValidatePhoto: function (photo) {
|
||||
if (!root.newItem) {
|
||||
AccountAdapter.setCurrentAccountAvatarBase64(photo);
|
||||
imageTemporaryValidated();
|
||||
} else {
|
||||
UtilsAdapter.setTempCreationImageFromString(photo, imageId);
|
||||
imageValidated();
|
||||
}
|
||||
root.close()
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
popupContent: RowLayout {
|
||||
id: buttonsRowLayout
|
||||
id: buttonsRowLayout
|
||||
|
||||
spacing: 18
|
||||
spacing: 18
|
||||
|
||||
JamiPushButton {
|
||||
id: takePhotoButton
|
||||
JamiPushButton {
|
||||
id: takePhotoButton
|
||||
Accessible.name: objectName
|
||||
|
||||
objectName: "takePhotoButton"
|
||||
objectName: "takePhotoButton"
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
height: buttonSize
|
||||
width: buttonSize
|
||||
height: buttonSize
|
||||
width: buttonSize
|
||||
|
||||
enabled: VideoDevices.listSize !== 0
|
||||
hoverEnabled: enabled
|
||||
enabled: VideoDevices.listSize !== 0
|
||||
hoverEnabled: enabled
|
||||
|
||||
normalColor: "transparent"
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
toolTipText: JamiStrings.takePhoto
|
||||
source: JamiResources.add_a_photo_black_24dp_svg
|
||||
normalColor: "transparent"
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
toolTipText: JamiStrings.takePhoto
|
||||
source: JamiResources.add_a_photo_black_24dp_svg
|
||||
|
||||
Keys.onPressed: function (keyEvent) {
|
||||
if (keyEvent.key === Qt.Key_Enter ||
|
||||
keyEvent.key === Qt.Key_Return) {
|
||||
clicked()
|
||||
keyEvent.accepted = true
|
||||
} else if (keyEvent.key === Qt.Key_Up) {
|
||||
root.focusOnPreviousItem()
|
||||
keyEvent.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: {
|
||||
if (clearButton.visible)
|
||||
return clearButton
|
||||
return importButton
|
||||
}
|
||||
KeyNavigation.down: KeyNavigation.tab
|
||||
|
||||
onClicked: {
|
||||
recordBox.parent = buttonsRowLayout
|
||||
startBooth()
|
||||
Keys.onPressed: function (keyEvent) {
|
||||
if (keyEvent.key === Qt.Key_Enter || keyEvent.key === Qt.Key_Return) {
|
||||
clicked();
|
||||
keyEvent.accepted = true;
|
||||
} else if (keyEvent.key === Qt.Key_Up) {
|
||||
root.focusOnPreviousItem();
|
||||
keyEvent.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
id: importButton
|
||||
KeyNavigation.tab: {
|
||||
if (clearButton.visible)
|
||||
return clearButton;
|
||||
return importButton;
|
||||
}
|
||||
KeyNavigation.down: KeyNavigation.tab
|
||||
|
||||
objectName: "photoboothViewImportButton"
|
||||
onClicked: {
|
||||
recordBox.parent = buttonsRowLayout;
|
||||
startBooth();
|
||||
}
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: parent.visible
|
||||
JamiPushButton {
|
||||
id: importButton
|
||||
|
||||
height: buttonSize
|
||||
width: buttonSize
|
||||
objectName: "photoboothViewImportButton"
|
||||
|
||||
normalColor: "transparent"
|
||||
source: JamiResources.add_photo_alternate_black_24dp_svg
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
toolTipText: JamiStrings.importFromFile
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: parent.visible
|
||||
|
||||
Keys.onPressed: function (keyEvent) {
|
||||
if (keyEvent.key === Qt.Key_Enter ||
|
||||
keyEvent.key === Qt.Key_Return) {
|
||||
clicked()
|
||||
keyEvent.accepted = true
|
||||
} else if (keyEvent.key === Qt.Key_Down ||
|
||||
keyEvent.key === Qt.Key_Tab) {
|
||||
clearButton.forceActiveFocus()
|
||||
keyEvent.accepted = true
|
||||
}
|
||||
height: buttonSize
|
||||
width: buttonSize
|
||||
|
||||
Accessible.name: objectName
|
||||
|
||||
normalColor: "transparent"
|
||||
source: JamiResources.add_photo_alternate_black_24dp_svg
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
toolTipText: JamiStrings.importFromFile
|
||||
|
||||
Keys.onPressed: function (keyEvent) {
|
||||
if (keyEvent.key === Qt.Key_Enter || keyEvent.key === Qt.Key_Return) {
|
||||
clicked();
|
||||
keyEvent.accepted = true;
|
||||
} else if (keyEvent.key === Qt.Key_Down || keyEvent.key === Qt.Key_Tab) {
|
||||
clearButton.forceActiveFocus();
|
||||
keyEvent.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.up: takePhotoButton
|
||||
KeyNavigation.up: takePhotoButton
|
||||
|
||||
onClicked: {
|
||||
stopBooth()
|
||||
var dlg = viewCoordinator.presentDialog(
|
||||
appWindow,
|
||||
"commoncomponents/JamiFileDialog.qml",
|
||||
{
|
||||
title: JamiStrings.selectProfilePicture,
|
||||
fileMode: JamiFileDialog.OpenFile,
|
||||
folder: StandardPaths.writableLocation(
|
||||
StandardPaths.PicturesLocation),
|
||||
nameFilters: [JamiStrings.imageFiles,
|
||||
JamiStrings.allFiles]
|
||||
})
|
||||
dlg.fileAccepted.connect(function(file) {
|
||||
var filePath = UtilsAdapter.getAbsPath(file)
|
||||
onClicked: {
|
||||
stopBooth();
|
||||
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/JamiFileDialog.qml", {
|
||||
title: JamiStrings.selectProfilePicture,
|
||||
fileMode: JamiFileDialog.OpenFile,
|
||||
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation),
|
||||
nameFilters: [JamiStrings.imageFiles, JamiStrings.allFiles]
|
||||
});
|
||||
dlg.fileAccepted.connect(function (file) {
|
||||
var filePath = UtilsAdapter.getAbsPath(file);
|
||||
if (!root.newItem) {
|
||||
AccountAdapter.setCurrentAccountAvatarFile(filePath)
|
||||
AccountAdapter.setCurrentAccountAvatarFile(filePath);
|
||||
imageTemporaryValidated();
|
||||
} else {
|
||||
UtilsAdapter.setTempCreationImageFromFile(filePath, root.imageId);
|
||||
imageValidated();
|
||||
}
|
||||
root.close()
|
||||
})
|
||||
root.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
id: clearButton
|
||||
|
||||
objectName: "photoboothViewClearButton"
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
height: buttonSize
|
||||
width: buttonSize
|
||||
|
||||
normalColor: "transparent"
|
||||
source: JamiResources.remove_circle_outline_black_24dp_svg
|
||||
toolTipText: JamiStrings.removeImage
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
|
||||
visible: {
|
||||
if (!newItem && LRCInstance.currentAccountAvatarSet)
|
||||
return true;
|
||||
if (newItem && UtilsAdapter.tempCreationImage(imageId).length !== 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
KeyNavigation.up: importButton
|
||||
|
||||
Keys.onPressed: function (keyEvent) {
|
||||
if (keyEvent.key === Qt.Key_Enter || keyEvent.key === Qt.Key_Return) {
|
||||
clicked();
|
||||
importButton.forceActiveFocus();
|
||||
keyEvent.accepted = true;
|
||||
} else if (keyEvent.key === Qt.Key_Down || keyEvent.key === Qt.Key_Tab) {
|
||||
btnCancel.forceActiveFocus();
|
||||
keyEvent.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
JamiPushButton {
|
||||
id: clearButton
|
||||
|
||||
objectName: "photoboothViewClearButton"
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
height: buttonSize
|
||||
width: buttonSize
|
||||
|
||||
normalColor: "transparent"
|
||||
source: JamiResources.remove_circle_outline_black_24dp_svg
|
||||
toolTipText: JamiStrings.removeImage
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
|
||||
visible: {
|
||||
if (!newItem && LRCInstance.currentAccountAvatarSet)
|
||||
return true
|
||||
if (newItem && UtilsAdapter.tempCreationImage(imageId).length !== 0)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
KeyNavigation.up: importButton
|
||||
|
||||
Keys.onPressed: function (keyEvent) {
|
||||
if (keyEvent.key === Qt.Key_Enter ||
|
||||
keyEvent.key === Qt.Key_Return) {
|
||||
clicked()
|
||||
importButton.forceActiveFocus()
|
||||
keyEvent.accepted = true
|
||||
} else if (keyEvent.key === Qt.Key_Down ||
|
||||
keyEvent.key === Qt.Key_Tab) {
|
||||
btnCancel.forceActiveFocus()
|
||||
keyEvent.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (!root.newItem)
|
||||
AccountAdapter.setCurrentAccountAvatarBase64()
|
||||
else {
|
||||
UtilsAdapter.setTempCreationImageFromString("", imageId);
|
||||
imageRemoved();
|
||||
}
|
||||
visible = false
|
||||
stopBooth()
|
||||
root.close()
|
||||
onClicked: {
|
||||
if (!root.newItem) {
|
||||
AccountAdapter.setCurrentAccountAvatarBase64();
|
||||
imageTemporaryRemoved();
|
||||
} else {
|
||||
UtilsAdapter.setTempCreationImageFromString("", imageId);
|
||||
imageRemoved();
|
||||
}
|
||||
visible = false;
|
||||
stopBooth();
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -37,12 +37,16 @@ Row {
|
||||
|
||||
SystemButton {
|
||||
id: minButton
|
||||
Accessible.name: JamiStrings.minimize
|
||||
Accessible.role: Accessible.Button
|
||||
source: JamiResources.window_bar_minimize_svg
|
||||
onClicked: appWindow.showMinimized()
|
||||
}
|
||||
|
||||
SystemButton {
|
||||
id: maxButton
|
||||
Accessible.name: JamiStrings.maximize
|
||||
Accessible.role: Accessible.Button
|
||||
source: appWindow.visibility === Window.Maximized ?
|
||||
JamiResources.window_bar_restore_svg :
|
||||
JamiResources.window_bar_maximize_svg
|
||||
@ -53,6 +57,8 @@ Row {
|
||||
|
||||
SystemButton {
|
||||
id: closeButton
|
||||
Accessible.name: JamiStrings.closeApplication
|
||||
Accessible.role: Accessible.Button
|
||||
source: JamiResources.window_bar_close_svg
|
||||
baseColor: "#e81123"
|
||||
onClicked: appWindow.close()
|
||||
|
||||
@ -24,6 +24,7 @@ import net.jami.Constants 1.1
|
||||
|
||||
Control {
|
||||
id: root
|
||||
Accessible.role: Accessible.StaticText
|
||||
|
||||
property alias avatarBlockWidth: avatarBlock.width
|
||||
property alias innerContent: innerContent
|
||||
@ -64,6 +65,7 @@ Control {
|
||||
property bool bigMsg
|
||||
property bool timeUnderBubble: false
|
||||
property var type: Type
|
||||
property var shouldBeVisible: msgRowlayout.msgHovered || root.activeFocus || reply.activeFocus || more.activeFocus || share.activeFocus
|
||||
|
||||
// If the ListView attached properties are not available,
|
||||
// then the root delegate is likely a Loader.
|
||||
@ -81,6 +83,16 @@ Control {
|
||||
rightPadding: hPadding
|
||||
leftPadding: hPadding
|
||||
|
||||
background: Rectangle {
|
||||
id: focusIndicator
|
||||
visible: rootDelegate.activeFocus
|
||||
radius: 4
|
||||
border.color: JamiTheme.tintedBlue
|
||||
border.width: 2
|
||||
color: "transparent"
|
||||
z: 1
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: mainColumnLayout
|
||||
|
||||
@ -142,6 +154,7 @@ Control {
|
||||
RowLayout {
|
||||
id: replyToLayout
|
||||
|
||||
spacing: replyItem.isSelf ? 2 : 4
|
||||
Layout.alignment: isOutgoing ? Qt.AlignRight : Qt.AlignLeft
|
||||
property var replyUserName: UtilsAdapter.getBestNameForUri(CurrentAccount.id, ReplyToAuthor)
|
||||
|
||||
@ -185,7 +198,7 @@ Control {
|
||||
text: textMetricsUsername2.elidedText
|
||||
TextMetrics {
|
||||
id: textMetricsUsername2
|
||||
text: replyItem.isSelf ? JamiStrings.inReplyToMe : replyToLayout.replyUserName
|
||||
text: replyItem.isSelf ? JamiStrings.inReplyToYou : replyToLayout.replyUserName
|
||||
elideWidth: 200
|
||||
elide: Qt.ElideMiddle
|
||||
}
|
||||
@ -300,7 +313,7 @@ Control {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: isOutgoing ? optionButtonItem.right : undefined
|
||||
anchors.left: !isOutgoing ? optionButtonItem.left : undefined
|
||||
visible: msgRowlayout.msgHovered
|
||||
visible: shouldBeVisible
|
||||
source: JamiResources.more_vert_24dp_svg
|
||||
width: optionButtonItem.width / 4
|
||||
height: optionButtonItem.height
|
||||
@ -310,7 +323,7 @@ Control {
|
||||
|
||||
function setBindings() {
|
||||
more.isOpen = false;
|
||||
visible = Qt.binding(() => msgRowlayout.msgHovered);
|
||||
visible = Qt.binding(() => shouldBeVisible);
|
||||
imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor);
|
||||
normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor);
|
||||
}
|
||||
@ -355,7 +368,7 @@ Control {
|
||||
anchors.rightMargin: 5
|
||||
anchors.right: isOutgoing ? more.left : undefined
|
||||
anchors.left: !isOutgoing ? more.right : undefined
|
||||
visible: msgRowlayout.msgHovered
|
||||
visible: shouldBeVisible
|
||||
|
||||
onClicked: {
|
||||
MessagesAdapter.editId = "";
|
||||
@ -379,13 +392,14 @@ Control {
|
||||
anchors.rightMargin: 5
|
||||
anchors.right: isOutgoing ? reply.left : undefined
|
||||
anchors.left: !isOutgoing ? reply.right : undefined
|
||||
visible: msgRowlayout.msgHovered
|
||||
|
||||
visible: shouldBeVisible
|
||||
property bool isOpen: false
|
||||
property var obj: undefined
|
||||
|
||||
function setBindings() { // when the popup is closed, setBindings is called to reset the icon's visual settings
|
||||
share.isOpen = false;
|
||||
visible = Qt.binding(() => msgRowlayout.msgHovered);
|
||||
visible = Qt.binding(() => shouldBeVisible);
|
||||
imageColor = Qt.binding(() => hovered ? JamiTheme.chatViewFooterImgHoverColor : JamiTheme.chatViewFooterImgColor);
|
||||
normalColor = Qt.binding(() => JamiTheme.primaryBackgroundColor);
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import QtQuick.Layouts
|
||||
Rectangle {
|
||||
property alias name: label.text
|
||||
property bool stretchParent: false
|
||||
property string tag: parent.toString()
|
||||
property string tag: parent.toString() + " (w:" + width + ", h: " + height + ")"
|
||||
signal moveX(real dx)
|
||||
signal moveY(real dy)
|
||||
property real ox: 0
|
||||
|
||||
@ -50,11 +50,14 @@ ComboBox {
|
||||
|
||||
contentItem: Text {
|
||||
text: {
|
||||
if (index < 0)
|
||||
if (index < 0 || !model)
|
||||
return '';
|
||||
var currentItem = root.delegateModel.items.get(index);
|
||||
const value = currentItem.model[root.textRole];
|
||||
return value === undefined ? '' : value.toString();
|
||||
|
||||
if (root.textRole && model[root.textRole] !== undefined) {
|
||||
return model[root.textRole].toString();
|
||||
}
|
||||
|
||||
return model.display !== undefined ? model.display.toString() : '';
|
||||
}
|
||||
|
||||
color: hovered ? JamiTheme.comboboxTextColorHovered : JamiTheme.textColor
|
||||
@ -80,7 +83,7 @@ ComboBox {
|
||||
|
||||
source: popup.visible ? JamiResources.expand_less_24dp_svg : JamiResources.expand_more_24dp_svg
|
||||
|
||||
color: JamiTheme.comboboxIconColor
|
||||
color: root.enabled ? JamiTheme.comboboxIconColor : "grey"
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
@ -92,7 +95,7 @@ ComboBox {
|
||||
anchors.rightMargin: root.indicator.width * 2
|
||||
font.pixelSize: JamiTheme.settingsDescriptionPixelSize
|
||||
text: root.displayText
|
||||
color: JamiTheme.comboboxTextColor
|
||||
color: root.enabled ? JamiTheme.comboboxTextColor : "grey"
|
||||
font.weight: Font.Medium
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
@ -104,7 +107,11 @@ ComboBox {
|
||||
color: JamiTheme.transparentColor
|
||||
implicitWidth: 120
|
||||
implicitHeight: contentItem.implicitHeight + JamiTheme.buttontextHeightMargin
|
||||
border.color: popup.visible ? JamiTheme.comboboxBorderColorActive : JamiTheme.comboboxBorderColor
|
||||
border.color: root.enabled ?
|
||||
(popup.visible ?
|
||||
JamiTheme.comboboxBorderColorActive :
|
||||
JamiTheme.comboboxBorderColor) :
|
||||
"grey"
|
||||
border.width: root.visualFocus ? 2 : 1
|
||||
radius: 5
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import "contextmenu"
|
||||
|
||||
BaseContextMenu {
|
||||
id: root
|
||||
|
||||
property var modelList
|
||||
signal audioRecordMessageButtonClicked
|
||||
signal videoRecordMessageButtonClicked
|
||||
@ -31,35 +32,63 @@ BaseContextMenu {
|
||||
GeneralMenuItem {
|
||||
id: audioMessage
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: itemName
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Keys.onReturnPressed: clicked()
|
||||
|
||||
canTrigger: true
|
||||
iconSource: JamiResources.message_audio_black_24dp_svg
|
||||
itemName: JamiStrings.leaveAudioMessage
|
||||
onClicked: {
|
||||
root.audioRecordMessageButtonClicked();
|
||||
root.close()
|
||||
}
|
||||
|
||||
KeyNavigation.tab: videoMessage
|
||||
KeyNavigation.backtab: shareLocation
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: videoMessage
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: itemName
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Keys.onReturnPressed: clicked()
|
||||
|
||||
canTrigger: true
|
||||
iconSource: JamiResources.message_video_black_24dp_svg
|
||||
itemName: JamiStrings.leaveVideoMessage
|
||||
|
||||
isActif: VideoDevices.listSize !== 0
|
||||
|
||||
onClicked: {
|
||||
root.videoRecordMessageButtonClicked();
|
||||
root.close()
|
||||
}
|
||||
|
||||
KeyNavigation.tab: shareLocation
|
||||
KeyNavigation.backtab: audioMessage
|
||||
},
|
||||
GeneralMenuItem {
|
||||
id: shareLocation
|
||||
|
||||
Accessible.role: Accessible.MenuItem
|
||||
Accessible.name: itemName
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Keys.onReturnPressed: clicked()
|
||||
|
||||
canTrigger: true
|
||||
iconSource: JamiResources.localisation_sharing_send_pin_svg
|
||||
itemName: JamiStrings.shareLocation
|
||||
onClicked: {
|
||||
root.showMapClicked();
|
||||
root.close()
|
||||
}
|
||||
|
||||
KeyNavigation.tab: audioMessage
|
||||
KeyNavigation.backtab: videoMessage
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
78
src/app/commoncomponents/SpellLanguageContextMenu.qml
Normal file
78
src/app/commoncomponents/SpellLanguageContextMenu.qml
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2020-2025 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 net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Models 1.1
|
||||
import net.jami.Enums 1.1
|
||||
import "contextmenu"
|
||||
import "../mainview"
|
||||
import "../mainview/components"
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
ContextMenuAutoLoader {
|
||||
id: root
|
||||
|
||||
signal languageChanged
|
||||
|
||||
function openMenuAt(mouseEvent) {
|
||||
x = mouseEvent.x;
|
||||
y = mouseEvent.y;
|
||||
root.openMenu();
|
||||
}
|
||||
|
||||
onOpenRequested: {
|
||||
// Create the menu items from the installed dictionaries
|
||||
menuItemsToLoad = generateMenuItems();
|
||||
}
|
||||
|
||||
function generateMenuItems() {
|
||||
var menuItems = [];
|
||||
// Create new menu items
|
||||
var dictionaries = SpellCheckAdapter.getInstalledDictionaries();
|
||||
var keys = Object.keys(dictionaries);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
const locale = keys[i];
|
||||
const nativeName = dictionaries[keys[i]];
|
||||
var menuItem = Qt.createComponent("qrc:/commoncomponents/contextmenu/GeneralMenuItem.qml", Component.PreferSynchronous);
|
||||
if (menuItem.status !== Component.Ready) {
|
||||
console.error("Error loading component:", menuItem.errorString());
|
||||
continue;
|
||||
}
|
||||
let menuItemObject = menuItem.createObject(root, {
|
||||
"parent": root,
|
||||
"canTrigger": true,
|
||||
"isActif": true,
|
||||
"itemName": nativeName,
|
||||
"hasIcon": false,
|
||||
"content": locale,
|
||||
"bold": UtilsAdapter.getAppValue(Settings.SpellLang) === locale
|
||||
});
|
||||
if (menuItemObject === null) {
|
||||
console.error("Error creating menu item:", menuItem.errorString());
|
||||
continue;
|
||||
}
|
||||
menuItemObject.clicked.connect(function () {
|
||||
const locale = menuItemObject.content;
|
||||
SpellCheckAdapter.setDictionary(locale);
|
||||
});
|
||||
// Log the object pointer
|
||||
menuItems.push(menuItemObject);
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,21 @@ import net.jami.Constants 1.1
|
||||
import net.jami.Enums 1.1
|
||||
|
||||
SBSMessageBase {
|
||||
id: root
|
||||
id: rootDelegate
|
||||
|
||||
Accessible.role: Accessible.StaticText
|
||||
Accessible.name: {
|
||||
let name = isOutgoing ? JamiStrings.inReplyToYou : UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author);
|
||||
return name + ": " + Body + " " + formattedTime;
|
||||
}
|
||||
Accessible.description: {
|
||||
let status = "";
|
||||
if (bubble.isEdited)
|
||||
status += JamiStrings.edited + " ";
|
||||
return status + (readers.length > 0 ? JamiStrings.readBy + " " + readers.map(function (uri) {
|
||||
return UtilsAdapter.getBestNameForUri(CurrentAccount.id, uri);
|
||||
}).join(", ") : "");
|
||||
}
|
||||
|
||||
property bool isRemoteImage
|
||||
property bool isEmojiOnly: IsEmojiOnly
|
||||
@ -34,11 +48,11 @@ SBSMessageBase {
|
||||
Connections {
|
||||
target: bubble
|
||||
function onColorChanged(color) {
|
||||
root.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark;
|
||||
root.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
|
||||
rootDelegate.colorUrl = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewLinkColorLight : JamiTheme.chatviewLinkColorDark;
|
||||
rootDelegate.colorText = UtilsAdapter.luma(bubble.color) ? JamiTheme.chatviewTextColorLight : JamiTheme.chatviewTextColorDark;
|
||||
// Update parsed body with correct colors
|
||||
if (Body !== "")
|
||||
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color);
|
||||
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), rootDelegate.colorUrl, bubble.color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +67,7 @@ SBSMessageBase {
|
||||
textContentWidth: textEditId.width
|
||||
textContentHeight: textEditId.height
|
||||
|
||||
bigMsg: textContentWidth >= (2 / 3) * root.maxMsgWidth || extraContent.active
|
||||
bigMsg: textContentWidth >= (2 / 3) * rootDelegate.maxMsgWidth || extraContent.active
|
||||
|
||||
innerContent.children: [
|
||||
TextEdit {
|
||||
@ -63,10 +77,10 @@ SBSMessageBase {
|
||||
topPadding: bubble.isDeleted ? 6 : 10
|
||||
bottomPadding: bubble.isDeleted ? 6 : 10
|
||||
anchors.right: isOutgoing ? parent.right : undefined
|
||||
anchors.rightMargin: isOutgoing && !isEmojiOnly && !bigMsg ? root.timeWidth + root.editedWidth : 0
|
||||
anchors.rightMargin: isOutgoing && !isEmojiOnly && !bigMsg ? rootDelegate.timeWidth + rootDelegate.editedWidth : 0
|
||||
text: {
|
||||
if (Body !== "" && ParsedBody.length === 0) {
|
||||
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), root.colorUrl, bubble.color);
|
||||
MessagesAdapter.parseMessage(Id, Body, UtilsAdapter.getAppValue(Settings.DisplayHyperlinkPreviews), rootDelegate.colorUrl, bubble.color);
|
||||
return "";
|
||||
}
|
||||
if (ParsedBody !== "")
|
||||
@ -82,11 +96,11 @@ SBSMessageBase {
|
||||
|
||||
width: {
|
||||
if (extraContent.active)
|
||||
Math.max(extraContent.width, Math.min((2 / 3) * root.maxMsgWidth, implicitWidth - avatarBlockWidth, extraContent.minSize) - senderMargin);
|
||||
Math.max(extraContent.width, Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth - avatarBlockWidth, extraContent.minSize) - senderMargin);
|
||||
else if (isEmojiOnly)
|
||||
Math.min((2 / 3) * root.maxMsgWidth, implicitWidth, innerContent.width - senderMargin - (innerContent.width - senderMargin) % (JamiTheme.chatviewEmojiSize + 2));
|
||||
Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth, innerContent.width - senderMargin - (innerContent.width - senderMargin) % (JamiTheme.chatviewEmojiSize + 2));
|
||||
else
|
||||
Math.min((2 / 3) * root.maxMsgWidth, implicitWidth + 5, innerContent.width - senderMargin + 5);
|
||||
Math.min((2 / 3) * rootDelegate.maxMsgWidth, implicitWidth + 5, innerContent.width - senderMargin + 5);
|
||||
}
|
||||
|
||||
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
|
||||
@ -96,7 +110,7 @@ SBSMessageBase {
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: Text.RichText
|
||||
clip: true
|
||||
onLinkHovered: root.hoveredLink = hoveredLink
|
||||
onLinkHovered: rootDelegate.hoveredLink = hoveredLink
|
||||
onLinkActivated: Qt.openUrlExternally(new URL(hoveredLink))
|
||||
readOnly: true
|
||||
color: (ParsedBody !== "") ? getBaseColor() : (UtilsAdapter.luma(bubble.color) ? "white" : "dark")
|
||||
@ -150,7 +164,7 @@ SBSMessageBase {
|
||||
HoverHandler {
|
||||
target: previewContent
|
||||
onHoveredChanged: {
|
||||
root.hoveredLink = hovered ? LinkPreviewInfo.url : "";
|
||||
rootDelegate.hoveredLink = hovered ? LinkPreviewInfo.url : "";
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
@ -204,7 +218,7 @@ SBSMessageBase {
|
||||
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: TextEdit.RichText
|
||||
color: root.colorText
|
||||
color: rootDelegate.colorText
|
||||
visible: LinkPreviewInfo.title.length > 0
|
||||
text: LinkPreviewInfo.title
|
||||
lineHeight: 1.3
|
||||
@ -217,9 +231,9 @@ SBSMessageBase {
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: TextEdit.RichText
|
||||
visible: LinkPreviewInfo.description.length > 0
|
||||
font.underline: root.hoveredLink
|
||||
font.underline: rootDelegate.hoveredLink
|
||||
text: LinkPreviewInfo.description
|
||||
color: root.colorUrl
|
||||
color: rootDelegate.colorUrl
|
||||
lineHeight: 1.3
|
||||
}
|
||||
Label {
|
||||
@ -229,7 +243,7 @@ SBSMessageBase {
|
||||
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: TextEdit.RichText
|
||||
color: root.colorText
|
||||
color: rootDelegate.colorText
|
||||
text: LinkPreviewInfo.domain
|
||||
lineHeight: 1.3
|
||||
}
|
||||
|
||||
@ -44,11 +44,15 @@ Menu {
|
||||
|
||||
function loadMenuItems(menuItems) {
|
||||
root.addItem(menuTopBorder);
|
||||
|
||||
// Establish the preferred width of the menu by taking the maximum width of the items
|
||||
for (var j = 0; j < menuItems.length; ++j) {
|
||||
var currentItemWidth = menuItems[j].itemPreferredWidth;
|
||||
if (currentItemWidth !== JamiTheme.menuItemsPreferredWidth && currentItemWidth > menuPreferredWidth && menuItems[j].canTrigger)
|
||||
menuPreferredWidth = currentItemWidth;
|
||||
}
|
||||
|
||||
// Add the items to the menu
|
||||
for (var i = 0; i < menuItems.length; ++i) {
|
||||
if (menuItems[i].canTrigger) {
|
||||
menuItems[i].parentMenu = root;
|
||||
|
||||
@ -27,11 +27,14 @@ Loader {
|
||||
property int contextMenuItemPreferredHeight: 0
|
||||
property int contextMenuSeparatorPreferredHeight: 0
|
||||
|
||||
signal openRequested
|
||||
|
||||
active: false
|
||||
|
||||
visible: false
|
||||
|
||||
function openMenu() {
|
||||
openRequested();
|
||||
root.active = true;
|
||||
root.sourceComponent = menuComponent;
|
||||
}
|
||||
|
||||
@ -28,6 +28,8 @@ MenuItem {
|
||||
id: menuItem
|
||||
|
||||
property string itemName: ""
|
||||
property bool bold: false
|
||||
property string content: ""
|
||||
property alias iconSource: contextMenuItemImage.source
|
||||
property string iconColor: ""
|
||||
property bool canTrigger: true
|
||||
@ -98,6 +100,7 @@ MenuItem {
|
||||
anchors.left: parent.left
|
||||
height: parent.height
|
||||
text: itemName
|
||||
font.bold: bold
|
||||
color: dangerous ? JamiTheme.redColor : isActif ? JamiTheme.textColor : JamiTheme.chatViewFooterImgColor
|
||||
font.pointSize: JamiTheme.textFontSize
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "version.h"
|
||||
#include "version_info.h"
|
||||
|
||||
#include <QVariantMap>
|
||||
@ -108,7 +107,7 @@ protected:
|
||||
{"platform", QSysInfo::prettyProductName() + "_" + QSysInfo::currentCpuArchitecture()},
|
||||
{"client_sha", APP_VERSION_STRING},
|
||||
{"jamicore_sha", CORE_VERSION_STRING},
|
||||
{"build_id", QString(VERSION_STRING)},
|
||||
{"build_id", BUILD_VERSION_STRING},
|
||||
#if defined(Q_OS_WIN) && defined(BETA)
|
||||
{"build_variant", "beta"},
|
||||
#endif
|
||||
|
||||
@ -147,6 +147,7 @@ CurrentAccount::updateData()
|
||||
set_isRendezVous(accConfig.isRendezVous, true);
|
||||
set_dhtPort(accConfig.dhtPort, true);
|
||||
set_autoAnswer(accConfig.autoAnswer, true);
|
||||
set_denySecondCall(accConfig.denySecondCall, true);
|
||||
set_proxyEnabled(accConfig.proxyEnabled, true);
|
||||
set_upnpEnabled(accConfig.upnpEnabled, true);
|
||||
set_publishedSameAsLocal(accConfig.publishedSameAsLocal, true);
|
||||
|
||||
@ -121,6 +121,7 @@ class CurrentAccount final : public QObject
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, sendComposing)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, isRendezVous)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, autoAnswer)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, denySecondCall)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, proxyEnabled)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, upnpEnabled)
|
||||
QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(bool, publishedSameAsLocal)
|
||||
|
||||
@ -356,7 +356,7 @@ CurrentCall::onCurrentAccountIdChanged()
|
||||
}
|
||||
|
||||
void
|
||||
CurrentCall::onCallStatusChanged(const QString& callId, int code)
|
||||
CurrentCall::onCallStatusChanged(const QString& accountId, const QString& callId, int code)
|
||||
{
|
||||
Q_UNUSED(code)
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ private:
|
||||
private Q_SLOTS:
|
||||
void onCurrentConvIdChanged();
|
||||
void onCurrentAccountIdChanged();
|
||||
void onCallStatusChanged(const QString& callId, int code);
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId, int code);
|
||||
void onCallInfosChanged(const QString& accountId, const QString& callId);
|
||||
void onCurrentCallChanged(const QString& callId);
|
||||
void onParticipantsChanged(const QString& callId);
|
||||
|
||||
@ -371,7 +371,7 @@ CurrentConversation::updateActiveCalls(const QString&, const QString& convId)
|
||||
}
|
||||
|
||||
void
|
||||
CurrentConversation::onCallStatusChanged(const QString& callId, int)
|
||||
CurrentConversation::onCallStatusChanged(const QString& accountId, const QString& callId, int)
|
||||
{
|
||||
if (callId != callId_) {
|
||||
return;
|
||||
|
||||
@ -90,7 +90,7 @@ private Q_SLOTS:
|
||||
void updateErrors(const QString& convId);
|
||||
void updateConversationPreferences(const QString& convId);
|
||||
void updateActiveCalls(const QString&, const QString& convId);
|
||||
void onCallStatusChanged(const QString& callId, int code);
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId, int code);
|
||||
void onShowIncomingCallView(const QString& accountId, const QString& convUid);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
@ -15,32 +15,32 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "imagedownloader.h"
|
||||
#include "filedownloader.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QLockFile>
|
||||
|
||||
ImageDownloader::ImageDownloader(ConnectivityMonitor* cm, QObject* parent)
|
||||
FileDownloader::FileDownloader(ConnectivityMonitor* cm, QObject* parent)
|
||||
: NetworkManager(cm, parent)
|
||||
{}
|
||||
|
||||
void
|
||||
ImageDownloader::downloadImage(const QUrl& url, const QString& localPath)
|
||||
FileDownloader::downloadFile(const QUrl& url, const QString& localPath)
|
||||
{
|
||||
Utils::oneShotConnect(this, &NetworkManager::errorOccurred, this, [this, localPath]() {
|
||||
onDownloadImageFinished({}, localPath);
|
||||
onDownloadFileFinished({}, localPath);
|
||||
});
|
||||
|
||||
sendGetRequest(url, [this, localPath](const QByteArray& imageData) {
|
||||
onDownloadImageFinished(imageData, localPath);
|
||||
sendGetRequest(url, [this, localPath](const QByteArray& fileData) {
|
||||
onDownloadFileFinished(fileData, localPath);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ImageDownloader::onDownloadImageFinished(const QByteArray& data, const QString& localPath)
|
||||
FileDownloader::onDownloadFileFinished(const QByteArray& data, const QString& localPath)
|
||||
{
|
||||
if (data.isEmpty()) {
|
||||
Q_EMIT downloadImageFailed(localPath);
|
||||
Q_EMIT downloadFileFailed(localPath);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ ImageDownloader::onDownloadImageFinished(const QByteArray& data, const QString&
|
||||
const QDir dir;
|
||||
if (!dir.mkpath(dirPath)) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to create directory" << dirPath;
|
||||
Q_EMIT downloadImageFailed(localPath);
|
||||
Q_EMIT downloadFileFailed(localPath);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -58,10 +58,10 @@ ImageDownloader::onDownloadImageFinished(const QByteArray& data, const QString&
|
||||
if (lf.lock() && file.open(QIODevice::WriteOnly)) {
|
||||
file.write(data);
|
||||
file.close();
|
||||
Q_EMIT downloadImageSuccessful(localPath);
|
||||
Q_EMIT downloadFileSuccessful(localPath);
|
||||
return;
|
||||
}
|
||||
|
||||
qWarning() << Q_FUNC_INFO << "Failed to write image to" << localPath;
|
||||
Q_EMIT downloadImageFailed(localPath);
|
||||
qWarning() << Q_FUNC_INFO << "Failed to write file to" << localPath;
|
||||
Q_EMIT downloadFileFailed(localPath);
|
||||
}
|
||||
@ -24,7 +24,7 @@
|
||||
#include <QQmlEngine> // QML registration
|
||||
#include <QApplication> // QML registration
|
||||
|
||||
class ImageDownloader : public NetworkManager
|
||||
class FileDownloader : public NetworkManager
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_SINGLETON
|
||||
@ -32,23 +32,23 @@ class ImageDownloader : public NetworkManager
|
||||
QML_PROPERTY(QString, cachePath)
|
||||
|
||||
public:
|
||||
static ImageDownloader* create(QQmlEngine*, QJSEngine*)
|
||||
static FileDownloader* create(QQmlEngine*, QJSEngine*)
|
||||
{
|
||||
return new ImageDownloader(
|
||||
return new FileDownloader(
|
||||
qApp->property("ConnectivityMonitor").value<ConnectivityMonitor*>());
|
||||
}
|
||||
|
||||
explicit ImageDownloader(ConnectivityMonitor* cm, QObject* parent = nullptr);
|
||||
~ImageDownloader() = default;
|
||||
explicit FileDownloader(ConnectivityMonitor* cm, QObject* parent = nullptr);
|
||||
~FileDownloader() = default;
|
||||
|
||||
// Download an image and call onDownloadImageFinished when done
|
||||
Q_INVOKABLE void downloadImage(const QUrl& url, const QString& localPath);
|
||||
// Download an image and call onDownloadFileFinished when done
|
||||
Q_INVOKABLE void downloadFile(const QUrl& url, const QString& localPath);
|
||||
|
||||
Q_SIGNALS:
|
||||
void downloadImageSuccessful(const QString& localPath);
|
||||
void downloadImageFailed(const QString& localPath);
|
||||
void downloadFileSuccessful(const QString& localPath);
|
||||
void downloadFileFailed(const QString& localPath);
|
||||
|
||||
private Q_SLOTS:
|
||||
// Saves the image to the localPath and emits the appropriate signal
|
||||
void onDownloadImageFinished(const QByteArray& reply, const QString& localPath);
|
||||
void onDownloadFileFinished(const QByteArray& reply, const QString& localPath);
|
||||
};
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
#include "mainapplication.h"
|
||||
#include "instancemanager.h"
|
||||
#include "version.h"
|
||||
#include "version_info.h"
|
||||
#if defined(Q_OS_MACOS)
|
||||
#include <os/macos/macutils.h>
|
||||
#endif
|
||||
@ -66,7 +66,7 @@ main(int argc, char* argv[])
|
||||
QApplication::setApplicationName(QStringLiteral("Jami"));
|
||||
QApplication::setOrganizationDomain(QStringLiteral("jami.net"));
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
QCoreApplication::setApplicationVersion(QString(VERSION_STRING));
|
||||
QCoreApplication::setApplicationVersion(BUILD_VERSION_STRING);
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
|
||||
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
|
||||
@ -159,6 +159,7 @@ MainApplication::MainApplication(int& argc, char** argv)
|
||||
"qml.debug=false\n"
|
||||
"default.debug=false\n"
|
||||
"client.debug=false\n"
|
||||
"spellcheck.debug=false\n"
|
||||
"\n");
|
||||
// These can be set in the environment as well.
|
||||
// e.g. QT_LOGGING_RULES="*.debug=false;qml.debug=true"
|
||||
@ -347,8 +348,8 @@ MainApplication::parseArguments()
|
||||
parser_.addOption(muteDaemonOption);
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
// In debug mode, add an option to test a specific QML component via its name.
|
||||
// e.g. ./jami --test AccountComboBox
|
||||
// In debug mode, add an option to test a specific QML component via its name.
|
||||
// e.g. ./jami --test AccountComboBox
|
||||
parser_.addOption(QCommandLineOption("test", "Test a QML component via its name.", "uri"));
|
||||
// We may need to force the test window dimensions in the case that the component to test
|
||||
// does not specify its own dimensions and is dependent on parent/sibling dimensions.
|
||||
@ -463,7 +464,7 @@ MainApplication::initQmlLayer()
|
||||
void
|
||||
MainApplication::initSystray()
|
||||
{
|
||||
systemTray_->setIcon(QIcon(":/images/jami.svg"));
|
||||
systemTray_->setIcon(QIcon(":/images/net.jami.Jami.svg"));
|
||||
|
||||
QMenu* menu {nullptr};
|
||||
// If there was a previous menu, reuse it, otherwise create a new one.
|
||||
|
||||
@ -85,7 +85,6 @@ Popup {
|
||||
anchors.rightMargin: 15
|
||||
spacing: 10
|
||||
|
||||
|
||||
Avatar {
|
||||
id: avatar
|
||||
objectName: "accountComboBoxPopupAvatar"
|
||||
@ -164,8 +163,13 @@ Popup {
|
||||
imageColor: hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
hoveredColor: JamiTheme.hoverColor
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: toolTipText
|
||||
Accessible.description: JamiStrings.qrCodeExplanation
|
||||
|
||||
onClicked: {
|
||||
viewCoordinator.presentDialog(appWindow, "mainview/components/WelcomePageQrDialog.qml");
|
||||
listView.currentIndex = -1;
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
@ -184,34 +188,40 @@ Popup {
|
||||
|
||||
toolTipText: !inSettings ? JamiStrings.openSettings : JamiStrings.closeSettings
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: toolTipText
|
||||
KeyNavigation.backtab: shareButton
|
||||
|
||||
onClicked: {
|
||||
!inSettings ? viewCoordinator.present("SettingsView") : viewCoordinator.dismiss("SettingsView");
|
||||
root.close();
|
||||
}
|
||||
|
||||
KeyNavigation.tab: addAccountItem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 15
|
||||
Layout.rightMargin: 15
|
||||
color: JamiTheme.smartListHoveredColor
|
||||
}
|
||||
|
||||
|
||||
JamiListView {
|
||||
ListView {
|
||||
id: listView
|
||||
objectName: "accountList"
|
||||
Accessible.name: JamiStrings.accountList
|
||||
Accessible.role: Accessible.List
|
||||
Accessible.description: JamiStrings.accountListDescription
|
||||
|
||||
layer.mipmap: false
|
||||
clip: true
|
||||
maximumFlickVelocity: 1024
|
||||
|
||||
// HACK: remove after migration to Qt 6.7+
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: parent.width
|
||||
|
||||
activeFocusOnTab: true
|
||||
focus: true
|
||||
currentIndex: -1 // Set to -1 to avoid initial highlighting
|
||||
|
||||
model: SortFilterProxyModel {
|
||||
sourceModel: AccountListModel
|
||||
filters: ValueFilter {
|
||||
@ -221,10 +231,35 @@ Popup {
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "transparent"
|
||||
border.color: JamiTheme.primaryBackgroundColor
|
||||
border.width: 2
|
||||
radius: 5
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: JamiTheme.hoverColor
|
||||
radius: 5
|
||||
opacity: 0.3
|
||||
}
|
||||
}
|
||||
|
||||
delegate: AccountItemDelegate {
|
||||
height: JamiTheme.accountListItemHeight
|
||||
width: root.width
|
||||
|
||||
Accessible.role: Accessible.ListItem
|
||||
Accessible.name: Alias || Username
|
||||
Accessible.description: JamiStrings.switchToAccount
|
||||
|
||||
// Update the background to show focus state
|
||||
background: Rectangle {
|
||||
color: parent.activeFocus || parent.hovered ? JamiTheme.hoverColor : "transparent"
|
||||
opacity: parent.activeFocus ? 0.3 : 1
|
||||
radius: 5
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.close();
|
||||
// This is a workaround for the synchronicity issue
|
||||
@ -235,7 +270,7 @@ Popup {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 1
|
||||
Layout.fillWidth: true
|
||||
@ -248,19 +283,24 @@ Popup {
|
||||
id: addAccountItem
|
||||
|
||||
Layout.preferredHeight: 45
|
||||
Layout.preferredWidth: parent.width -10
|
||||
Layout.preferredWidth: parent.width - 10
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.leftMargin: 5
|
||||
|
||||
Accessible.name: JamiStrings.addAccount
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Accessible.name: addAccountText.text
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
KeyNavigation.tab: manageAccountItem
|
||||
KeyNavigation.up: listView
|
||||
KeyNavigation.down: manageAccountItem
|
||||
|
||||
background: Rectangle {
|
||||
color: addAccountItem.hovered ? JamiTheme.hoverColor : JamiTheme.accountComboBoxBackgroundColor
|
||||
radius: 5
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 18
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@ -274,6 +314,7 @@ Popup {
|
||||
}
|
||||
|
||||
Text {
|
||||
id: addAccountText
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: JamiStrings.addAccount
|
||||
textFormat: TextEdit.PlainText
|
||||
@ -285,18 +326,21 @@ Popup {
|
||||
root.close();
|
||||
viewCoordinator.present("WizardView");
|
||||
}
|
||||
|
||||
KeyNavigation.tab: manageAccountItem
|
||||
}
|
||||
|
||||
ItemDelegate {
|
||||
id: manageAccountItem
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: JamiStrings.manageAccount
|
||||
Accessible.name: manageAccountText.text
|
||||
|
||||
KeyNavigation.backtab: addAccountItem
|
||||
KeyNavigation.tab: shareButton
|
||||
KeyNavigation.up: addAccountItem
|
||||
|
||||
Layout.preferredHeight: 45
|
||||
Layout.preferredWidth: parent.width-10
|
||||
Layout.preferredWidth: parent.width - 10
|
||||
Layout.leftMargin: 5
|
||||
Layout.bottomMargin: 5
|
||||
|
||||
@ -305,7 +349,7 @@ Popup {
|
||||
radius: 5
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 18
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@ -319,8 +363,8 @@ Popup {
|
||||
color: manageAccountItem.hovered ? JamiTheme.textColor : JamiTheme.buttonTintedGreyHovered
|
||||
}
|
||||
Text {
|
||||
id: manageAccountText
|
||||
text: JamiStrings.manageAccount
|
||||
|
||||
textFormat: TextEdit.PlainText
|
||||
color: JamiTheme.textColor
|
||||
font.pointSize: JamiTheme.textFontSize
|
||||
@ -328,7 +372,7 @@ Popup {
|
||||
}
|
||||
onClicked: {
|
||||
root.close();
|
||||
viewCoordinator.present("SettingsView")
|
||||
viewCoordinator.present("SettingsView");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,17 +28,24 @@ ItemDelegate {
|
||||
height: JamiTheme.accountListItemHeight
|
||||
|
||||
background: Rectangle {
|
||||
width: root.width - 10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors {
|
||||
left: parent ? parent.left : undefined
|
||||
right: parent ? parent.right : undefined
|
||||
leftMargin: 5
|
||||
rightMargin: 5
|
||||
}
|
||||
radius: 5
|
||||
|
||||
Rectangle{
|
||||
Rectangle {
|
||||
id: separationLine
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
leftMargin: 10
|
||||
rightMargin: 10
|
||||
}
|
||||
height: 1
|
||||
width: parent.width - 20
|
||||
color: JamiTheme.hoverColor
|
||||
visible: index !== 0
|
||||
}
|
||||
|
||||
@ -25,6 +25,13 @@ import "../../commoncomponents"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string tipTitle: title.text
|
||||
property string tipDescription: opened ? description.text : JamiStrings.whyBackupAccount
|
||||
|
||||
Accessible.name: tipTitle
|
||||
Accessible.description: tipDescription
|
||||
|
||||
width: parent.width
|
||||
height: backupLayout.height
|
||||
|
||||
|
||||
@ -64,8 +64,8 @@ Item {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ImageDownloader
|
||||
function onDownloadImageSuccessful(localPath) {
|
||||
target: FileDownloader
|
||||
function onDownloadFileSuccessful(localPath) {
|
||||
if (localPath === cachedImage.localPath) {
|
||||
image.source = UtilsAdapter.urlFromLocalPath(localPath);
|
||||
}
|
||||
@ -90,7 +90,7 @@ Item {
|
||||
}
|
||||
if (downloadUrl && downloadUrl !== "" && localPath !== "") {
|
||||
if (!UtilsAdapter.fileExists(localPath)) {
|
||||
ImageDownloader.downloadImage(downloadUrl, localPath);
|
||||
FileDownloader.downloadFile(downloadUrl, localPath);
|
||||
} else {
|
||||
image.source = UtilsAdapter.urlFromLocalPath(localPath);
|
||||
if (image.isGif) {
|
||||
|
||||
@ -55,15 +55,30 @@ Control {
|
||||
signal fullScreenClicked
|
||||
signal swarmDetailsClicked
|
||||
|
||||
// For Keyboard naviguation
|
||||
property bool isInternalNavigation: false
|
||||
|
||||
function exitBarNavigation() {
|
||||
isInternalNavigation = false;
|
||||
// Let the parent control take over focus handling
|
||||
parent.forceActiveFocus();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: buttonDelegate
|
||||
|
||||
CallButtonDelegate {
|
||||
id: delegateItem
|
||||
width: root.height
|
||||
height: width
|
||||
barWidth: root.width
|
||||
onSubMenuVisibleChanged: subMenuOpen = subMenuVisible
|
||||
onHoveredChanged: root.barHovered = hovered
|
||||
|
||||
focusPolicy: Qt.StrongFocus
|
||||
focus: false
|
||||
|
||||
Keys.onEscapePressed: root.exitBarNavigation()
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,6 +582,34 @@ Control {
|
||||
ComboBox {
|
||||
id: overflowButton
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: JamiStrings.more
|
||||
Accessible.description: JamiStrings.moreOptions
|
||||
|
||||
KeyNavigation.tab: {
|
||||
if (popup.opened) {
|
||||
return popup.contentItem.itemAtIndex(0);
|
||||
}
|
||||
// Exit bar navigation if we've reached the end
|
||||
root.exitBarNavigation();
|
||||
return null;
|
||||
}
|
||||
|
||||
KeyNavigation.backtab: {
|
||||
if (overflowItemListView.count > 0) {
|
||||
return overflowItemListView.itemAtIndex(overflowItemListView.count - 1);
|
||||
}
|
||||
return itemListView.itemAtIndex(itemListView.count - 1);
|
||||
}
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
if (popup.opened) {
|
||||
popup.close();
|
||||
} else {
|
||||
root.exitBarNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
visible: CallOverlayModel.overflowIndex < overflowItemCount - 2
|
||||
width: root.height
|
||||
height: width
|
||||
|
||||
@ -42,8 +42,18 @@ ItemDelegate {
|
||||
text: ""
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: text
|
||||
Accessible.description: text
|
||||
Accessible.name: ItemAction.text
|
||||
Accessible.description: {
|
||||
if (!ItemAction?.text)
|
||||
return "";
|
||||
if (ItemAction.checkable) {
|
||||
return JamiStrings.pressToToggle.arg(ItemAction.text).arg(ItemAction.checked ? JamiStrings.active : JamiStrings.inactive);
|
||||
}
|
||||
return JamiStrings.pressToAction.arg(ItemAction.text);
|
||||
}
|
||||
Accessible.pressed: pressed
|
||||
Accessible.checkable: ItemAction ? ItemAction.checkable : false
|
||||
Accessible.checked: ItemAction ? ItemAction.checked : false
|
||||
|
||||
z: index
|
||||
|
||||
@ -77,7 +87,7 @@ ItemDelegate {
|
||||
return HalfPill.None;
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: JamiTheme.shortFadeDuration
|
||||
}
|
||||
@ -94,7 +104,7 @@ ItemDelegate {
|
||||
radius: isLast ? 5 : width / 2
|
||||
type: isLast ? HalfPill.Right : HalfPill.None
|
||||
|
||||
Behavior on color {
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: JamiTheme.shortFadeDuration
|
||||
}
|
||||
@ -113,7 +123,7 @@ ItemDelegate {
|
||||
source: ItemAction ? ItemAction.icon.source : ""
|
||||
color: ItemAction ? (ItemAction.enabled ? ItemAction.icon.color : Qt.lighter(ItemAction.icon.color)) : null
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
running: ItemAction !== undefined && ItemAction.blinksWhenChecked !== undefined && ItemAction.blinksWhenChecked && checked
|
||||
onStopped: icon.opacity = 1
|
||||
@ -188,7 +198,7 @@ ItemDelegate {
|
||||
radius: 4
|
||||
}
|
||||
|
||||
onActivated: index => menuAction.accept(index);
|
||||
onActivated: index => menuAction.accept(index)
|
||||
model: visible ? menuAction.listModel : null
|
||||
delegate: ItemDelegate {
|
||||
id: menuItem
|
||||
@ -313,7 +323,6 @@ ItemDelegate {
|
||||
// it fits within the overlay, with an extra leftward margin of 24 pixels.
|
||||
return diff > 0 ? xValue - diff - 24 : xValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
|
||||
@ -116,23 +116,23 @@ Rectangle {
|
||||
|
||||
spacing: 0
|
||||
|
||||
LineEditContextMenu {
|
||||
id: displayNameContextMenu
|
||||
lineEditObj: title
|
||||
selectOnly: true
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: function (mouse) {
|
||||
displayNameContextMenu.openMenuAt(mouse);
|
||||
}
|
||||
}
|
||||
|
||||
ElidedTextLabel {
|
||||
id: title
|
||||
|
||||
LineEditContextMenu {
|
||||
id: displayNameContextMenu
|
||||
lineEditObj: title
|
||||
selectOnly: true
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: function (mouse) {
|
||||
displayNameContextMenu.openMenuAt(mouse);
|
||||
}
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
|
||||
font.pointSize: JamiTheme.textFontSize + 2
|
||||
|
||||
@ -26,6 +26,12 @@ ColumnLayout {
|
||||
id: column
|
||||
width: parent.width
|
||||
|
||||
property string tipTitle: JamiStrings.customize
|
||||
property string tipDescription: JamiStrings.customizeText
|
||||
|
||||
Accessible.name: tipTitle
|
||||
Accessible.description: tipDescription
|
||||
|
||||
property var iconSize: 26
|
||||
property var margin: 5
|
||||
property var prefWidth: 170
|
||||
|
||||
@ -24,6 +24,13 @@ import "../../commoncomponents"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string tipTitle: title.text
|
||||
property string tipDescription: content.text
|
||||
|
||||
Accessible.name: tipTitle
|
||||
Accessible.description: tipDescription
|
||||
|
||||
focus: true
|
||||
width: parent.width
|
||||
height: backupLayout.height
|
||||
|
||||
@ -43,6 +43,9 @@ TabButton {
|
||||
hoverEnabled: true
|
||||
onClicked: selected()
|
||||
|
||||
Accessible.name: root.labelText
|
||||
Accessible.role: Accessible.Button
|
||||
|
||||
Rectangle {
|
||||
id: contentRect
|
||||
|
||||
|
||||
@ -26,6 +26,12 @@ ColumnLayout {
|
||||
id: column
|
||||
width: parent.width
|
||||
|
||||
property alias tipTitle: title.text
|
||||
property alias tipDescription: description.text
|
||||
|
||||
Accessible.name: tipTitle
|
||||
Accessible.description: tipDescription
|
||||
|
||||
property real maxHeight: 250
|
||||
|
||||
property var iconSize: 26
|
||||
|
||||
@ -25,4 +25,6 @@ PushButton {
|
||||
|
||||
normalColor: JamiTheme.chatviewBgColor
|
||||
imageColor: hovered ? JamiTheme.chatviewButtonColor : JamiTheme.chatViewFooterImgColor
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.description: toolTipText
|
||||
}
|
||||
|
||||
@ -228,6 +228,11 @@ Window {
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
|
||||
// make a list view of keyboardShortcutsModelList[selectionBar.currentIndex]
|
||||
JamiListView {
|
||||
id: keyboardShortcutsListView
|
||||
@ -262,6 +267,7 @@ Window {
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 20
|
||||
text: description
|
||||
color: JamiTheme.textColor
|
||||
background: Rectangle {
|
||||
width: parent.width + 16
|
||||
height: parent.height + 16
|
||||
@ -269,6 +275,7 @@ Window {
|
||||
border.width: 2
|
||||
radius: 5
|
||||
anchors.centerIn: parent
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
}
|
||||
Label {
|
||||
@ -277,6 +284,7 @@ Window {
|
||||
Layout.topMargin: 8
|
||||
Layout.rightMargin: 20
|
||||
text: shortcut
|
||||
color: JamiTheme.textColor
|
||||
background: Rectangle {
|
||||
width: parent.width + 16
|
||||
height: parent.height + 16
|
||||
@ -284,6 +292,7 @@ Window {
|
||||
border.width: 2
|
||||
radius: 5
|
||||
anchors.centerIn: parent
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -300,6 +309,10 @@ Window {
|
||||
|
||||
focus: true
|
||||
|
||||
background: Rectangle {
|
||||
color: JamiTheme.backgroundColor
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [JamiStrings.generalSettingsTitle, JamiStrings.conversationKeyboardShortcuts, JamiStrings.callKeyboardShortcuts, JamiStrings.markdownKeyboardShortcuts, JamiStrings.settings]
|
||||
|
||||
@ -339,9 +352,16 @@ Window {
|
||||
footer: Item {
|
||||
height: JamiTheme.keyboardShortcutTabBarSize
|
||||
PageIndicator {
|
||||
id: pageIndicator
|
||||
anchors.centerIn: parent
|
||||
count: selectionBar.count
|
||||
currentIndex: selectionBar.currentIndex
|
||||
delegate: Rectangle {
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
color: index === pageIndicator.currentIndex ? JamiTheme.textColor : JamiTheme.textColorHoveredHighContrast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,17 @@ Item {
|
||||
|
||||
property string timeText: "00:00"
|
||||
property string remoteRecordingLabel
|
||||
property bool isKeyboardSelectionActive: {
|
||||
if (!appWindow || !appWindow.activeFocusItem)
|
||||
return false;
|
||||
let parent = appWindow.activeFocusItem.parent;
|
||||
while (parent && parent !== appWindow && parent !== root && parent !== null) {
|
||||
if (parent.objectName === "callActionBar")
|
||||
return true;
|
||||
parent = parent.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: CurrentCall
|
||||
@ -42,7 +53,11 @@ Item {
|
||||
|
||||
property alias callActionBar: __callActionBar
|
||||
|
||||
property bool frozen: callActionBar.overflowOpen || callActionBar.barHovered || callActionBar.subMenuOpen || participantCallInStatusView.visible
|
||||
property bool frozen: callActionBar.overflowOpen ||
|
||||
callActionBar.barHovered ||
|
||||
callActionBar.subMenuOpen ||
|
||||
participantCallInStatusView.visible ||
|
||||
isKeyboardSelectionActive
|
||||
|
||||
property string muteAlertMessage: ""
|
||||
property bool muteAlertActive: false
|
||||
@ -59,15 +74,27 @@ Item {
|
||||
Component.onDestruction: CallOverlayModel.setEventFilterActive(appWindow, this, false)
|
||||
onVisibleChanged: CallOverlayModel.setEventFilterActive(appWindow, this, visible)
|
||||
|
||||
function kickOverlay() {
|
||||
root.opacity = 1;
|
||||
fadeOutTimer.restart();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: CallOverlayModel
|
||||
|
||||
function onMouseMoved(item) {
|
||||
if (item === root) {
|
||||
root.opacity = 1;
|
||||
fadeOutTimer.restart();
|
||||
kickOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
// This is part of a mechanism used to show the overlay when a focus key is pressed
|
||||
// and keep it open in the case that the user is navigating with the keyboard over
|
||||
// the call action bar.
|
||||
function onFocusKeyPressed() {
|
||||
// Always show the overlay when a focus key (Tab/BackTab) is pressed
|
||||
kickOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
@ -76,8 +103,7 @@ Item {
|
||||
context: Qt.ApplicationShortcut
|
||||
onActivated: {
|
||||
CallAdapter.muteAudioToggle();
|
||||
root.opacity = 1;
|
||||
fadeOutTimer.restart();
|
||||
kickOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,8 +113,7 @@ Item {
|
||||
context: Qt.ApplicationShortcut
|
||||
onActivated: {
|
||||
CallAdapter.muteCameraToggle();
|
||||
root.opacity = 1;
|
||||
fadeOutTimer.restart();
|
||||
kickOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,19 +17,17 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Enums 1.1
|
||||
import net.jami.Models 1.1
|
||||
|
||||
import SortFilterProxyModel 0.2
|
||||
|
||||
import "../../commoncomponents"
|
||||
|
||||
JamiFlickable {
|
||||
id: root
|
||||
|
||||
property int underlineHeight: JamiTheme.messageUnderlineHeight
|
||||
property alias text: textArea.text
|
||||
property var textAreaObj: textArea
|
||||
property alias placeholderText: textArea.placeholderText
|
||||
@ -39,9 +37,11 @@ JamiFlickable {
|
||||
property bool showPreview: false
|
||||
property bool isShowTypo: UtilsAdapter.getAppValue(Settings.Key.ShowMardownOption)
|
||||
property int textWidth: textArea.contentWidth
|
||||
property var language: AppSettingsManager.getValue(Settings.SpellLang)
|
||||
|
||||
// Used to cache the editable text when showing the preview message
|
||||
// and also to debounce the textChanged signal's effect on the composing status.
|
||||
property var underlineList: []
|
||||
property string cachedText
|
||||
property string debounceText
|
||||
|
||||
@ -72,6 +72,7 @@ JamiFlickable {
|
||||
|
||||
lineEditObj: textArea
|
||||
customizePaste: true
|
||||
spellCheckEnabled: root.spellCheckEnabled
|
||||
|
||||
onContextMenuRequirePaste: {
|
||||
// Intercept paste event to use C++ QMimeData
|
||||
@ -112,12 +113,50 @@ JamiFlickable {
|
||||
}
|
||||
}
|
||||
|
||||
property bool spellCheckEnabled: AppSettingsManager.getValue(Settings.EnableSpellCheck) && AppSettingsManager.getValue(Settings.SpellLang) !== ""
|
||||
|
||||
// Spell check is active under the following conditions:
|
||||
// 1. Spell check is enabled in settings
|
||||
// 2. The selected spell language is not ""
|
||||
// 3. We are not in preview mode
|
||||
readonly property bool spellCheckActive: spellCheckEnabled && !showPreview
|
||||
|
||||
onSpellCheckActiveChanged: textArea.updateSpellCorrection()
|
||||
|
||||
TextArea.flickable: TextArea {
|
||||
id: textArea
|
||||
|
||||
Connections {
|
||||
target: SpellCheckAdapter
|
||||
|
||||
function onDictionaryChanged() {
|
||||
textArea.updateSpellCorrection();
|
||||
}
|
||||
}
|
||||
|
||||
// Listen to settings changes to apply it to the text area
|
||||
Connections {
|
||||
target: UtilsAdapter
|
||||
|
||||
function onChangeLanguage() {
|
||||
textArea.updateSpellCorrection();
|
||||
}
|
||||
|
||||
function onChangeFontSize() {
|
||||
textArea.updateSpellCorrection();
|
||||
}
|
||||
|
||||
function onEnableSpellCheckChanged() {
|
||||
textArea.updateSpellCorrection();
|
||||
}
|
||||
}
|
||||
|
||||
readOnly: showPreview
|
||||
leftPadding: JamiTheme.scrollBarHandleSize
|
||||
rightPadding: JamiTheme.scrollBarHandleSize
|
||||
topPadding: 0
|
||||
bottomPadding: underlineHeight
|
||||
|
||||
persistentSelection: true
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
font.pointSize: JamiTheme.textFontSize + 2
|
||||
@ -135,12 +174,39 @@ JamiFlickable {
|
||||
color: "transparent"
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: textMetrics
|
||||
elide: Text.ElideMiddle
|
||||
font.family: textArea.font.family
|
||||
font.pointSize: JamiTheme.textFontSize + 2
|
||||
}
|
||||
|
||||
Text {
|
||||
id: highlight
|
||||
color: "black"
|
||||
font.bold: true
|
||||
visible: false
|
||||
}
|
||||
|
||||
onReleased: function (event) {
|
||||
if (event.button === Qt.RightButton)
|
||||
if (event.button === Qt.RightButton) {
|
||||
if (spellCheckActive && SpellCheckAdapter.hasLoadedDictionary) {
|
||||
var position = textArea.positionAt(event.x, event.y);
|
||||
textArea.moveCursorSelection(position, TextInput.SelectWords);
|
||||
textArea.selectWord();
|
||||
if (!SpellCheckAdapter.spell(textArea.selectedText)) {
|
||||
var wordList = SpellCheckAdapter.spellSuggestionsRequest(textArea.selectedText);
|
||||
if (wordList.length !== 0) {
|
||||
textAreaContextMenu.addMenuItem(wordList);
|
||||
}
|
||||
}
|
||||
}
|
||||
textAreaContextMenu.openMenuAt(event);
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
updateSpellCorrection();
|
||||
if (text !== debounceText && !showPreview) {
|
||||
debounceText = text;
|
||||
MessagesAdapter.userIsComposing(text ? true : false);
|
||||
@ -152,6 +218,8 @@ JamiFlickable {
|
||||
// eg. Enter -> Send messages
|
||||
// Shift + Enter -> Next Line
|
||||
Keys.onPressed: function (keyEvent) {
|
||||
// Update underline on each input to take into account deleted text and sent ones
|
||||
updateSpellCorrection();
|
||||
if (keyEvent.matches(StandardKey.Paste)) {
|
||||
MessagesAdapter.onPaste();
|
||||
keyEvent.accepted = true;
|
||||
@ -180,5 +248,41 @@ JamiFlickable {
|
||||
keyEvent.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
function updateSpellCorrection() {
|
||||
clearUnderlines();
|
||||
// We iterate over the whole text to find words to check and underline them if needed
|
||||
if (spellCheckActive && SpellCheckAdapter.hasLoadedDictionary) {
|
||||
var text = textArea.text;
|
||||
var words = SpellCheckAdapter.findWords(text);
|
||||
if (!words)
|
||||
return;
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
var wordInfo = words[i];
|
||||
if (wordInfo && wordInfo.word && !SpellCheckAdapter.spell(wordInfo.word)) {
|
||||
textMetrics.text = wordInfo.word;
|
||||
var xPos = textArea.positionToRectangle(wordInfo.position).x;
|
||||
var yPos = textArea.positionToRectangle(wordInfo.position).y + textArea.positionToRectangle(wordInfo.position).height;
|
||||
var underlineObject = Qt.createQmlObject('import QtQuick; Rectangle {height: 2; color: "red";}', textArea);
|
||||
underlineObject.x = xPos;
|
||||
underlineObject.y = yPos;
|
||||
underlineObject.width = textMetrics.width;
|
||||
underlineList.push(underlineObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearUnderlines() {
|
||||
// Destroy all of the underline boxes
|
||||
while (underlineList.length > 0) {
|
||||
// Get the previous item
|
||||
var underlineObject = underlineList[underlineList.length - 1];
|
||||
// Remove the last item
|
||||
underlineList.pop();
|
||||
// Destroy the removed item
|
||||
underlineObject.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,6 +479,9 @@ Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
enabled: !showPreview
|
||||
hoverEnabled: !showPreview
|
||||
Accessible.name: JamiStrings.showMoreMessagingOptions
|
||||
Accessible.role: Accessible.ComboBox
|
||||
Accessible.description: JamiStrings.showMoreMessagingOptionsDescription
|
||||
|
||||
// Used to choose the correct color for the button.
|
||||
readonly property bool highlight: down || hovered
|
||||
|
||||
@ -24,8 +24,26 @@ import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import "../../commoncomponents"
|
||||
|
||||
JamiListView {
|
||||
ListView {
|
||||
id: root
|
||||
property alias verticalScrollBar: verticalScrollBar
|
||||
layer.mipmap: false
|
||||
clip: true
|
||||
|
||||
ScrollBar.vertical: JamiScrollBar {
|
||||
id: verticalScrollBar
|
||||
|
||||
attachedFlickableMoving: root.moving
|
||||
}
|
||||
|
||||
keyNavigationEnabled: true
|
||||
keyNavigationWraps: false
|
||||
|
||||
focus: true
|
||||
activeFocusOnTab: true
|
||||
|
||||
Accessible.role: Accessible.List
|
||||
Accessible.name: JamiStrings.conversationMessages
|
||||
|
||||
function getDistanceToBottom() {
|
||||
const scrollDiff = ScrollBar.vertical.position - (1.0 - ScrollBar.vertical.size);
|
||||
@ -139,7 +157,10 @@ JamiListView {
|
||||
}
|
||||
|
||||
// fade-in mechanism
|
||||
Component.onCompleted: fadeAnimation.start()
|
||||
Component.onCompleted: {
|
||||
positionViewAtBeginning();
|
||||
fadeAnimation.start();
|
||||
}
|
||||
Rectangle {
|
||||
id: overlay
|
||||
anchors.fill: parent
|
||||
@ -194,13 +215,19 @@ JamiListView {
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
currentIndex: -1
|
||||
|
||||
Connections {
|
||||
target: CurrentConversation
|
||||
function onIdChanged() {
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
model: MessagesAdapter.messageListModel
|
||||
delegate: DelegateChooser {
|
||||
id: delegateChooser
|
||||
role: "Type"
|
||||
|
||||
DelegateChoice {
|
||||
id: delegateChoice
|
||||
roleValue: Interaction.Type.TEXT
|
||||
|
||||
TextMessageDelegate {
|
||||
|
||||
@ -220,7 +220,6 @@ Popup {
|
||||
id: cancelBtn
|
||||
objectName: "cancelBtn"
|
||||
z: 1
|
||||
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
Layout.preferredHeight: 20
|
||||
Layout.preferredWidth: 20
|
||||
@ -323,6 +322,9 @@ Popup {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
preferredSize: btnSize
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: toolTipText
|
||||
|
||||
source: JamiResources.stop_rectangle_24dp_svg
|
||||
|
||||
imageColor: JamiTheme.whiteColor
|
||||
|
||||
@ -34,7 +34,13 @@ Rectangle {
|
||||
|
||||
function clearText() {
|
||||
textArea.clear();
|
||||
textArea.forceActiveFocus();
|
||||
setTextAreaFocus();
|
||||
}
|
||||
|
||||
function setTextAreaFocus() {
|
||||
if (visible){
|
||||
textArea.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
radius: JamiTheme.primaryRadius
|
||||
@ -42,7 +48,7 @@ Rectangle {
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus) {
|
||||
textArea.forceActiveFocus();
|
||||
setTextAreaFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -49,35 +49,33 @@ Window {
|
||||
function calculateRepeaterModel() {
|
||||
var newModel = [];
|
||||
var idx;
|
||||
|
||||
if (!showWindows) {
|
||||
for (idx in Qt.application.screens) {
|
||||
newModel.push({
|
||||
title: JamiStrings.screen.arg(idx),
|
||||
index: parseInt(idx),
|
||||
isAllScreens: false
|
||||
});
|
||||
title: JamiStrings.screen.arg(idx),
|
||||
index: parseInt(idx),
|
||||
isAllScreens: false
|
||||
});
|
||||
}
|
||||
} else {
|
||||
AvAdapter.getListWindows();
|
||||
for (idx in AvAdapter.windowsNames) {
|
||||
newModel.push({
|
||||
title: AvAdapter.windowsNames[idx],
|
||||
index: parseInt(idx),
|
||||
isAllScreens: false
|
||||
});
|
||||
title: AvAdapter.windowsNames[idx],
|
||||
index: parseInt(idx),
|
||||
isAllScreens: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add "All Screens" option for non-Windows platforms when showing screens
|
||||
if (!showWindows && Qt.application.screens.length > 1 && Qt.platform.os.toString() !== "windows") {
|
||||
newModel.unshift({
|
||||
title: JamiStrings.allScreens,
|
||||
index: -1,
|
||||
isAllScreens: true
|
||||
});
|
||||
title: JamiStrings.allScreens,
|
||||
index: -1,
|
||||
isAllScreens: true
|
||||
});
|
||||
}
|
||||
|
||||
listModel = newModel;
|
||||
}
|
||||
|
||||
@ -129,7 +127,7 @@ Window {
|
||||
var calculatedWidth = Math.floor(width / cellsPerRow);
|
||||
return Math.max(componentMinWidth, calculatedWidth);
|
||||
}
|
||||
cellHeight: cellWidth * 3/4 + marginSize * 2
|
||||
cellHeight: cellWidth * 3 / 4 + marginSize * 2
|
||||
|
||||
model: listModel
|
||||
|
||||
@ -137,8 +135,7 @@ Window {
|
||||
width: screenGrid.cellWidth - marginSize
|
||||
height: screenGrid.cellHeight - marginSize
|
||||
|
||||
visible: JamiStrings.selectScreen !== modelData.title &&
|
||||
JamiStrings.selectWindow !== modelData.title
|
||||
visible: JamiStrings.selectScreen !== modelData.title && JamiStrings.selectWindow !== modelData.title
|
||||
|
||||
ScreenSharePreview {
|
||||
id: screenItem
|
||||
@ -152,8 +149,7 @@ Window {
|
||||
if (modelData.isAllScreens)
|
||||
return AvAdapter.getSharingResource(-1);
|
||||
else if (showWindows)
|
||||
return AvAdapter.getSharingResource(-2, AvAdapter.windowsIds[modelData.index],
|
||||
AvAdapter.windowsNames[modelData.index]);
|
||||
return AvAdapter.getSharingResource(-2, AvAdapter.windowsIds[modelData.index], AvAdapter.windowsNames[modelData.index], 1);
|
||||
return AvAdapter.getSharingResource(modelData.index);
|
||||
}
|
||||
}
|
||||
@ -192,8 +188,7 @@ Window {
|
||||
if (!showWindows)
|
||||
AvAdapter.shareEntireScreen(selectedScreenNumber);
|
||||
else {
|
||||
AvAdapter.shareWindow(AvAdapter.windowsIds[selectedScreenNumber],
|
||||
AvAdapter.windowsNames[selectedScreenNumber]);
|
||||
AvAdapter.shareWindow(AvAdapter.windowsIds[selectedScreenNumber], AvAdapter.windowsNames[selectedScreenNumber]);
|
||||
}
|
||||
}
|
||||
root.close();
|
||||
|
||||
@ -36,6 +36,36 @@ FocusScope {
|
||||
property color textColor: JamiTheme.textColor
|
||||
property color iconColor: JamiTheme.tintedBlue
|
||||
|
||||
Accessible.role: Accessible.Paragraph
|
||||
Accessible.name: {
|
||||
switch (type) {
|
||||
case "donation":
|
||||
return JamiStrings.donation
|
||||
case "backup":
|
||||
return JamiStrings.backupAccountBtn
|
||||
case "customize":
|
||||
return JamiStrings.customize
|
||||
case "tip":
|
||||
return title
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
Accessible.description: {
|
||||
switch (type) {
|
||||
case "donation":
|
||||
return JamiStrings.donationTipBoxText
|
||||
case "backup":
|
||||
return JamiStrings.whyBackupAccount
|
||||
case "customize":
|
||||
return JamiStrings.customizeText
|
||||
case "tip":
|
||||
return description
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
property string customizeTip: "CustomizeTipBox {}"
|
||||
|
||||
property string backupTip: "BackupTipBox {" + " onIgnore: {" + " root.ignoreClicked()" + " }" + "}"
|
||||
@ -75,6 +105,9 @@ FocusScope {
|
||||
active: type === "donation"
|
||||
focus: true
|
||||
sourceComponent: DonationTipBox {
|
||||
Accessible.name: JamiStrings.donation
|
||||
Accessible.description: JamiStrings.donationTipBoxText
|
||||
Accessible.role: Accessible.Link
|
||||
maxHeight: root.maximumHeight
|
||||
textColor: root.textColor
|
||||
iconColor: root.iconColor
|
||||
@ -86,6 +119,9 @@ FocusScope {
|
||||
id: loader_backupTip
|
||||
active: type === "backup"
|
||||
sourceComponent: BackupTipBox {
|
||||
Accessible.name: JamiStrings.backupAccountBtn
|
||||
Accessible.description: JamiStrings.whyBackupAccount
|
||||
Accessible.role: Accessible.Link
|
||||
onIgnore: {
|
||||
root.ignoreClicked();
|
||||
}
|
||||
@ -95,20 +131,24 @@ FocusScope {
|
||||
}
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader_customizeTip
|
||||
active: type === "customize"
|
||||
sourceComponent: CustomizeTipBox {
|
||||
Accessible.role: Accessible.Link
|
||||
textColor: root.textColor
|
||||
iconColor: root.iconColor
|
||||
}
|
||||
width: parent.width
|
||||
focus: true
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader_infoTip
|
||||
active: type === "tip"
|
||||
sourceComponent: InformativeTipBox {
|
||||
Accessible.role: Accessible.Link
|
||||
maxHeight: root.maximumHeight
|
||||
textColor: root.textColor
|
||||
iconColor: root.iconColor
|
||||
@ -159,6 +199,8 @@ FocusScope {
|
||||
id: component_btnClose
|
||||
PushButton {
|
||||
id: btnClose
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: JamiStrings.dismissTip
|
||||
|
||||
width: 20
|
||||
height: 20
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user