diff --git a/.github/workflows/ci.yml b/.github/workflows/ci_linux.yml
similarity index 50%
rename from .github/workflows/ci.yml
rename to .github/workflows/ci_linux.yml
index 87db274f..9703dc99 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci_linux.yml
@@ -1,38 +1,32 @@
-name: CI
+name: Linux
on: [push]
jobs:
- ci:
+ build-linux:
strategy:
- fail-fast: false
matrix:
- os: [ubuntu-latest, macos-latest]
+ qt_version: [5.15.2, 6.5.2]
- runs-on: ${{matrix.os}}
+ runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- with:
- path: 'source'
- fetch-depth: 0
- lfs: 'false'
-
- - name: Cache Qt
- id: cache-qt
- uses: actions/cache@v1
- with:
- path: ../Qt
- key: ${{ runner.os }}-QtCache
- name: Install Qt
- uses: jurplel/install-qt-action@v2
+ uses: jurplel/install-qt-action@v3
with:
- cached: ${{ steps.cache-qt.outputs.cache-hit }}
+ version: ${{matrix.qt_version}}
+ cache: true
+ cache-key-prefix: QtCache
+
+ # Actually needed for Qt6
+ - name: Install dependencies for "xcb" Qt plugin
+ run: |
+ sudo apt-get -y install libxcb-cursor0
- - name: Install OpenCascade[Ubuntu]
- if: startsWith(matrix.os, 'ubuntu')
+ - name: Install OpenCascade
run: |
sudo apt-get -y install libocct-data-exchange-dev libocct-draw-dev
GH_CASCADE_INC_DIR=`dpkg -L libocct-foundation-dev | grep -i "Standard_Version.hxx" | sed "s/\/Standard_Version.hxx//i"`
@@ -40,37 +34,38 @@ jobs:
echo "GH_CASCADE_INC_DIR=$GH_CASCADE_INC_DIR" >> $GITHUB_ENV
echo "GH_CASCADE_LIB_DIR=$GH_CASCADE_LIB_DIR" >> $GITHUB_ENV
- - name: Install OpenCascade[macOS]
- if: startsWith(matrix.os, 'macos')
+ - name: Install Assimp
run: |
- brew install opencascade
- GH_CASCADE_BASE_DIR=`brew info opencascade | grep -E -i --only-matching --max-count=1 "^(/[a-z\.\-_0-9]+)+"`
- echo "GH_CASCADE_INC_DIR=$GH_CASCADE_BASE_DIR/include/opencascade" >> $GITHUB_ENV
- echo "GH_CASCADE_LIB_DIR=$GH_CASCADE_BASE_DIR/lib" >> $GITHUB_ENV
+ sudo apt-get -y install libassimp-dev
+ GH_ASSIMP_INC_DIR=`dpkg -L libassimp-dev | grep -i "version.h" | sed "s/\/version.h//i"`
+ GH_ASSIMP_LIB_DIR=`dpkg -L libassimp-dev | grep -i "libassimp.so" | sed "s/\/libassimp.so//i"`
+ echo "GH_ASSIMP_INC_DIR=$GH_ASSIMP_INC_DIR" >> $GITHUB_ENV
+ echo "GH_ASSIMP_LIB_DIR=$GH_ASSIMP_LIB_DIR" >> $GITHUB_ENV
- name: Get count of CPU cores
uses: SimenB/github-actions-cpu-cores@v1
id: cpu-cores
- - name: Create Build folder
- run: mkdir ${{github.workspace}}/build
-
- - name: QMake
- working-directory: ${{github.workspace}}/build
+ - name: Build
run: |
+ mkdir ${{github.workspace}}/build
+ cd ${{github.workspace}}/build
echo CASCADE_INC_DIR=${{env.GH_CASCADE_INC_DIR}}
echo CASCADE_LIB_DIR=${{env.GH_CASCADE_LIB_DIR}}
+ echo ASSIMP_INC_DIR=${{env.GH_ASSIMP_INC_DIR}}
+ echo ASSIMP_LIB_DIR=${{env.GH_ASSIMP_LIB_DIR}}
[ ! -d $CASCADE_INC_DIR ] && echo "ERROR: OpenCascade include dir doesn't exist"
[ ! -d $CASCADE_LIB_DIR ] && echo "ERROR: OpenCascade lib dir doesn't exist"
- qmake ../source CASCADE_INC_DIR=${{env.GH_CASCADE_INC_DIR}} CASCADE_LIB_DIR=${{env.GH_CASCADE_LIB_DIR}} CONFIG+=withtests
-
- - name: Build
- working-directory: ${{github.workspace}}/build
- run: |
+ [ ! -d $ASSIMP_INC_DIR ] && echo "ERROR: assimp include dir doesn't exist"
+ [ ! -d $ASSIMP_LIB_DIR ] && echo "ERROR: assimp lib dir doesn't exist"
+ qmake .. CONFIG+=withtests \
+ CASCADE_INC_DIR=${{env.GH_CASCADE_INC_DIR}} \
+ CASCADE_LIB_DIR=${{env.GH_CASCADE_LIB_DIR}} \
+ ASSIMP_INC_DIR=${{env.GH_ASSIMP_INC_DIR}} \
+ ASSIMP_LIB_DIR=${{env.GH_ASSIMP_LIB_DIR}}
make -j${{steps.cpu-cores.outputs.count}}
- - name: Execute Unit Tests[Ubuntu]
- if: startsWith(matrix.os, 'ubuntu')
+ - name: Execute Unit Tests
working-directory: ${{github.workspace}}/build
env:
DISPLAY: :0
@@ -84,9 +79,3 @@ jobs:
sleep 5s
# Run tests
./mayo --runtests
-
- - name: Execute Unit Tests[macOS]
- if: startsWith(matrix.os, 'macos')
- working-directory: ${{github.workspace}}/build
- run: |
- ./mayo.app/Contents/MacOS/mayo --runtests
diff --git a/.github/workflows/ci_macos.yml b/.github/workflows/ci_macos.yml
new file mode 100644
index 00000000..8161795a
--- /dev/null
+++ b/.github/workflows/ci_macos.yml
@@ -0,0 +1,59 @@
+name: macOS
+
+on: [push]
+
+jobs:
+ build-macos:
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v3
+ with:
+ cache: true
+ cache-key-prefix: QtCache
+
+ - name: Install OpenCascade
+ run: |
+ brew install opencascade
+ GH_CASCADE_BASE_DIR=`brew info opencascade | grep -E -i --only-matching --max-count=1 "^(/[a-z\.\-_0-9]+)+"`
+ echo "GH_CASCADE_INC_DIR=$GH_CASCADE_BASE_DIR/include/opencascade" >> $GITHUB_ENV
+ echo "GH_CASCADE_LIB_DIR=$GH_CASCADE_BASE_DIR/lib" >> $GITHUB_ENV
+
+ - name: Install Assimp
+ run: |
+ brew install assimp
+ GH_ASSIMP_BASE_DIR=`brew info assimp | grep -E -i --only-matching --max-count=1 "^(/[a-z\.\-_0-9]+)+"`
+ echo "GH_ASSIMP_INC_DIR=$GH_ASSIMP_BASE_DIR/include/assimp" >> $GITHUB_ENV
+ echo "GH_ASSIMP_LIB_DIR=$GH_ASSIMP_BASE_DIR/lib" >> $GITHUB_ENV
+
+ - name: Get count of CPU cores
+ uses: SimenB/github-actions-cpu-cores@v1
+ id: cpu-cores
+
+ - name: Build
+ run: |
+ mkdir ${{github.workspace}}/build
+ cd ${{github.workspace}}/build
+ echo CASCADE_INC_DIR=${{env.GH_CASCADE_INC_DIR}}
+ echo CASCADE_LIB_DIR=${{env.GH_CASCADE_LIB_DIR}}
+ echo ASSIMP_INC_DIR=${{env.GH_ASSIMP_INC_DIR}}
+ echo ASSIMP_LIB_DIR=${{env.GH_ASSIMP_LIB_DIR}}
+ [ ! -d $CASCADE_INC_DIR ] && echo "ERROR: OpenCascade include dir doesn't exist"
+ [ ! -d $CASCADE_LIB_DIR ] && echo "ERROR: OpenCascade lib dir doesn't exist"
+ [ ! -d $ASSIMP_INC_DIR ] && echo "ERROR: assimp include dir doesn't exist"
+ [ ! -d $ASSIMP_LIB_DIR ] && echo "ERROR: assimp lib dir doesn't exist"
+ qmake .. CONFIG+=withtests \
+ CASCADE_INC_DIR=${{env.GH_CASCADE_INC_DIR}} \
+ CASCADE_LIB_DIR=${{env.GH_CASCADE_LIB_DIR}} \
+ ASSIMP_INC_DIR=${{env.GH_ASSIMP_INC_DIR}} \
+ ASSIMP_LIB_DIR=${{env.GH_ASSIMP_LIB_DIR}}
+ make -j${{steps.cpu-cores.outputs.count}}
+
+ - name: Execute Unit Tests
+ working-directory: ${{github.workspace}}/build
+ run: |
+ ./mayo.app/Contents/MacOS/mayo --runtests
diff --git a/.github/workflows/ci_windows.yml b/.github/workflows/ci_windows.yml
new file mode 100644
index 00000000..de894de8
--- /dev/null
+++ b/.github/workflows/ci_windows.yml
@@ -0,0 +1,99 @@
+name: Windows
+
+on: [push]
+
+jobs:
+ build-windows-msvc:
+ strategy:
+ matrix:
+ occ_version: [7.3.0, 7.4.0, 7.5.0, 7.6.0, 7.7.0]
+
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v3
+ with:
+ cache: true
+ cache-key-prefix: QtCache
+
+ - name: Cache OpenCascade archive
+ id: cache-occ
+ uses: actions/cache@v3
+ with:
+ path: OpenCASCADE-${{matrix.occ_version}}-vc14-64.zip
+ key: occ-${{matrix.occ_version}}
+
+ - name: Download OpenCascade
+ if: steps.cache-occ.outputs.cache-hit != 'true'
+ uses: carlosperate/download-file-action@v2
+ with:
+ file-url: 'https://www.fougue.pro/share/bin/OpenCASCADE-${{matrix.occ_version}}-vc14-64.zip'
+
+ - name: Extract OpenCascade
+ shell: pwsh
+ run: |
+ Expand-Archive -Path OpenCASCADE-${{matrix.occ_version}}-vc14-64.zip -DestinationPath .
+
+ - name: Cache Assimp archive
+ id: cache-assimp
+ uses: actions/cache@v3
+ with:
+ path: assimp-5.3.1.zip
+ key: assimp-5.3.1
+
+ - name: Download Assimp
+ if: steps.cache-assimp.outputs.cache-hit != 'true'
+ uses: carlosperate/download-file-action@v2
+ with:
+ file-url: 'https://www.fougue.pro/share/bin/assimp-5.3.1.zip'
+
+ - name: Extract Assimp
+ shell: pwsh
+ run: |
+ Expand-Archive -Path assimp-5.3.1.zip -DestinationPath .
+
+ - name: Download jom.exe
+ uses: carlosperate/download-file-action@v2
+ with:
+ file-url: 'https://www.fougue.pro/share/bin/jom.exe'
+
+ - name: Get count of CPU cores
+ uses: SimenB/github-actions-cpu-cores@v1
+ id: cpu-cores
+
+ - name: Create Build folder
+ run: mkdir ${{github.workspace}}/build
+
+ - name: Configure Compiler
+ uses: ilammy/msvc-dev-cmd@v1
+
+ - name: QMake
+ working-directory: ${{github.workspace}}/build
+ shell: cmd
+ run: |
+ call ..\OpenCASCADE-${{matrix.occ_version}}-vc14-64\opencascade-${{matrix.occ_version}}\env.bat
+ echo CSF_OCCTIncludePath=%CSF_OCCTIncludePath%
+ echo CSF_OCCTLibPath=%CSF_OCCTLibPath%
+ qmake --version
+ qmake ..\mayo.pro CONFIG+=release CONFIG+=withtests ^
+ ASSIMP_INC_DIR=${{github.workspace}}/assimp-5.3.1/include/assimp ^
+ ASSIMP_LIB_DIR=${{github.workspace}}/assimp-5.3.1/lib ^
+ ASSIMP_LIBNAME_SUFFIX=-vc143-mt
+
+ - name: Build
+ working-directory: ${{github.workspace}}/build
+ run: |
+ ..\jom.exe -j${{steps.cpu-cores.outputs.count}}
+
+ - name: Execute Unit Tests
+ working-directory: ${{github.workspace}}/build
+ shell: cmd
+ run: |
+ call ..\OpenCASCADE-${{matrix.occ_version}}-vc14-64\opencascade-${{matrix.occ_version}}\env.bat
+ set PATH=${{github.workspace}}\assimp-5.3.1\bin;%PATH%
+ release\mayo.exe --runtests -o utests-output.txt
+ more utests-output.txt
diff --git a/.gitignore b/.gitignore
index 39ce3cf0..96cf762e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
build-*
*.user
*.user.*
-installer/setupvars.iss
-installer/Output
-custom.pri
\ No newline at end of file
+custom.pri
+env.pri
diff --git a/README.md b/README.md
index e8051a4e..1fba5424 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-[![CI](https://github.com/fougue/mayo/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/fougue/mayo/actions/workflows/ci.yml)
-[![Build status](https://ci.appveyor.com/api/projects/status/6d1w0d6gw28npxpf/branch/develop?svg=true)](https://ci.appveyor.com/project/HuguesDelorme/mayo)
-[![Codacy Badge](https://app.codacy.com/project/badge/Grade/d51f8ca6fea34886b8308ff0246172ce)](https://www.codacy.com/gh/fougue/mayo/dashboard?utm_source=github.com&utm_medium=referral&utm_content=fougue/mayo&utm_campaign=Badge_Grade)
+[![Windows CI](https://github.com/fougue/mayo/actions/workflows/ci_windows.yml/badge.svg?branch=develop)](https://github.com/fougue/mayo/actions/workflows/ci_windows.yml)
+[![Linux CI](https://github.com/fougue/mayo/actions/workflows/ci_linux.yml/badge.svg?branch=develop)](https://github.com/fougue/mayo/actions/workflows/ci_linux.yml)
+[![macOS CI](https://github.com/fougue/mayo/actions/workflows/ci_macos.yml/badge.svg?branch=develop)](https://github.com/fougue/mayo/actions/workflows/ci_macos.yml)
+[![Codacy Badge](https://app.codacy.com/project/badge/Grade/d51f8ca6fea34886b8308ff0246172ce)](https://app.codacy.com/gh/fougue/mayo/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![Downloads](https://img.shields.io/github/downloads/fougue/mayo/total.svg)](https://github.com/fougue/mayo/releases)
-[![License](https://img.shields.io/badge/license-BSD%202--clause-blue.svg)](https://github.com/fougue/mayo/blob/develop/LICENSE.txt)
[![Version](https://img.shields.io/badge/version-v0.7.0-blue.svg?style=flat)](https://github.com/fougue/mayo/releases)
@@ -62,9 +62,14 @@ OBJ | :white_check_mark: | :white_check_mark: |
glTF | :white_check_mark: | :white_check_mark: | 1.0, 2.0 and GLB
VRML | :white_check_mark: | :white_check_mark: | v2.0 UTF8
STL | :white_check_mark: | :white_check_mark: | ASCII/binary
-AMF | :x: | :white_check_mark: | v1.2 Text/ZIP
+AMF | :white_check_mark: | :white_check_mark: | v1.2 Text/ZIP(export)
PLY | :white_check_mark: | :white_check_mark: | ASCII/binary
OFF | :white_check_mark: | :white_check_mark: |
+3MF | :white_check_mark: | :x: |
+3DS | :white_check_mark: | :x: |
+FBX | :white_check_mark: | :x: |
+Collada | :white_check_mark: | :x: |
+X3D | :white_check_mark: | :x: |
Image | :x: | :white_check_mark: | PNG, JPEG, ...
See also this dedicated [wikipage](https://github.com/fougue/mayo/wiki/Supported-formats) for more details
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 4f7157bf..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-version: 0.7_build{build}
-
-image: Visual Studio 2017
-platform: x64
-configuration: Release
-
-clone_folder: c:\projects\mayo
-
-#branches:
-# only:
-# - develop
-# - master
-
-matrix:
- fast_finish: true
-
-environment:
- matrix:
- - APPVEYOR_OCC_VERSION: 7.3.0
- - APPVEYOR_OCC_VERSION: 7.4.0
- - APPVEYOR_OCC_VERSION: 7.5.0
- - APPVEYOR_OCC_VERSION: 7.6.0
- - APPVEYOR_OCC_VERSION: 7.7.0
-
-cache:
- - OpenCASCADE-7.3.0-vc14-64.rar
- - OpenCASCADE-7.4.0-vc14-64.rar
- - OpenCASCADE-7.5.0-vc14-64.rar
- - OpenCASCADE-7.6.0-vc14-64.rar
- - OpenCASCADE-7.7.0-vc14-64.rar
-
-install:
- - if not exist OpenCASCADE-%APPVEYOR_OCC_VERSION%-vc14-64.rar
- appveyor DownloadFile http://www.fougue.pro/share/bin/OpenCASCADE-%APPVEYOR_OCC_VERSION%-vc14-64.rar -FileName OpenCASCADE-%APPVEYOR_OCC_VERSION%-vc14-64.rar
- - 7z x OpenCASCADE-%APPVEYOR_OCC_VERSION%-vc14-64.rar
-
-before_build:
- - echo JOB_NAME=%APPVEYOR_JOB_NAME%
- - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
- - call "OpenCASCADE-%APPVEYOR_OCC_VERSION%-vc14-64\opencascade-%APPVEYOR_OCC_VERSION%\env.bat"
- - set PATH=C:\Qt\5.13\msvc2017_64\bin;%PATH%
- - set PATH=C:\Qt\Tools\QtCreator\bin;%PATH%
- - qmake --version
- - echo NUMBER_OF_PROCESSORS=%NUMBER_OF_PROCESSORS%
-
-build_script:
- - mkdir build-%APPVEYOR_OCC_VERSION%
- - cd build-%APPVEYOR_OCC_VERSION%
- - qmake ..\mayo.pro CONFIG+=withtests
- - jom -j%NUMBER_OF_PROCESSORS%
- - cd ..
-
-test_script:
- - cd build-%APPVEYOR_OCC_VERSION%
- - release\mayo.exe --runtests
- - cd ..
-
-on_success:
- - ps: >-
- if ($true)
- {
- Write-Host "Success"
- }
diff --git a/doc/screenshot_ubuntu_1.png b/doc/screenshot_ubuntu_1.png
new file mode 100644
index 00000000..dd877c88
Binary files /dev/null and b/doc/screenshot_ubuntu_1.png differ
diff --git a/doc/screenshot_ubuntu_2.png b/doc/screenshot_ubuntu_2.png
new file mode 100755
index 00000000..b57cdc3e
Binary files /dev/null and b/doc/screenshot_ubuntu_2.png differ
diff --git a/doc/screenshot_ubuntu_3.png b/doc/screenshot_ubuntu_3.png
new file mode 100755
index 00000000..ff9e8059
Binary files /dev/null and b/doc/screenshot_ubuntu_3.png differ
diff --git a/doc/screenshot_ubuntu_4.png b/doc/screenshot_ubuntu_4.png
new file mode 100755
index 00000000..2a0f4070
Binary files /dev/null and b/doc/screenshot_ubuntu_4.png differ
diff --git a/doc/screenshot_ubuntu_5.png b/doc/screenshot_ubuntu_5.png
new file mode 100755
index 00000000..34ab7e52
Binary files /dev/null and b/doc/screenshot_ubuntu_5.png differ
diff --git a/doc/screenshot_ubuntu_main.png b/doc/screenshot_ubuntu_main.png
new file mode 100755
index 00000000..9519a13a
Binary files /dev/null and b/doc/screenshot_ubuntu_main.png differ
diff --git a/i18n/mayo_en.qm b/i18n/mayo_en.qm
index 2bd1c216..d83aed12 100644
Binary files a/i18n/mayo_en.qm and b/i18n/mayo_en.qm differ
diff --git a/i18n/mayo_en.ts b/i18n/mayo_en.ts
index 43b76ad8..5fcd5f1a 100644
--- a/i18n/mayo_en.ts
+++ b/i18n/mayo_en.ts
@@ -28,6 +28,33 @@
User Defined
+
+ AppModuleProperties
+
+
+ Very Coarse
+
+
+
+ Coarse
+
+
+
+ Normal
+
+
+
+ Precise
+
+
+
+ Very Precise
+
+
+
+ User Defined
+
+
Mayo::AppModule
@@ -157,34 +184,28 @@
Export
-
- Very Coarse
+ Very Coarse
-
- Coarse
+ Coarse
-
- Normal
+ Normal
-
- Precise
+ Precise
-
- Very Precise
+ Very Precise
-
- User Defined
+ User Defined
@@ -240,17 +261,17 @@
Export
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -417,18 +438,58 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
Show Nodes
+
+
+
+
+
+
+
+
+ Imperial UK
+
+
+
+
+ Very Coarse
+
+
+
+
+ Coarse
+
+
+
+
+ Normal
+
+
+
+
+ Precise
+
+
+
+
+ Very Precise
+
+
+
+
+ User Defined
+
Mayo::Application
-
+
-
+
@@ -462,214 +523,218 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
Mayo::Command
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
%1 is the format identifier and %2 is the file filters string
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
Import
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
-
+
-
+
+
+
+
+
+
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
+
@@ -680,29 +745,34 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
-
+
-
-
+
+
-
+
-
+
-
+
+
+
+
+
+
@@ -712,17 +782,38 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
Mayo::DialogAbout
@@ -772,82 +863,82 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
-
+
-
+
-
+
-
+
-
+
-
+
-
+
Shape
-
+
Color
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -855,76 +946,76 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -932,79 +1023,79 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1012,43 +1103,43 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
-
+
-
+
Mayo::DocumentPropertyGroup
-
+
File Path
-
+
File Size
-
+
Created
-
+
Modified
-
+
Owner
-
+
Count Of Entities
@@ -1169,22 +1260,22 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
-
+
-
+
-
+
-
+
@@ -1212,44 +1303,44 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1276,6 +1367,21 @@ Only applicable if option `{}` is on
Use ZIP64 extensions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Mayo::IO::ImageWriter::Properties
@@ -1305,52 +1411,62 @@ Only applicable if option `{}` is on
-
+
-
+
-
+
-
+
Width
-
+
Height
-
+
Background Color
-
+
Camera Orientation
-
+
Camera Projection
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1384,60 +1500,60 @@ Only applicable if option `{}` is on
Mayo::IO::OccCommon
-
-
+
+
-
+
-
+
-
+ +Zup
-
+
-
+ +Yup
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1455,12 +1571,12 @@ Only applicable if option `{}` is on
-
+
-
+
@@ -1470,129 +1586,139 @@ Only applicable if option `{}` is on
Coordinates Converter
-
+
Transformation Format
-
+
- Format
+ Target Format
-
+
Force UV Export
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+ Input Coordinate System
-
+
-
+ Output Coordinate System
-
+
Node Name Format
-
+
Mesh Name Format
-
+
Embed Textures
-
+
Merge Faces
-
+
Keep 16bit Indices
-
+
-
+
+
+
+
+
+ JSON
+
+
+
+
+ Binary
@@ -1620,7 +1746,7 @@ Applicable only if option `{}` is on
-
+
@@ -1631,52 +1757,52 @@ Concerned entity types are 141 (Boundary), 142 (CurveOnSurface) and 508 (Loop).
The processor also decides to re-compute either the 3D or the 2D curve even if both curves are translated successfully and seem to be correct, in case there is inconsistency between them. The processor considers that there is inconsistency if any of the following conditions is satisfied:
- the number of sub-curves in the 2D curve is different from the number of sub-curves in the 3D curve. This can be either due to different numbers of sub-curves given in the IGES file or because of splitting of curves during translation
- 3D or 2D curve is a Circular Arc (entity type 100) starting and ending in the same point (note that this case is incorrect according to the IGES standard)
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1699,17 +1825,17 @@ The processor also decides to re-compute either the 3D or the 2D curve even if b
-
+
-
+
-
+
@@ -1722,7 +1848,7 @@ The processor also decides to re-compute either the 3D or the 2D curve even if b
-
+
@@ -1734,22 +1860,22 @@ The processor also decides to re-compute either the 3D or the 2D curve even if b
-
+
-
+
-
+ Input Coordinate System
-
+ Output Coordinate System
@@ -1788,83 +1914,83 @@ The processor also decides to re-compute either the 3D or the 2D curve even if b
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1922,58 +2048,58 @@ This kind of association was used for the representation of hybrid models (i.e.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1983,28 +2109,36 @@ It can be disabled in order to minimize the size of the resulting file.
Target Format
-
- Text
+ Text
-
- Binary
+ Binary
Mayo::IO::OccStlWriterI18N
-
+
- Target Format
+ Target Format
-
-
+
+
-
+
+
+
+
+
+ Text
+
+
+
+
+ Binary
@@ -2052,171 +2186,186 @@ It can be disabled in order to minimize the size of the resulting file.
+
+
+
+ Text
+
+
+
+
+ Binary
+
Mayo::IO::System
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
Mayo::Main
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
File Path
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2224,105 +2373,47 @@ It can be disabled in order to minimize the size of the resulting file.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
Import
-
-
-
-
-
-
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -2383,7 +2474,7 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
@@ -2391,33 +2482,33 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2426,12 +2517,12 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
-
+
@@ -2440,41 +2531,41 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2482,12 +2573,12 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
-
+
@@ -2495,7 +2586,7 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
@@ -2503,37 +2594,37 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2541,12 +2632,12 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
-
+
@@ -2556,71 +2647,230 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
+
+
+
+ Mayo::WidgetGrid
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Color
Mayo::WidgetGuiDocument
-
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2628,47 +2878,47 @@ Last modified: %3
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2680,7 +2930,81 @@ Created: %3
Modified: %4
Read: %5
-
+
+
+
+
+ Mayo::WidgetMainControl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Mayo::WidgetMainHome
+
+
+
+
@@ -2688,137 +3012,142 @@ Read: %5
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2826,17 +3155,17 @@ Read: %5
-
+
-
+
-
+
@@ -2849,22 +3178,22 @@ Read: %5
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2872,17 +3201,17 @@ Read: %5
-
+
-
+
-
+
@@ -2994,6 +3323,17 @@ Read: %5
OccStlWriter::Properties
+
+
+ Text
+
+
+
+ Binary
+
+
+
+ OccStlWriterI18N
Text
@@ -3076,102 +3416,102 @@ Read: %5
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/i18n/mayo_fr.qm b/i18n/mayo_fr.qm
index d0002376..dbaa78ea 100644
Binary files a/i18n/mayo_fr.qm and b/i18n/mayo_fr.qm differ
diff --git a/i18n/mayo_fr.ts b/i18n/mayo_fr.ts
index 502e1072..3a75a448 100644
--- a/i18n/mayo_fr.ts
+++ b/i18n/mayo_fr.ts
@@ -28,6 +28,33 @@
Custom
+
+ AppModuleProperties
+
+
+ Très grossière
+
+
+
+ Grossière
+
+
+
+ Normale
+
+
+
+ Précise
+
+
+
+ Très précise
+
+
+
+ Custom
+
+
Mayo::AppModule
@@ -197,34 +224,28 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
Export
-
- Très grossière
+ Très grossière
-
- Grossière
+ Grossière
-
- Normale
+ Normale
-
- Précise
+ Précise
-
- Très précise
+ Très précise
-
- Custom
+ Custom
@@ -280,17 +301,17 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
Export
-
+
Langage de l'application. Tout changement sera effectif après redémarrage de l'application
-
+
Dans le cas où plusieurs documents sont ouverts, fait en sort que le document affiché dans la vue 2D correspond à ce qui est sélectionné dans l'arborescence du modèle
-
+
-
+
Contrôle la précision du maillage calculé à partir de la forme BRep
-
+
Pour la tesselation des faces, la déflection chordale limite la distance entre une courbe et sa discrétisation
-
+
Pour la tesselation des faces, la déflection angulaire limite l'angle entre les segments successifs d'une polyligne
@@ -330,7 +351,7 @@ Cette option est appliquable seulement avec la version ≥ 7.6 d'OpenCascad
Pour la tesselation des faces, la déflection angulaire limite l'angle entre les segments successifs d'une polyligne
-
+
@@ -339,27 +360,27 @@ If activated, deflection used for the polygonalisation of each edge will be `Cho
Si actif, la déflection utilisée pour la polygonisation de chaque arête sera de `DéflectionChordale` × `TailleArête`. La déflection utilisée pour les faces sera la déflection maximale de ses arêtes.
-
+
Configuration des raccourcis pour manipuler la vue 3D, permet d'imiter les autres application CAO
-
+
Incrément angulaire utilisé pour tourner la vue 3D autour de la normale au plan de vue (axe Z de référence)
-
+
Montrer/cacher par défaut le trièdre positionné à l'orgine "monde". N'affecte pas la vue 3D des documents actuellement ouverts
-
+
Activer le bouchage des graphismes actuellement coupés
-
+
Activer le hachage texturé pour le bouchage des graphismes actuellement coupés
@@ -473,6 +494,46 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
Afficher les nœuds
+
+
+
+ SI
+
+
+
+
+ Système impérial
+
+
+
+
+ Très grossière
+
+
+
+
+ Grossière
+
+
+
+
+ Normale
+
+
+
+
+ Précise
+
+
+
+
+ Très précise
+
+
+
+
+ Custom
+
Mayo::Application
@@ -561,216 +622,224 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
Mayo::Command
-
+
Orthographique
-
+
-
+
-
+
Mode
-
+
Montrer le trihèdre Origine
-
+
Montrer/cacher le trihèdre Origine
-
+
Montrer les statistiques de rendu
-
+
Montrer/cacher les statistiques de rendu
-
+
Zoom avant
-
+
Zoom arrière
-
+
Tourner dans le sens anti-horaire
-
+
Tourner dans le sens horaire
-
+
%1 is the format identifier and %2 is the file filters string
%1 fichiers (%2)
-
+
Tous les fichiers (*.*)
-
+
Selectionner fichier pièce
-
-
+
+
Maillage des formes BRep
-
-
+
+
Durée import: {}ms
-
+
Nouveau
-
+
Nouveau Document
-
+
Anonyme%1
-
+
Ouvrir
-
+
Ouvrir des documents
-
+
Fichiers récents
-
+
-
+
Vider le menu
-
-
+
+
Importer
-
+
Importer dans le document courant
-
-
+
+
Exporter les éléments sélectionnées
-
+
Aucun élément sélectionné pour l'export
-
+
Sélection fichier de sortie
-
+
Durée export: {}ms
-
-
+
Fermer "%1"
-
+
+
+ Fermer %1
+
+
+
Fermer
-
+
Tout fermer
-
+
Fermer tous les documents
-
-
+
+
Tout fermer sauf document courant
-
+
Tout fermer sauf document courant
-
+
+
+ Tout fermer sauf %1
+
+
- Tout fermer sauf "%1"
+ Tout fermer sauf "%1"
-
+
Quitter
-
+
Signaler un bug
-
+
- À propos %1
+ À propos de %1
@@ -779,29 +848,34 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
Sauvegarder la vue vers une image
-
+
Inspection XDE
-
-
+
+
Options
-
+
Plein-écran
-
+
Basculer plein-écran/normal
-
+
+
+ Cacher le bandeau vertical fixé à gauche
+
+
+
Montrer le bandeau vertical fixé à gauche
@@ -811,17 +885,38 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
Montrer/cacher le bandeau vertical fixé à gauche
-
-
+
+
+
+ Aller à la page d'accueil
+
+
+
+
+ Aller à la page des documents
+
+
+
+
Document précédent
-
-
+
+
Document suivant
+
+
+
+ Informations du système ...
+
+
+
+
+ Copier dans le presse-papiers
+
Mayo::CommandCloseCurrentDocument
@@ -910,52 +1005,52 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
-
+
Forme
-
+
Couleur
-
+
-
+
-
+
-
+
-
+
-
+
Erreur
-
+
Ce document n'est pas XDE-compatible
-
+
Attributs
@@ -978,61 +1073,61 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
%1 / %2
-
+
Échanger
-
+
Charger le fichier ...
-
+
Sauvergarder vers ...
-
-
+
+
Choisir fichier INI
-
-
+
+
Fichiers INI(*.ini)
-
-
-
+
+
+
Erreur
-
+
'%1' n'existe pas
-
+
'%1' ne dispose pas des permissions de lecture
-
+
Erreur lors de l'écriture vers '%1'
-
+
Restaurer les valeurs seulement pour la section par défaut
-
+
Restaure les valeurs pour tout le groupe
@@ -1133,32 +1228,32 @@ Si actif, la déflection utilisée pour la polygonisation de chaque arête sera
Mayo::DocumentPropertyGroup
-
+
Chemin
-
+
Taille
-
+
Créé
-
+
Modifié
-
+
Propriétaire
-
+
Nombre d'entités
@@ -1412,6 +1507,21 @@ Seulement applicable si l'option `%1` est activée
Utiliser les extensions ZIP64
+
+
+
+ Décimal
+
+
+
+
+ Scientifique
+
+
+
+
+ Le plus bref
+
Mayo::IO::ImageWriter::Properties
@@ -1466,40 +1576,50 @@ Seulement applicable si l'option `%1` est activée
Orientation de la caméra selon la convention Z-up exprimée en tant que vecteur unitaire
-
+
Largeur
-
+
Hauteur
-
+
Couleur de l'arrière plan
-
+
Orientation de la caméra
-
+
Projection de la caméra
-
+
Aucun élément transféré
-
+
Le vecteur d'orienation de la caméra ne doit pas être nul
+
+
+
+
+
+
+
+
+ Orthographique
+
Mayo::IO::OccBaseMeshReaderProperties
@@ -1521,69 +1641,69 @@ Seulement applicable si l'option `%1` est activée
-
+ Préfixe pour la génération des noms de labels racine
-
+ Système d'unité de longueur cible lors de la lecture fichier
Mayo::IO::OccCommon
-
-
+
+
Indéfini
-
+
+Zup
-
+
+Yup
-
+
Micromètre
-
+
Millimètre
-
+
Centimètre
-
+
Mètre
-
+
Kilomètre
-
+
Pouce
-
+
Pied
-
+
Mile
@@ -1622,17 +1742,17 @@ Seulement applicable si l'option `%1` est activée
Convertisseur de coordonnées
-
+
Format de transformation
-
+
- Format
+ Format cible
-
+
Forcer l'export UV
@@ -1676,17 +1796,17 @@ Seulement applicable si l'option `%1` est activée
Transformation décomposée en vecteur de translation, quaternion de rotation et facteur d'échelle (T x R x S)
-
+
Format du nom utilisé pour exporter la hiérarchie de nœuds
-
+
Format du nom utilisé pour exporter la hiérarchie de maillages
-
+
-
+
@@ -1720,7 +1840,7 @@ May reduce JSON size thanks to smaller number of primitive arrays
Peut réduire la taille JSON grâce à une quantité réduite de tableaux de primitives
-
+
-
+
Système de coordonnées d'entrée
-
+
Système de coordonnées de sortie
-
+
Format du nom pour les nœuds
-
+
Format du nom pour les maillages
-
+
Incorporer les textures dans le même fichier cible
-
+
Fusionner les faces
-
+
Utiliser des indices 16bit
-
+
Option prise en charge à partir de OpenCascade ≥ v7.6 [option={}, version actuelle={}]
+
+
+
+ JSON
+
+
+
+
+ Binaire
+
Mayo::IO::OccIgesReader::Properties
@@ -1814,7 +1944,7 @@ The processor also decides to re-compute either the 3D or the 2D curve even if b
-
+ Lecture des entités en erreur
@@ -2161,29 +2291,37 @@ It can be disabled in order to minimize the size of the resulting file.
Format cible
-
- Texte
+ Texte
-
- Binaire
+ Binaire
Mayo::IO::OccStlWriterI18N
-
+
Format cible
-
-
+
+
Les faces BRep ne sont pas toutes maillées
+
+
+
+ Texte
+
+
+
+
+ Binaire
+
Mayo::IO::OccVrmlWriter::Properties
@@ -2230,6 +2368,20 @@ It can be disabled in order to minimize the size of the resulting file.
Boutisme du CPU inconnu
+
+
+ JSON
+
+
+
+
+ Texte
+
+
+
+
+ Binaire
+
Mayo::IO::System
@@ -2323,62 +2475,67 @@ It can be disabled in order to minimize the size of the resulting file.
Mayo, une visionneuse 3D en code libre basée surQt5/OpenCascade
-
+
Thème de l'IHM (classic|dark)
-
+
nom
-
+
Écrit les messages de log dans un fichier de sortie
-
+
Ne pas filtrer les messages de debug dans la version "release"
-
+
Désactiver l'indicateur de progression dans la sortie console (mode CLI seulement)
-
+
+
+ Montrer les informations détaillées du système puis quitter
+
+
+
files
-
+
Fichiers à ouvrir au démarrage, optionnel
-
+
[fichiers ...]
-
+
Exécuter les tests unitaires et quitter l'application
-
+
Le fichier de configuration OpenCascade n'existe pas ou non lisible [chemin=%1]
-
+
Le fichier de configuration OpenCascade n'a pu être chargé par QSettings [chemin=%1]
-
+
Échec chargement du fichier de traductions [chemin=%1]
@@ -2391,7 +2548,7 @@ It can be disabled in order to minimize the size of the resulting file.
Export de {} en cours ...
-
+
Échec chargement du fichier de configuration [chemin=%1]
@@ -2402,24 +2559,24 @@ It can be disabled in order to minimize the size of the resulting file.
-
+
Fichier de configuration (format INI) à charger au démarrage
-
+
Mayo le visualiseur et convertisseur 3D pour la CAO
-
-
-
+
+
+
-
+
Exporter des fichiers dans un fichier de sortie, répétable selon les différents formats supportés (par exemple -e file.stp -e file.igs ...)
@@ -2444,12 +2601,12 @@ It can be disabled in order to minimize the size of the resulting file.
Export de %1 en cours ...
-
+
Auncun fichier en entrée -> aucun export
-
+
Impossible de charger le thème '%1'
@@ -2470,69 +2627,59 @@ It can be disabled in order to minimize the size of the resulting file.
-
- Arborescence Modèle
+ Arborescence Modèle
-
- Documents ouverts
+ Documents ouverts
-
- Système de fichiers
+ Système de fichiers
-
- Fermer la barre à gauche
+ Fermer la barre à gauche
-
- X=
+ X=
-
-
-
- ?
+ ?
-
- Y=
+ Y=
-
- Z=
+ Z=
-
+
&Fichier
-
+
&Aide
-
+
&Outils
-
+
F&enêtre
-
+
A&ffichage
@@ -2560,11 +2707,6 @@ It can be disabled in order to minimize the size of the resulting file.
Signaler un bug
-
-
-
-
-
Sauvegarder la vue vers une image
@@ -2655,13 +2797,13 @@ It can be disabled in order to minimize the size of the resulting file.
Selectionner fichier pièce
-
+
Avertissement
-
-
+
+
Erreur
@@ -2698,15 +2840,12 @@ It can be disabled in order to minimize the size of the resulting file.
Temps export : %1ms
-
-
- Données
+ Données
-
- Graphismes
+ Graphismes
@@ -2992,12 +3131,12 @@ It can be disabled in order to minimize the size of the resulting file.
- Form
+ Form
-
+
@@ -3014,65 +3153,224 @@ Modifié le: %3 {1
?}
+
+ Mayo::WidgetGrid
+
+
+
+ Form
+
+
+
+
+ Afficher grille
+
+
+
+
+ Plan : XOY
+
+
+
+
+ Plan : ZOX
+
+
+
+
+ Plan :YOZ
+
+
+
+
+ Plan : personnalisé
+
+
+
+
+ Configuration
+
+
+
+
+
+
+
+
+
+ Rectangulaire
+
+
+
+
+ Circulaire
+
+
+
+
+
+
+
+
+
+
+
+
+ Pas
+
+
+
+
+
+
+
+
+
+
+
+
+ Taille
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Décalage
+
+
+
+
+
+ Origine
+
+
+
+
+ Rayon
+
+
+
+
+ Pas du rayon
+
+
+
+
+
+
+
+
+
+ Graphismes
+
+
+
+
+
+
+
+
+
+
+ Couleur des dizaines
+
+
+
+
+ Mode
+
+
+
+
+ Lignes
+
+
+
+
+ Points
+
+
+
+
+ Couleur
+
+
Mayo::WidgetGuiDocument
-
+
Adapter à tout
-
+
+
+ Éditer la grille
+
+
+
Éditer les plans de coupe
-
+
Éclater l'assemblage
-
+
Mesures
-
+
Isométrique
-
+
Arrière
-
+
Devant
-
+
Gauche
-
+
Droit
-
+
Haut
-
+
Bas
-
+
<b>Click gauche</b> : menu déroulant des vues pré-définies
@@ -3150,12 +3448,86 @@ Lu: %5
+
+ Mayo::WidgetMainControl
+
+
+
+ Form
+
+
+
+
+ Arborescence Modèle
+
+
+
+
+ Documents ouverts
+
+
+
+
+ Système de fichiers
+
+
+
+
+ Fermer le bandeau vertical fixé à gauche
+
+
+
+
+ X=
+
+
+
+
+
+
+ ?
+
+
+
+
+ Y=
+
+
+
+
+ Z=
+
+
+
+
+
+ Données
+
+
+
+
+ Graphismes
+
+
+
+
+ Options
+
+
+
+ Mayo::WidgetMainHome
+
+
+
+ Form
+
+
Mayo::WidgetMeasure
- Form
+ Form
@@ -3170,42 +3542,42 @@ Lu: %5
- Millimètre(mm)
+ Millimètre (mm)
- Centimètre(cm)
+ Centimètre (cm)
- Mètre(m)
+ Mètre (m)
- Pouce(in)
+ Pouce (in)
- Pied(ft)
+ Pied (ft)
-
+ Yard (yd)
- Degré(°)
+ Degré (°)
-
+ Radian (rad)
@@ -3228,47 +3600,52 @@ Lu: %5
Distance min
-
+
+
+ Distance entre les centres
+
+
+
Longueur
-
+
- Millimètre carré(mm²)
+ Millimètre carré (mm²)
-
+
- Centimètre carré(cm²)
+ Centimètre carré (cm²)
-
+
- Mètre carré(m²)
+ Mètre carré (m²)
-
+
- Pouce carré(in²)
+ Pouce carré (in²)
-
+
- Pied carré(ft²)
+ Pied carré (ft²)
-
+
- Yard carré(yd²)
+ Yard carré (yd²)
-
+
-
+
Aire surface
@@ -3303,7 +3680,7 @@ Lu: %5
Unité angle
-
+
Sélectionner les entités à mesurer
@@ -3343,17 +3720,17 @@ Lu: %5
Montrer %1
-
+
Instance
-
+
Produit
-
+
Les Deux
@@ -3541,6 +3918,17 @@ Lu: %5
Binaire
+
+ OccStlWriterI18N
+
+
+ Texte
+
+
+
+ Binaire
+
+
OpenCascade::Aspect_HatchStyle
diff --git a/i18n/messages.cpp b/i18n/messages.cpp
index 2b6956c2..9770e5ac 100644
--- a/i18n/messages.cpp
+++ b/i18n/messages.cpp
@@ -2,45 +2,59 @@ namespace {
double dummy = 0;
}
+#include "src/io_gmio/io_gmio_amf_writer.cpp"
+#include "src/io_image/io_image.cpp"
#include "src/io_occ/io_occ_common.h"
#include "src/io_occ/io_occ_stl.cpp"
+#include "src/io_occ/io_occ_gltf_writer.cpp"
+#include "src/io_ply/io_ply_writer.cpp"
#include "src/app/app_module.h"
#include "src/app/widget_model_tree_builder_xde.h"
-namespace Mayo {
static void messages() {
- AppModule::textId("VeryCoarse");
- AppModule::textId("Coarse");
- AppModule::textId("Normal");
- AppModule::textId("Precise");
- AppModule::textId("VeryPrecise");
- AppModule::textId("UserDefined");
-
- WidgetModelTreeBuilder_Xde::textId("Instance");
- WidgetModelTreeBuilder_Xde::textId("Product");
- WidgetModelTreeBuilder_Xde::textId("Both");
+ // App
+ Mayo::AppModuleProperties::textId("SI");
+ Mayo::AppModuleProperties::textId("ImperialUK");
+
+ Mayo::AppModuleProperties::textId("VeryCoarse");
+ Mayo::AppModuleProperties::textId("Coarse");
+ Mayo::AppModuleProperties::textId("Normal");
+ Mayo::AppModuleProperties::textId("Precise");
+ Mayo::AppModuleProperties::textId("VeryPrecise");
+ Mayo::AppModuleProperties::textId("UserDefined");
+
+ Mayo::WidgetModelTreeBuilder_Xde::textId("Instance");
+ Mayo::WidgetModelTreeBuilder_Xde::textId("Product");
+ Mayo::WidgetModelTreeBuilder_Xde::textId("Both");
+
+ // I/O
+ Mayo::IO::GmioAmfWriter::Properties::textId("Decimal");
+ Mayo::IO::GmioAmfWriter::Properties::textId("Scientific");
+ Mayo::IO::GmioAmfWriter::Properties::textId("Shortest");
+
+ Mayo::IO::OccCommon::textId("Undefined"); // RWMesh_CoordinateSystem_Undefined
+ Mayo::IO::OccCommon::textId("posYfwd_posZup"); // RWMesh_CoordinateSystem_Zup
+ Mayo::IO::OccCommon::textId("negZfwd_posYup"); // RWMesh_CoordinateSystem_Yup
+
+ Mayo::IO::OccCommon::textId("Undefined");
+ Mayo::IO::OccCommon::textId("Micrometer");
+ Mayo::IO::OccCommon::textId("Millimeter");
+ Mayo::IO::OccCommon::textId("Centimeter");
+ Mayo::IO::OccCommon::textId("Meter");
+ Mayo::IO::OccCommon::textId("Kilometer");
+ Mayo::IO::OccCommon::textId("Inch");
+ Mayo::IO::OccCommon::textId("Foot");
+ Mayo::IO::OccCommon::textId("Mile");
+
+ Mayo::IO::OccStlWriterI18N::textId("Ascii");
+ Mayo::IO::OccStlWriterI18N::textId("Binary");
+
+ Mayo::IO::OccGltfWriter::Properties::textId("Json");
+ Mayo::IO::OccGltfWriter::Properties::textId("Binary");
+
+ Mayo::IO::PlyWriterI18N::textId("Ascii");
+ Mayo::IO::PlyWriterI18N::textId("Binary");
+
+ Mayo::IO::ImageWriterI18N::textId("Perspective");
+ Mayo::IO::ImageWriterI18N::textId("Orthographic");
}
-
-namespace IO {
-
-static void messages() {
- OccCommon::textId("Undefined"); // RWMesh_CoordinateSystem_Undefined
- OccCommon::textId("posYfwd_posZup"); // RWMesh_CoordinateSystem_Zup
- OccCommon::textId("negZfwd_posYup"); // RWMesh_CoordinateSystem_Yup
-
- OccCommon::textId("Undefined");
- OccCommon::textId("Micrometer");
- OccCommon::textId("Millimeter");
- OccCommon::textId("Centimeter");
- OccCommon::textId("Meter");
- OccCommon::textId("Kilometer");
- OccCommon::textId("Inch");
- OccCommon::textId("Foot");
- OccCommon::textId("Mile");
-
- OccStlWriter::Properties::textId("Ascii");
- OccStlWriter::Properties::textId("Binary");
-}
-
-} // namespace IO
-} // namespace Mayo
diff --git a/images/appicon.icns b/images/appicon.icns
new file mode 100644
index 00000000..9a740155
Binary files /dev/null and b/images/appicon.icns differ
diff --git a/images/themes/classic/grid.svg b/images/themes/classic/grid.svg
new file mode 100644
index 00000000..c09b16a4
--- /dev/null
+++ b/images/themes/classic/grid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/themes/dark/grid.svg b/images/themes/dark/grid.svg
new file mode 100644
index 00000000..c09b16a4
--- /dev/null
+++ b/images/themes/dark/grid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/mayo.pro b/mayo.pro
index 770c796f..a21b22d9 100644
--- a/mayo.pro
+++ b/mayo.pro
@@ -14,6 +14,9 @@ CONFIG(debug, debug|release) {
}
message(Qt version $$QT_VERSION)
+!versionAtLeast(QT_VERSION, 5.14) {
+ error(Qt >= 5.14 is required but detected version is $$QT_VERSION)
+}
QT += core gui widgets
greaterThan(QT_MAJOR_VERSION, 5) {
@@ -33,7 +36,7 @@ DEFINES += \
QT_IMPLICIT_QFILEINFO_CONSTRUCTION \
release_with_debuginfo:msvc {
- # https://docs.microsoft.com/en-us/cpp/build/reference/how-to-debug-a-release-build
+ # https://learn.microsoft.com/en-us/cpp/build/how-to-debug-a-release-build?view=msvc-170
QMAKE_CXXFLAGS_RELEASE += /Zi
QMAKE_LFLAGS_RELEASE += /DEBUG /INCREMENTAL:NO /OPT:REF /OPT:ICF
}
@@ -61,6 +64,7 @@ macx {
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.15
LIBS += -liconv
# QMAKE_CXXFLAGS += -mmacosx-version-min=10.15
+ ICON = images/appicon.icns
}
win32 {
LIBS += -lOpengl32 -lUser32
@@ -109,11 +113,25 @@ RC_ICONS = images/appicon.ico
OTHER_FILES += \
README.md \
- appveyor.yml \
- .github/workflows/ci.yml \
+ .github/workflows/ci_linux.yml \
+ .github/workflows/ci_macos.yml \
+ .github/workflows/ci_windows.yml \
images/credits.txt \
scripts/bump-version.rb \
+# Embed qtbase_*.qm files as a resource
+qtBaseQmRes.files = \
+ $$[QT_INSTALL_TRANSLATIONS]/qtbase_en.qm \
+ $$[QT_INSTALL_TRANSLATIONS]/qtbase_fr.qm
+qtBaseQmRes.base = $$[QT_INSTALL_TRANSLATIONS]
+qtBaseQmRes.prefix = "/i18n"
+RESOURCES += qtBaseQmRes
+
+# Optional developer-specific QMake pri file for environment related settings
+exists($$PWD/env.pri) {
+ include($$PWD/env.pri)
+}
+
# OpenCascade
include(opencascade.pri)
!isEmpty(OCC_VERSION_STR) {
@@ -155,7 +173,7 @@ LIBS += \
-lTKXmlXCAF \
-lTKXSBase \
-minOpenCascadeVersion(7, 7, 0) {
+versionAtLeast(OCC_VERSION_STR, 7.7.0) {
LIBS += -lTKXDE
}
@@ -166,26 +184,62 @@ LIBS += -lTKSTEP -lTKSTEP209 -lTKSTEPAttr -lTKSTEPBase -lTKXDESTEP
# -- STL support
LIBS += -lTKSTL
# -- OBJ/glTF support
-minOpenCascadeVersion(7, 4, 0) {
+versionAtLeast(OCC_VERSION_STR, 7.4.0) {
LIBS += -lTKRWMesh
} else {
SOURCES -= \
src/io_occ/io_occ_base_mesh.cpp \
src/io_occ/io_occ_gltf_reader.cpp \
src/io_occ/io_occ_obj_reader.cpp
+
+ message(glTF reader disabled because OpenCascade < v7.4)
+ message(OBJ reader disabled because OpenCascade < v7.4)
}
-!minOpenCascadeVersion(7, 5, 0) {
+!versionAtLeast(OCC_VERSION_STR, 7.5.0) {
SOURCES -= src/io_occ/io_occ_gltf_writer.cpp
+ message(glTF writer disabled because OpenCascade < v7.5)
}
-!minOpenCascadeVersion(7, 6, 0) {
+!versionAtLeast(OCC_VERSION_STR, 7.6.0) {
SOURCES -= src/io_occ/io_occ_obj_writer.cpp
+ message(OBJ writer disabled because OpenCascade < v7.6)
}
# -- VRML support
LIBS += -lTKVRML
-!minOpenCascadeVersion(7, 7, 0) {
+!versionAtLeast(OCC_VERSION_STR, 7.7.0) {
SOURCES -= src/io_occ/io_occ_vrml_reader.cpp
+ message(VRML reader disabled because OpenCascade < v7.7)
+}
+
+# assimp
+isEmpty(ASSIMP_INC_DIR) | isEmpty(ASSIMP_LIB_DIR) {
+ message(assimp OFF)
+} else {
+ !versionAtLeast(OCC_VERSION_STR, 7.5.0) {
+ message(assimp reader disabled because OpenCascade < v7.5)
+ }
+ else {
+ message(assimp ON)
+ ASSIMP_IS_ON = 1
+ }
+}
+
+defined(ASSIMP_IS_ON, var) {
+ HEADERS += $$files(src/io_assimp/*.h)
+ SOURCES += $$files(src/io_assimp/*.cpp)
+
+ ASSIMP_VERSION_FILE_CONTENTS = $$cat($$ASSIMP_INC_DIR/version.h, lines)
+ ASSIMP_aiGetVersionPatch = $$find(ASSIMP_VERSION_FILE_CONTENTS, aiGetVersionPatch)
+ !isEmpty(ASSIMP_aiGetVersionPatch) {
+ } else {
+ DEFINES += NO_ASSIMP_aiGetVersionPatch
+ message(Assimp function aiGetVersionPatch() not available)
+ }
+
+ INCLUDEPATH += $$ASSIMP_INC_DIR/..
+ LIBS += -L$$ASSIMP_LIB_DIR -lassimp$$ASSIMP_LIBNAME_SUFFIX
+ DEFINES += HAVE_ASSIMP
}
# gmio
@@ -205,10 +259,7 @@ isEmpty(GMIO_ROOT) {
INCLUDEPATH += $$GMIO_ROOT/include
LIBS += -L$$GMIO_ROOT/lib -lgmio_static -lzlibstatic
- SOURCES += \
-# $$GMIO_ROOT/src/gmio_support/stl_occ_brep.cpp \
-# $$GMIO_ROOT/src/gmio_support/stl_occ_polytri.cpp \
- $$GMIO_ROOT/src/gmio_support/stream_qt.cpp
+ SOURCES += $$GMIO_ROOT/src/gmio_support/stream_qt.cpp
DEFINES += HAVE_GMIO
}
@@ -218,7 +269,7 @@ CONFIG(withtests) {
DEFINES += MAYO_WITH_TESTS
}
-# Developer custom processing
+# Optional developer-specific QMake pri file for custom processing
exists($$PWD/custom.pri) {
include($$PWD/custom.pri)
}
diff --git a/mayo.qrc b/mayo.qrc
index aecced61..b446ce4f 100644
--- a/mayo.qrc
+++ b/mayo.qrc
@@ -82,5 +82,7 @@
images/themes/classic/turn-cw.svg
images/themes/dark/turn-ccw.svg
images/themes/dark/turn-cw.svg
+ images/themes/classic/grid.svg
+ images/themes/dark/grid.svg
diff --git a/opencascade.pri b/opencascade.pri
index 76544374..18feb540 100644
--- a/opencascade.pri
+++ b/opencascade.pri
@@ -47,35 +47,8 @@ equals(QT_ARCH, i386) {
} else:equals(QT_ARCH, x86_64) {
DEFINES += _OCC64
} else {
- warning(Platform architecture may be not supported (QT_ARCH = $$QT_ARCH))
+ warning(Platform architecture may be not supported(QT_ARCH = $$QT_ARCH))
}
LIBS += $$system_path($$join(CASCADE_LIB_DIR, " -L", -L))
QMAKE_RPATHDIR += $$CASCADE_LIB_DIR
-
-defineTest(minOpenCascadeVersion) {
- maj = $$1
- min = $$2
- patch = $$3
- isEqual(OCC_VERSION_MAJOR, $$maj) {
- isEqual(OCC_VERSION_MINOR, $$min) {
- isEqual(OCC_VERSION_PATCH, $$patch) {
- return(true)
- }
-
- greaterThan(OCC_VERSION_PATCH, $$patch) {
- return(true)
- }
- }
-
- greaterThan(OCC_VERSION_MINOR, $$min) {
- return(true)
- }
- }
-
- greaterThan(OCC_VERSION_MAJOR, $$maj) {
- return(true)
- }
-
- return(false)
-}
diff --git a/scripts/bump-version.rb b/scripts/bump-version.rb
index 13a54c90..dadfb5e5 100644
--- a/scripts/bump-version.rb
+++ b/scripts/bump-version.rb
@@ -33,10 +33,3 @@
version_pri.sub!(/(MAYO_VERSION_PAT\s*=\s*)\d+/, "\\1#{patch}")
File.open(path_version_pri, "w").write(version_pri)
puts "Bumped #{path_version_pri}"
-
-# ../appveyor.yml
-path_appveyor_yml = "#{script_dir_name}/../appveyor.yml"
-appveyor_yml = File.open(path_appveyor_yml, "r").read
-appveyor_yml.sub!(/(version\s*:\s*)\d+\.\d+/, "\\1#{major}.#{minor}")
-File.open(path_appveyor_yml, "w").write(appveyor_yml)
-puts "Bumped #{path_appveyor_yml}"
diff --git a/src/app/app_context.cpp b/src/app/app_context.cpp
index a4778116..8cecc24e 100644
--- a/src/app/app_context.cpp
+++ b/src/app/app_context.cpp
@@ -9,16 +9,24 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "widget_gui_document.h"
+#include "widget_main_control.h"
+#include "widget_main_home.h"
+
+#include
namespace Mayo {
AppContext::AppContext(MainWindow* wnd)
- : m_wnd(wnd)
+ : IAppContext(wnd),
+ m_wnd(wnd)
{
+ assert(m_wnd != nullptr);
+ assert(m_wnd->widgetPageDocuments() != nullptr);
+
QObject::connect(
- m_wnd->m_ui->combo_GuiDocuments, qOverload(&QComboBox::currentIndexChanged),
+ m_wnd->widgetPageDocuments(), &WidgetMainControl::currentDocumentIndexChanged,
this, &AppContext::onCurrentDocumentIndexChanged
- );
+ );
}
GuiApplication* AppContext::guiApp() const
@@ -31,25 +39,43 @@ TaskManager* AppContext::taskMgr() const
return &m_wnd->m_taskMgr;
}
+QWidget* AppContext::pageDocuments_widgetLeftSideBar() const
+{
+ const WidgetMainControl* pageDocs = m_wnd->widgetPageDocuments();
+ return pageDocs ? pageDocs->widgetLeftSideBar() : nullptr;
+}
+
QWidget* AppContext::widgetMain() const
{
return m_wnd;
}
-QWidget* AppContext::widgetLeftSidebar() const
+QWidget* AppContext::widgetPage(Page page) const
{
- return m_wnd->m_ui->widget_Left;
+ if (page == Page::Home)
+ return m_wnd->widgetPageHome();
+ else if (page == Page::Documents)
+ return m_wnd->widgetPageDocuments();
+ else
+ return nullptr;
}
-IAppContext::ModeWidgetMain AppContext::modeWidgetMain() const
+IAppContext::Page AppContext::currentPage() const
{
auto widget = m_wnd->m_ui->stack_Main->currentWidget();
- if (widget == m_wnd->m_ui->page_MainHome)
- return ModeWidgetMain::Home;
- else if (widget == m_wnd->m_ui->page_MainControl)
- return ModeWidgetMain::Documents;
+ if (widget == m_wnd->widgetPageHome())
+ return Page::Home;
+ else if (widget == m_wnd->widgetPageDocuments())
+ return Page::Documents;
+
+ return Page::Unknown;
+}
- return ModeWidgetMain::Unknown;
+void AppContext::setCurrentPage(Page page)
+{
+ QWidget* widgetPage = this->widgetPage(page);
+ assert(widgetPage);
+ m_wnd->m_ui->stack_Main->setCurrentWidget(widgetPage);
}
V3dViewController* AppContext::v3dViewController(const GuiDocument* guiDoc) const
@@ -72,14 +98,14 @@ int AppContext::findDocumentIndex(Document::Identifier docId) const
Document::Identifier AppContext::findDocumentFromIndex(int index) const
{
- auto widgetDoc = m_wnd->widgetGuiDocument(index);
+ auto widgetDoc = this->widgetGuiDocument(index);
return widgetDoc ? widgetDoc->documentIdentifier() : -1;
}
Document::Identifier AppContext::currentDocument() const
{
- const int index = m_wnd->m_ui->combo_GuiDocuments->currentIndex();
- auto widgetDoc = m_wnd->widgetGuiDocument(index);
+ const int index = m_wnd->widgetPageDocuments()->currentDocumentIndex();
+ auto widgetDoc = this->widgetGuiDocument(index);
return widgetDoc ? widgetDoc->documentIdentifier() : -1;
}
@@ -88,8 +114,8 @@ void AppContext::setCurrentDocument(Document::Identifier docId)
auto widgetDoc = this->findWidgetGuiDocument([=](WidgetGuiDocument* widgetDoc) {
return widgetDoc->documentIdentifier() == docId;
});
- const int docIndex = m_wnd->m_ui->stack_GuiDocuments->indexOf(widgetDoc);
- m_wnd->m_ui->combo_GuiDocuments->setCurrentIndex(docIndex);
+ const int docIndex = m_wnd->widgetPageDocuments()->indexOfWidgetGuiDocument(widgetDoc);
+ m_wnd->widgetPageDocuments()->setCurrentDocumentIndex(docIndex);
}
void AppContext::updateControlsEnabledStatus()
@@ -99,20 +125,22 @@ void AppContext::updateControlsEnabledStatus()
void AppContext::deleteDocumentWidget(const DocumentPtr& doc)
{
- QWidget* widgetDoc = this->findWidgetGuiDocument([&](WidgetGuiDocument* widgetDoc) {
+ auto widgetDoc = this->findWidgetGuiDocument([&](WidgetGuiDocument* widgetDoc) {
return widgetDoc->documentIdentifier() == doc->identifier();
});
- if (widgetDoc) {
- m_wnd->m_ui->stack_GuiDocuments->removeWidget(widgetDoc);
- widgetDoc->deleteLater();
- }
+ m_wnd->widgetPageDocuments()->removeWidgetGuiDocument(widgetDoc);
+}
+
+WidgetGuiDocument* AppContext::widgetGuiDocument(int idx) const
+{
+ return m_wnd->widgetPageDocuments()->widgetGuiDocument(idx);
}
WidgetGuiDocument* AppContext::findWidgetGuiDocument(std::function fn) const
{
- const int widgetCount = m_wnd->m_ui->stack_GuiDocuments->count();
+ const int widgetCount = m_wnd->widgetPageDocuments()->widgetGuiDocumentCount();
for (int i = 0; i < widgetCount; ++i) {
- auto candidate = m_wnd->widgetGuiDocument(i);
+ auto candidate = this->widgetGuiDocument(i);
if (candidate && fn(candidate))
return candidate;
}
@@ -122,7 +150,7 @@ WidgetGuiDocument* AppContext::findWidgetGuiDocument(std::functionwidgetGuiDocument(docIndex);
+ auto widgetDoc = this->widgetGuiDocument(docIndex);
emit this->currentDocumentChanged(widgetDoc ? widgetDoc->documentIdentifier() : -1);
}
diff --git a/src/app/app_context.h b/src/app/app_context.h
index 583ef5e4..942011bd 100644
--- a/src/app/app_context.h
+++ b/src/app/app_context.h
@@ -20,11 +20,14 @@ class AppContext : public IAppContext {
GuiApplication* guiApp() const override;
TaskManager* taskMgr() const override;
- QWidget* widgetMain() const override;
- QWidget* widgetLeftSidebar() const override;
- ModeWidgetMain modeWidgetMain() const override;
V3dViewController* v3dViewController(const GuiDocument* guiDoc) const override;
+ QWidget* widgetMain() const override;
+ QWidget* widgetPage(Page page) const override;
+ Page currentPage() const override;
+ void setCurrentPage(Page page) override;
+ QWidget* pageDocuments_widgetLeftSideBar() const override;
+
int findDocumentIndex(Document::Identifier docId) const override;
Document::Identifier findDocumentFromIndex(int index) const override;
@@ -35,6 +38,7 @@ class AppContext : public IAppContext {
void deleteDocumentWidget(const DocumentPtr& doc) override;
private:
+ WidgetGuiDocument* widgetGuiDocument(int idx) const;
WidgetGuiDocument* findWidgetGuiDocument(std::function fn) const;
void onCurrentDocumentIndexChanged(int docIndex);
diff --git a/src/app/app_module.cpp b/src/app/app_module.cpp
index 46e87e49..efa02e97 100644
--- a/src/app/app_module.cpp
+++ b/src/app/app_module.cpp
@@ -38,7 +38,7 @@ AppModule::AppModule()
{
static bool metaTypesRegistered = false;
if (!metaTypesRegistered) {
- qRegisterMetaType("Messenger::MessageType");
+ qRegisterMetaType("MessageType");
metaTypesRegistered = true;
}
@@ -139,7 +139,7 @@ bool AppModule::fromVariant(Property* prop, const Settings::Variant& variant) co
}
}
-void AppModule::emitMessage(Messenger::MessageType msgType, std::string_view text)
+void AppModule::emitMessage(MessageType msgType, std::string_view text)
{
const QString qtext = to_QString(text);
{
diff --git a/src/app/app_module.h b/src/app/app_module.h
index 5fadb7a7..42e02b67 100644
--- a/src/app/app_module.h
+++ b/src/app/app_module.h
@@ -74,7 +74,7 @@ class AppModule :
// Logging
void clearMessageLog();
Span messageLog() const { return m_messageLog; }
- Signal signalMessage;
+ Signal signalMessage;
Signal<> signalMessageLogCleared;
// Recent files
diff --git a/src/app/app_module_properties.cpp b/src/app/app_module_properties.cpp
index a66b597f..e513e7ed 100644
--- a/src/app/app_module_properties.cpp
+++ b/src/app/app_module_properties.cpp
@@ -155,6 +155,9 @@ void AppModuleProperties::IO_bindParameters(const IO::System* ioSystem)
void AppModuleProperties::retranslate()
{
+ // System
+ this->unitSystemSchema.mutableEnumeration().changeTrContext(AppModuleProperties::textIdContext());
+
// Application
this->language.setDescription(
textIdTr("Language used for the application. Change will take effect after application restart"));
diff --git a/src/app/command_system_information.cpp b/src/app/command_system_information.cpp
new file mode 100644
index 00000000..5d149644
--- /dev/null
+++ b/src/app/command_system_information.cpp
@@ -0,0 +1,470 @@
+/****************************************************************************
+** Copyright (c) 2023, Fougue Ltd.
+** All rights reserved.
+** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt
+****************************************************************************/
+
+#include "commands_help.h"
+
+#include "app_module.h"
+#include "qstring_conv.h"
+#include "qtwidgets_utils.h"
+#include "version.h"
+#include "../base/meta_enum.h"
+#include "../base/filepath.h"
+#include "../base/io_system.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// NOTICE for Linux/X11
+// Because of #define conflicts, OpenGL_Context.hxx must be included *before* QtGui/QOpenGLContext
+// It also has to be included *after* QtCore/QTextStream
+// Beware of these limitations when adding/removing inclusion of headers here
+#include
+#include
+
+#include
+#include
+
+#ifdef HAVE_GMIO
+# include
+#endif
+
+#include
+#include
+
+namespace Mayo {
+
+CommandSystemInformation::CommandSystemInformation(IAppContext* context)
+ : Command(context)
+{
+ auto action = new QAction(this);
+ action->setText(Command::tr("System Information..."));
+ this->setAction(action);
+}
+
+void CommandSystemInformation::execute()
+{
+ const QString text = CommandSystemInformation::data();
+
+ auto dlg = new QDialog(this->widgetMain());
+ dlg->setWindowTitle(this->action()->text());
+ dlg->resize(800 * dlg->devicePixelRatioF(), 600 * dlg->devicePixelRatioF());
+
+ auto textEdit = new QPlainTextEdit(dlg);
+ textEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+ textEdit->setReadOnly(true);
+ textEdit->setPlainText(text);
+
+ auto btnBox = new QDialogButtonBox(dlg);
+ btnBox->addButton(QDialogButtonBox::Close);
+ auto btnCopy = btnBox->addButton(Command::tr("Copy to Clipboard"), QDialogButtonBox::ActionRole);
+ QObject::connect(btnCopy, &QAbstractButton::clicked, this, [=]{
+ QGuiApplication::clipboard()->setText(text);
+ });
+ QObject::connect(btnBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject);
+
+ auto layout = new QVBoxLayout(dlg);
+ layout->addWidget(textEdit);
+ layout->addWidget(btnBox);
+
+ QtWidgetsUtils::asyncDialogExec(dlg);
+}
+
+namespace {
+
+QTextStream& operator<<(QTextStream& str, bool v)
+{
+ str << (v ? "true" : "false");
+ return str;
+}
+
+QTextStream& operator<<(QTextStream& str, const QFont& f)
+{
+ str << '"' << f.family() << "\" " << f.pointSize();
+ return str;
+}
+
+QTextStream& operator<<(QTextStream& str, const QSize& sz)
+{
+ str << sz.width() << 'x' << sz.height();
+ return str;
+}
+
+QTextStream& operator<<(QTextStream& str, const QSizeF& sz)
+{
+ str << sz.width() << 'x' << sz.height();
+ return str;
+}
+
+QTextStream& operator<<(QTextStream& str, const QRect& r)
+{
+ str << '(' << r.x() << ", " << r.y() << ", " << r.size() << ")";
+ return str;
+}
+
+QTextStream& operator<<(QTextStream& str, std::string_view sv)
+{
+ str << to_QString(sv);
+ return str;
+}
+
+const char indent[] = " ";
+const char indentx2[] = " ";
+
+std::vector& getLibraryInfos()
+{
+ static std::vector vec;
+ return vec;
+}
+
+// Helper function returning unicode representation of a QChar object in the form "U+NNNN"
+QString toUnicodeHexa(const QChar& ch)
+{
+ QString strHexa;
+ QTextStream ostr(&strHexa);
+ ostr.setNumberFlags(ostr.numberFlags() | QTextStream::UppercaseDigits);
+ ostr.setIntegerBase(16); // Same as Qt::hex
+ ostr << "U+" << qSetFieldWidth(4) << qSetPadChar('0') << static_cast(ch.unicode());
+ return strHexa;
+}
+
+// Helper function returning unicode representation of a QString object in the form "U+NNNNU+PPPP..."
+[[maybe_unused]] QString toUnicodeHexa(const QString& str)
+{
+ QString strHexa;
+ QTextStream ostr(&strHexa);
+ ostr.setNumberFlags(ostr.numberFlags() | QTextStream::UppercaseDigits);
+ ostr.setIntegerBase(16); // Same as Qt::hex
+ for (const QChar& ch : str)
+ ostr << "U+" << qSetFieldWidth(4) << qSetPadChar('0') << static_cast(ch.unicode());
+
+ return strHexa;
+}
+
+} // namespace
+
+static void dumpOpenGlInfo(QTextStream& str)
+{
+ QOpenGLContext qtContext;
+ if (!qtContext.create()) {
+ str << "Unable to create QOpenGLContext object" << '\n';
+ return;
+ }
+
+ QWindow window;
+ window.setSurfaceType(QSurface::OpenGLSurface);
+ window.create();
+ qtContext.makeCurrent(&window);
+
+ OpenGl_Context occContext;
+ if (!occContext.Init()) {
+ str << "Unable to initialize OpenGl_Context object" << '\n';
+ return;
+ }
+
+ TColStd_IndexedDataMapOfStringString dict;
+ occContext.DiagnosticInformation(dict, Graphic3d_DiagnosticInfo_Basic);
+ for (TColStd_IndexedDataMapOfStringString::Iterator it(dict); it.More(); it.Next())
+ str << indent << to_QString(it.Key()) << ": " << to_QString(it.Value()) << '\n';
+
+ str << indent << "MaxDegreeOfAnisotropy: " << occContext.MaxDegreeOfAnisotropy() << '\n'
+ << indent << "MaxDrawBuffers: " << occContext.MaxDrawBuffers() << '\n'
+ << indent << "MaxClipPlanes: " << occContext.MaxClipPlanes() << '\n'
+ << indent << "HasRayTracing: " << occContext.HasRayTracing() << '\n'
+ << indent << "HasRayTracingTextures: " << occContext.HasRayTracingTextures() << '\n'
+ << indent << "HasRayTracingAdaptiveSampling: " << occContext.HasRayTracingAdaptiveSampling() << '\n'
+ << indent << "UseVBO: " << occContext.ToUseVbo() << '\n';
+
+#if OCC_VERSION_HEX >= 0x070400
+ str << indent << "MaxDumpSizeX: " << occContext.MaxDumpSizeX() << '\n'
+ << indent << "MaxDumpSizeY: " << occContext.MaxDumpSizeY() << '\n'
+ << indent << "HasRayTracingAdaptiveSamplingAtomic: " << occContext.HasRayTracingAdaptiveSamplingAtomic() << '\n';
+#endif
+
+#if OCC_VERSION_HEX >= 0x070500
+ str << indent << "HasTextureBaseLevel: " << occContext.HasTextureBaseLevel() << '\n'
+ << indent << "HasSRGB: " << occContext.HasSRGB() << '\n'
+ << indent << "RenderSRGB: " << occContext.ToRenderSRGB() << '\n'
+ << indent << "IsWindowSRGB: " << occContext.IsWindowSRGB() << '\n'
+ << indent << "HasPBR: " << occContext.HasPBR() << '\n';
+#endif
+
+#if OCC_VERSION_HEX >= 0x070700
+ str << indent << "GraphicsLibrary: " << MetaEnum::name(occContext.GraphicsLibrary()) << '\n'
+ << indent << "HasTextureMultisampling: " << occContext.HasTextureMultisampling() << '\n';
+#endif
+}
+
+QString CommandSystemInformation::data()
+{
+ QString strSysInfo;
+ QTextStream ostr(&strSysInfo);
+
+ // Mayo version
+ ostr << '\n'
+ << "Mayo: v" << strVersion
+ << " commit:" << strVersionCommitId
+ << " revnum:" << versionRevisionNumber
+ << " " << QT_POINTER_SIZE * 8 << "bit"
+ << '\n';
+
+ // OS version
+ ostr << '\n' << "OS: " << QSysInfo::prettyProductName()
+ << " [" << QSysInfo::kernelType() << " version " << QSysInfo::kernelVersion() << "]" << '\n'
+ << "Current CPU Architecture: " << QSysInfo::currentCpuArchitecture() << '\n';
+
+ // Qt version
+ ostr << '\n' << QLibraryInfo::build() << " on \"" << QGuiApplication::platformName() << "\" " << '\n'
+ << indent << "QStyle keys: " << QStyleFactory::keys().join("; ") << '\n'
+ << indent << "Image formats(read): " << QImageReader::supportedImageFormats().join(' ') << '\n'
+ << indent << "Image formats(write): " << QImageWriter::supportedImageFormats().join(' ') << '\n';
+
+ // OpenCascade version
+ ostr << '\n' << "OpenCascade: " << OCC_VERSION_STRING_EXT << " (build)" << '\n';
+
+ // Other registered libraries
+ for (const LibraryInfo& libInfo : getLibraryInfos()) {
+ ostr << '\n' << to_QString(libInfo.name) << ": " << to_QString(libInfo.version)
+ << " " << to_QString(libInfo.versionDetails)
+ << '\n';
+ }
+
+ // I/O supported formats
+ {
+ ostr << '\n' << "Import(read) formats:" << '\n' << indent;
+ const IO::System* ioSystem = AppModule::get()->ioSystem();
+ for (IO::Format format : ioSystem->readerFormats())
+ ostr << IO::formatIdentifier(format) << " ";
+
+ ostr << '\n' << "Export(write) formats:" << '\n' << indent;
+ for (IO::Format format : ioSystem->writerFormats())
+ ostr << IO::formatIdentifier(format) << " ";
+
+ ostr << '\n';
+ }
+
+ // Locales
+ ostr << '\n' << "Locale:\n";
+ {
+ const std::locale& stdLoc = AppModule::get()->stdLocale();
+ const auto& numFacet = std::use_facet>(stdLoc);
+ const QChar charDecPnt(numFacet.decimal_point());
+ const QChar char1000Sep(numFacet.thousands_sep());
+ std::string strGrouping;
+ for (char c : numFacet.grouping())
+ strGrouping += std::to_string(static_cast(c)) + " ";
+
+ ostr << indent << "std::locale:" << '\n'
+ << indentx2 << "name: " << to_QString(stdLoc.name()) << '\n'
+ << indentx2 << "numpunct.decimal_point: " << charDecPnt << " " << toUnicodeHexa(charDecPnt) << '\n'
+ << indentx2 << "numpunct.thousands_sep: " << char1000Sep << " " << toUnicodeHexa(char1000Sep) << '\n'
+ << indentx2 << "numpunct.grouping: " << to_QString(strGrouping) << '\n'
+ << indentx2 << "numpunct.truename: " << to_QString(numFacet.truename()) << '\n'
+ << indentx2 << "numpunct.falsename: " << to_QString(numFacet.falsename()) << '\n';
+ const QLocale& qtLoc = AppModule::get()->qtLocale();
+ ostr << indent << "QLocale:" << '\n'
+ << indentx2 << "name: " << qtLoc.name() << '\n'
+ << indentx2 << "language: " << MetaEnum::name(qtLoc.language()) << '\n'
+ << indentx2 << "measurementSytem: " << MetaEnum::name(qtLoc.measurementSystem()) << '\n'
+ << indentx2 << "textDirection: " << MetaEnum::name(qtLoc.textDirection()) << '\n'
+ << indentx2 << "decimalPoint: " << qtLoc.decimalPoint() << " " << toUnicodeHexa(qtLoc.decimalPoint()) << '\n'
+ << indentx2 << "groupSeparator: " << qtLoc.groupSeparator() << " " << toUnicodeHexa(qtLoc.groupSeparator()) << '\n';
+ }
+
+ // C++ StdLib
+ ostr << '\n' << "C++ StdLib:\n";
+ {
+ const QChar dirSeparator(FilePath::preferred_separator);
+ ostr << indent << "std::thread::hardware_concurrency: " << std::thread::hardware_concurrency() << '\n'
+ << indent << "std::filepath::path::preferred_separator: " << dirSeparator << " " << toUnicodeHexa(dirSeparator) << '\n';
+ }
+
+ // OpenGL
+ ostr << '\n' << "OpenGL:" << '\n';
+ dumpOpenGlInfo(ostr);
+
+ // File selectors
+ ostr << '\n' << "File selectors(increasing order of precedence):\n" << indent;
+ for (const QString& selector : QFileSelector().allSelectors())
+ ostr << selector << " ";
+
+ ostr << '\n';
+
+ // Fonts
+ ostr << '\n' << "Fonts:\n";
+ ostr << indent << "General font: " << QFontDatabase::systemFont(QFontDatabase::GeneralFont) << '\n'
+ << indent << "Fixed font: " << QFontDatabase::systemFont(QFontDatabase::FixedFont) << '\n'
+ << indent << "Title font: " << QFontDatabase::systemFont(QFontDatabase::TitleFont) << '\n'
+ << indent << "Smallest font: " << QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont) << '\n';
+
+ // Screens
+ ostr << '\n' << "Screens:\n";
+ {
+ const QList listScreen = QGuiApplication::screens();
+ ostr << indent << "Count: " << listScreen.size() << '\n';
+ for (int i = 0; i < listScreen.size(); ++i) {
+ const QScreen* screen = listScreen.at(i);
+ ostr << indent << "Screen #" << i << '\n';
+ ostr << indentx2 << "name: " << screen->name() << '\n'
+ << indentx2 << "model: " << screen->model() << '\n'
+ << indentx2 << "manufacturer: " << screen->manufacturer() << '\n'
+ << indentx2 << "serialNumber: " << screen->serialNumber() << '\n'
+ << indentx2 << "depth: " << screen->depth() << '\n'
+ << indentx2 << "size: " << screen->size() << '\n'
+ << indentx2 << "availableSize: " << screen->availableSize() << '\n'
+ << indentx2 << "virtualSize: " << screen->virtualSize() << '\n'
+ << indentx2 << "availableVirtualSize: " << screen->availableVirtualSize() << '\n'
+ << indentx2 << "geometry: " << screen->geometry() << '\n'
+ << indentx2 << "availableGeometry: " << screen->availableGeometry() << '\n'
+ << indentx2 << "virtualGeometry: " << screen->virtualGeometry() << '\n'
+ << indentx2 << "availableVirtualGeometry: " << screen->availableVirtualGeometry() << '\n'
+ << indentx2 << "physicalSize: " << screen->physicalSize() << '\n'
+ << indentx2 << "physicalDotsPerInchX: " << screen->physicalDotsPerInchX() << '\n'
+ << indentx2 << "physicalDotsPerInchY: " << screen->physicalDotsPerInchY() << '\n'
+ << indentx2 << "physicalDotsPerInch: " << screen->physicalDotsPerInch() << '\n'
+ << indentx2 << "logicalDotsPerInchX: " << screen->logicalDotsPerInchX() << '\n'
+ << indentx2 << "logicalDotsPerInchY: " << screen->logicalDotsPerInchY() << '\n'
+ << indentx2 << "logicalDotsPerInch: " << screen->logicalDotsPerInch() << '\n'
+ << indentx2 << "devicePixelRatio: " << screen->devicePixelRatio() << '\n'
+ << indentx2 << "logicalDotsPerInch: " << screen->logicalDotsPerInch() << '\n'
+ << indentx2 << "primaryOrientation: " << MetaEnum::name(screen->primaryOrientation()) << '\n'
+ << indentx2 << "orientation: " << MetaEnum::name(screen->orientation()) << '\n'
+ << indentx2 << "nativeOrientation: " << MetaEnum::name(screen->nativeOrientation()) << '\n'
+ << indentx2 << "refreshRate: " << screen->refreshRate() << "Hz" << '\n';
+ }
+ }
+
+ // QGuiApplication
+ ostr << '\n' << "QGuiApplication:\n";
+ ostr << indent << "platformName: " << QGuiApplication::platformName() << '\n'
+ << indent << "desktopFileName: " << QGuiApplication::desktopFileName() << '\n'
+ << indent << "desktopSettingsAware: " << QGuiApplication::desktopSettingsAware() << '\n'
+ << indent << "layoutDirection: " << MetaEnum::name(QGuiApplication::layoutDirection()) << '\n';
+ const QStyleHints* sh = QGuiApplication::styleHints();
+ if (sh) {
+ const auto pwdChar = sh->passwordMaskCharacter();
+ ostr << indent << "styleHints:\n"
+ << indentx2 << "keyboardAutoRepeatRate: " << sh->keyboardAutoRepeatRate() << '\n'
+ << indentx2 << "keyboardInputInterval: " << sh->keyboardInputInterval() << '\n'
+ << indentx2 << "mouseDoubleClickInterval: " << sh->mouseDoubleClickInterval() << '\n'
+ << indentx2 << "mousePressAndHoldInterval: " << sh->mousePressAndHoldInterval() << '\n'
+ << indentx2 << "passwordMaskCharacter: " << pwdChar << " " << toUnicodeHexa(pwdChar) << '\n'
+ << indentx2 << "passwordMaskDelay: " << sh->passwordMaskDelay() << '\n'
+ << indentx2 << "setFocusOnTouchRelease: " << sh->setFocusOnTouchRelease() << '\n'
+ << indentx2 << "showIsFullScreen: " << sh->showIsFullScreen() << '\n'
+ << indentx2 << "showIsMaximized: " << sh->showIsMaximized() << '\n'
+ << indentx2 << "showShortcutsInContextMenus: " << sh->showShortcutsInContextMenus() << '\n'
+ << indentx2 << "startDragDistance: " << sh->startDragDistance() << '\n'
+ << indentx2 << "startDragTime: " << sh->startDragTime() << '\n'
+ << indentx2 << "startDragVelocity: " << sh->startDragVelocity() << '\n'
+ << indentx2 << "useRtlExtensions: " << sh->useRtlExtensions() << '\n'
+ << indentx2 << "tabFocusBehavior: " << MetaEnum::name(sh->tabFocusBehavior()) << '\n'
+ << indentx2 << "singleClickActivation: " << sh->singleClickActivation() << '\n'
+ << indentx2 << "useHoverEffects: " << sh->useHoverEffects() << '\n'
+ << indentx2 << "wheelScrollLines: " << sh->wheelScrollLines() << '\n'
+ << indentx2 << "mouseQuickSelectionThreshold: " << sh->mouseQuickSelectionThreshold() << '\n'
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
+ << indentx2 << "mouseDoubleClickDistance: " << sh->mouseDoubleClickDistance() << '\n'
+ << indentx2 << "touchDoubleTapDistance: " << sh->touchDoubleTapDistance() << '\n'
+#endif
+ ;
+ }
+
+ // Library info
+ ostr << '\n' << "Library info:\n";
+ {
+ auto fnLibInfo = [&](QLibraryInfo::LibraryLocation loc) {
+ const QString locDir = QDir::toNativeSeparators(QLibraryInfo::QLibraryInfo::location(loc));
+ ostr << indent << MetaEnum::name(loc) << ": " << locDir << '\n';
+ };
+ fnLibInfo(QLibraryInfo::PrefixPath);
+ fnLibInfo(QLibraryInfo::HeadersPath);
+ fnLibInfo(QLibraryInfo::LibrariesPath);
+ fnLibInfo(QLibraryInfo::LibraryExecutablesPath);
+ fnLibInfo(QLibraryInfo::BinariesPath);
+ fnLibInfo(QLibraryInfo::PluginsPath);
+ fnLibInfo(QLibraryInfo::ArchDataPath);
+ fnLibInfo(QLibraryInfo::DataPath);
+ fnLibInfo(QLibraryInfo::TranslationsPath);
+ fnLibInfo(QLibraryInfo::SettingsPath);
+ }
+
+ // Standard paths
+ ostr << '\n' << "Standard Paths:\n";
+ {
+ auto fnStdPath = [&](QStandardPaths::StandardLocation loc) {
+ QStringList locValues = QStandardPaths::standardLocations(loc);
+ for (QString& dir : locValues)
+ dir = QDir::toNativeSeparators(dir);
+
+ ostr << indent << MetaEnum::name(loc) << ": " << locValues.join("; ") << '\n';
+ };
+ fnStdPath(QStandardPaths::DocumentsLocation);
+ fnStdPath(QStandardPaths::FontsLocation);
+ fnStdPath(QStandardPaths::ApplicationsLocation);
+ fnStdPath(QStandardPaths::TempLocation);
+ fnStdPath(QStandardPaths::HomeLocation);
+ fnStdPath(QStandardPaths::CacheLocation);
+ fnStdPath(QStandardPaths::GenericCacheLocation);
+ fnStdPath(QStandardPaths::GenericDataLocation);
+ fnStdPath(QStandardPaths::RuntimeLocation);
+ fnStdPath(QStandardPaths::ConfigLocation);
+ fnStdPath(QStandardPaths::GenericConfigLocation);
+ fnStdPath(QStandardPaths::AppDataLocation);
+ fnStdPath(QStandardPaths::AppLocalDataLocation);
+ fnStdPath(QStandardPaths::AppConfigLocation);
+ }
+
+ // Environment
+ ostr << '\n' << "Environment:\n";
+ {
+ const QProcessEnvironment sysEnv = QProcessEnvironment::systemEnvironment();
+ for (const QString& key : sysEnv.keys())
+ ostr << indent << key << "=\"" << sysEnv.value(key) << "\"\n";
+ }
+
+ ostr.flush();
+ return strSysInfo;
+}
+
+void CommandSystemInformation::addLibraryInfo(
+ std::string_view libName,
+ std::string_view version,
+ std::string_view versionDetails
+ )
+{
+ if (!libName.empty() && !version.empty()) {
+ const LibraryInfo libInfo{
+ std::string(libName),
+ std::string(version),
+ std::string(versionDetails)
+ };
+ getLibraryInfos().push_back(std::move(libInfo));
+ }
+}
+
+Span CommandSystemInformation::libraryInfos()
+{
+ return getLibraryInfos();
+}
+
+} // namespace Mayo
diff --git a/src/app/commands_api.cpp b/src/app/commands_api.cpp
index 5ed5b40f..d7c1a500 100644
--- a/src/app/commands_api.cpp
+++ b/src/app/commands_api.cpp
@@ -10,6 +10,8 @@
#include "../gui/gui_application.h"
#include
+#include
+#include
namespace Mayo {
@@ -26,7 +28,7 @@ Command::Command(IAppContext* context)
Application* Command::app() const
{
- return m_context->guiApp()->application().get();
+ return m_context ? m_context->guiApp()->application().get() : nullptr;
}
GuiDocument* Command::currentGuiDocument() const
@@ -37,7 +39,7 @@ GuiDocument* Command::currentGuiDocument() const
int Command::currentDocumentIndex() const
{
- return this->context()->findDocumentIndex(this->currentDocument());
+ return m_context ? m_context->findDocumentIndex(this->currentDocument()) : -1;
}
void Command::setCurrentDocument(const DocumentPtr& doc)
@@ -51,4 +53,44 @@ void Command::setAction(QAction* action)
QObject::connect(action, &QAction::triggered, this, &Command::execute);
}
+CommandContainer::CommandContainer(IAppContext* appContext)
+ : m_appContext(appContext)
+{
+}
+
+void CommandContainer::setAppContext(IAppContext* appContext)
+{
+ assert(!m_appContext && m_mapCommand.empty());
+ m_appContext = appContext;
+}
+
+Command* CommandContainer::findCommand(std::string_view name) const
+{
+ auto it = m_mapCommand.find(name);
+ return it != m_mapCommand.cend() ? it->second : nullptr;
+}
+
+QAction* CommandContainer::findCommandAction(std::string_view name) const
+{
+ auto cmd = this->findCommand(name);
+ return cmd ? cmd->action() : nullptr;
+}
+
+void CommandContainer::clear()
+{
+ for (auto [name, cmd] : m_mapCommand) {
+ delete cmd;
+ }
+
+ m_mapCommand.clear();
+}
+
+void CommandContainer::addCommand_impl(std::string_view name, Command* cmd)
+{
+ assert(m_appContext != nullptr);
+ auto [it, ok] = m_mapCommand.insert({ name, cmd });
+ if (!ok)
+ throw std::invalid_argument(fmt::format("Command name {} already exists", name));
+}
+
} // namespace Mayo
diff --git a/src/app/commands_api.h b/src/app/commands_api.h
index 755ce7e1..1b70fdcd 100644
--- a/src/app/commands_api.h
+++ b/src/app/commands_api.h
@@ -12,6 +12,7 @@
#include
#include // WARNING Qt5 / Qt6
+#include
class QWidget;
namespace Mayo {
@@ -22,21 +23,26 @@ class GuiDocument;
class V3dViewController;
class TaskManager;
+class Command;
+
// Provides interface to access/interact with application
class IAppContext : public QObject {
Q_OBJECT
public:
- enum class ModeWidgetMain { Unknown, Home, Documents };
+ enum class Page { Unknown, Home, Documents };
IAppContext(QObject* parent = nullptr);
virtual GuiApplication* guiApp() const = 0;
virtual TaskManager* taskMgr() const = 0;
- virtual QWidget* widgetMain() const = 0;
- virtual QWidget* widgetLeftSidebar() const = 0;
- virtual ModeWidgetMain modeWidgetMain() const = 0;
virtual V3dViewController* v3dViewController(const GuiDocument* guiDoc) const = 0;
+ virtual QWidget* widgetMain() const = 0;
+ virtual QWidget* widgetPage(Page page) const = 0;
+ virtual Page currentPage() const = 0;
+ virtual void setCurrentPage(Page page) = 0;
+ virtual QWidget* pageDocuments_widgetLeftSideBar() const = 0;
+
virtual Document::Identifier currentDocument() const = 0;
virtual void setCurrentDocument(Document::Identifier docId) = 0;
@@ -67,10 +73,10 @@ class Command : public QObject {
protected:
Application* app() const;
- GuiApplication* guiApp() const { return m_context->guiApp(); }
- TaskManager* taskMgr() const { return m_context->taskMgr(); }
- QWidget* widgetMain() const { return m_context->widgetMain(); }
- Document::Identifier currentDocument() const { return m_context->currentDocument(); }
+ GuiApplication* guiApp() const { return m_context ? m_context->guiApp() : nullptr; }
+ TaskManager* taskMgr() const { return m_context ? m_context->taskMgr() : nullptr; }
+ QWidget* widgetMain() const { return m_context ? m_context->widgetMain() : nullptr; }
+ Document::Identifier currentDocument() const { return m_context ? m_context->currentDocument() : -1; }
GuiDocument* currentGuiDocument() const;
int currentDocumentIndex() const;
@@ -82,4 +88,72 @@ class Command : public QObject {
QAction* m_action = nullptr;
};
+// Provides an associative container dedicated to Command objects
+// Each command in the container is mapped to a unique identifier(ie the "command name")
+class CommandContainer {
+public:
+ CommandContainer() = default;
+ CommandContainer(IAppContext* appContext);
+
+ IAppContext* appContext() const { return m_appContext; }
+ void setAppContext(IAppContext* appContext);
+
+ template void foreachCommand(Function fn);
+
+ // Returns the Command object mapped to 'name'
+ // That object was previously created and associated with a call to addCommand()/addNamedCommand()
+ // Might return null in case no command is mapped to 'name'
+ Command* findCommand(std::string_view name) const;
+
+ // Helper function to retrieve the action provided by the Command object mapped to 'name'
+ // Might return null in case no command is mapped to 'name'
+ QAction* findCommandAction(std::string_view name) const;
+
+ // Construct and add new Command object with arguments 'args'
+ // The command is associated to identigfier 'name' and can be retrieved later on with findCommand()
+ template CmdType* addCommand(std::string_view name, Args... p);
+
+ // Same behavior as addCommand() function
+ // The command name is implicit and found by assuming the presence of CmdType::Name class member
+ template CmdType* addNamedCommand(Args... p);
+
+ void clear();
+
+private:
+ void addCommand_impl(std::string_view name, Command* cmd);
+
+ IAppContext* m_appContext = nullptr;
+ std::unordered_map m_mapCommand;
+};
+
+
+
+// --
+// -- Implementation
+// --
+
+template
+void CommandContainer::foreachCommand(Function fn)
+{
+ for (auto [name, cmd] : m_mapCommand) {
+ fn(name, cmd);
+ }
+}
+
+template
+CmdType* CommandContainer::addCommand(std::string_view name, Args... p)
+{
+ auto cmd = new CmdType(m_appContext, p...);
+ this->addCommand_impl(name, cmd);
+ return cmd;
+}
+
+template
+CmdType* CommandContainer::addNamedCommand(Args... p)
+{
+ auto cmd = new CmdType(m_appContext, p...);
+ this->addCommand_impl(CmdType::Name, cmd);
+ return cmd;
+}
+
} // namespace Mayo
diff --git a/src/app/commands_display.cpp b/src/app/commands_display.cpp
index 1b021a6b..9cdea974 100644
--- a/src/app/commands_display.cpp
+++ b/src/app/commands_display.cpp
@@ -23,8 +23,19 @@
namespace Mayo {
-CommandChangeProjection::CommandChangeProjection(IAppContext* context)
+BaseCommandDisplay::BaseCommandDisplay(IAppContext* context)
: Command(context)
+{
+}
+
+bool BaseCommandDisplay::getEnabledStatus() const
+{
+ return this->app()->documentCount() != 0
+ && this->context()->currentPage() == IAppContext::Page::Documents;
+}
+
+CommandChangeProjection::CommandChangeProjection(IAppContext* context)
+ : BaseCommandDisplay(context)
{
m_actionOrtho = new QAction(Command::tr("Orthographic"), this);
m_actionPersp = new QAction(Command::tr("Perspective"), this);
@@ -67,11 +78,6 @@ void CommandChangeProjection::execute()
{
}
-bool CommandChangeProjection::getEnabledStatus() const
-{
- return this->app()->documentCount() != 0;
-}
-
void CommandChangeProjection::onCurrentDocumentChanged()
{
const GuiDocument* guiDoc = this->currentGuiDocument();
@@ -89,7 +95,7 @@ void CommandChangeProjection::onCurrentDocumentChanged()
}
CommandChangeDisplayMode::CommandChangeDisplayMode(IAppContext* context)
- : Command(context)
+ : BaseCommandDisplay(context)
{
auto action = new QAction(this);
action->setText(Command::tr("Mode"));
@@ -109,11 +115,6 @@ void CommandChangeDisplayMode::execute()
{
}
-bool CommandChangeDisplayMode::getEnabledStatus() const
-{
- return this->app()->documentCount() != 0;
-}
-
void CommandChangeDisplayMode::recreateMenuDisplayMode()
{
QMenu* menu = this->action()->menu();
@@ -156,7 +157,7 @@ void CommandChangeDisplayMode::recreateMenuDisplayMode()
}
CommandToggleOriginTrihedron::CommandToggleOriginTrihedron(IAppContext* context)
- : Command(context)
+ : BaseCommandDisplay(context)
{
auto action = new QAction(this);
action->setText(Command::tr("Show Origin Trihedron"));
@@ -180,11 +181,6 @@ void CommandToggleOriginTrihedron::execute()
}
}
-bool CommandToggleOriginTrihedron::getEnabledStatus() const
-{
- return this->app()->documentCount() != 0;
-}
-
void CommandToggleOriginTrihedron::onCurrentDocumentChanged()
{
GuiDocument* guiDoc = this->currentGuiDocument();
@@ -199,7 +195,7 @@ void CommandToggleOriginTrihedron::onCurrentDocumentChanged()
}
CommandTogglePerformanceStats::CommandTogglePerformanceStats(IAppContext* context)
- : Command(context)
+ : BaseCommandDisplay(context)
{
auto action = new QAction(this);
action->setText(Command::tr("Show Performance Stats"));
@@ -218,16 +214,11 @@ void CommandTogglePerformanceStats::execute()
{
GuiDocument* guiDoc = this->currentGuiDocument();
if (guiDoc) {
- CppUtils::toggle(guiDoc->v3dView()->ChangeRenderingParams().ToShowStats);
+ CppUtils::toggle(guiDoc->graphicsView()->ChangeRenderingParams().ToShowStats);
guiDoc->graphicsView().redraw();
}
}
-bool CommandTogglePerformanceStats::getEnabledStatus() const
-{
- return this->app()->documentCount() != 0;
-}
-
void CommandTogglePerformanceStats::onCurrentDocumentChanged()
{
GuiDocument* guiDoc = this->currentGuiDocument();
@@ -242,7 +233,7 @@ void CommandTogglePerformanceStats::onCurrentDocumentChanged()
}
CommandZoomInCurrentDocument::CommandZoomInCurrentDocument(IAppContext* context)
- : Command(context)
+ : BaseCommandDisplay(context)
{
auto action = new QAction(this);
action->setText(Command::tr("Zoom In"));
@@ -258,13 +249,8 @@ void CommandZoomInCurrentDocument::execute()
ctrl->zoomIn();
}
-bool CommandZoomInCurrentDocument::getEnabledStatus() const
-{
- return this->app()->documentCount() != 0;
-}
-
CommandZoomOutCurrentDocument::CommandZoomOutCurrentDocument(IAppContext* context)
- : Command(context)
+ : BaseCommandDisplay(context)
{
auto action = new QAction(this);
action->setText(Command::tr("Zoom Out"));
@@ -280,13 +266,8 @@ void CommandZoomOutCurrentDocument::execute()
ctrl->zoomOut();
}
-bool CommandZoomOutCurrentDocument::getEnabledStatus() const
-{
- return this->app()->documentCount() != 0;
-}
-
CommandTurnViewCounterClockWise::CommandTurnViewCounterClockWise(IAppContext* context)
- : Command(context)
+ : BaseCommandDisplay(context)
{
auto action = new QAction(this);
action->setText(Command::tr("Turn Counter Clockwise"));
@@ -303,13 +284,8 @@ void CommandTurnViewCounterClockWise::execute()
ctrl->turn(V3d_Z, -increment);
}
-bool CommandTurnViewCounterClockWise::getEnabledStatus() const
-{
- return this->app()->documentCount() != 0;
-}
-
CommandTurnViewClockWise::CommandTurnViewClockWise(IAppContext* context)
- : Command(context)
+ : BaseCommandDisplay(context)
{
auto action = new QAction(this);
action->setText(Command::tr("Turn Clockwise"));
@@ -326,9 +302,4 @@ void CommandTurnViewClockWise::execute()
ctrl->turn(V3d_Z, increment);
}
-bool CommandTurnViewClockWise::getEnabledStatus() const
-{
- return this->app()->documentCount() != 0;
-}
-
} // namespace Mayo
diff --git a/src/app/commands_display.h b/src/app/commands_display.h
index 14dc6d50..7432fffc 100644
--- a/src/app/commands_display.h
+++ b/src/app/commands_display.h
@@ -8,15 +8,24 @@
#include "commands_api.h"
+#include
+
class QMenu;
namespace Mayo {
-class CommandChangeProjection : public Command {
+class BaseCommandDisplay : public Command {
+public:
+ BaseCommandDisplay(IAppContext* context);
+ bool getEnabledStatus() const override;
+};
+
+class CommandChangeProjection : public BaseCommandDisplay {
public:
CommandChangeProjection(IAppContext* context);
void execute() override;
- bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "change-projection";
private:
void onCurrentDocumentChanged();
@@ -24,63 +33,70 @@ class CommandChangeProjection : public Command {
QAction* m_actionPersp = nullptr;
};
-class CommandChangeDisplayMode : public Command {
+class CommandChangeDisplayMode : public BaseCommandDisplay {
public:
CommandChangeDisplayMode(IAppContext* context);
CommandChangeDisplayMode(IAppContext* context, QMenu* containerMenu);
void execute() override;
- bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "change-display-mode";
private:
void recreateMenuDisplayMode();
};
-class CommandToggleOriginTrihedron : public Command {
+class CommandToggleOriginTrihedron : public BaseCommandDisplay {
public:
CommandToggleOriginTrihedron(IAppContext* context);
void execute() override;
- bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "toggle-origin-trihedron";
private:
void onCurrentDocumentChanged();
};
-class CommandTogglePerformanceStats : public Command {
+class CommandTogglePerformanceStats : public BaseCommandDisplay {
public:
CommandTogglePerformanceStats(IAppContext* context);
void execute() override;
- bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "toggle-performance-stats";
private:
void onCurrentDocumentChanged();
};
-class CommandZoomInCurrentDocument : public Command {
+class CommandZoomInCurrentDocument : public BaseCommandDisplay {
public:
CommandZoomInCurrentDocument(IAppContext* context);
void execute() override;
- bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "current-doc-zoom-in";
};
-class CommandZoomOutCurrentDocument : public Command {
+class CommandZoomOutCurrentDocument : public BaseCommandDisplay {
public:
CommandZoomOutCurrentDocument(IAppContext* context);
void execute() override;
- bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "current-doc-zoom-out";
};
-class CommandTurnViewCounterClockWise : public Command {
+class CommandTurnViewCounterClockWise : public BaseCommandDisplay {
public:
CommandTurnViewCounterClockWise(IAppContext* context);
void execute() override;
- bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "current-doc-turn-view-ccw";
};
-class CommandTurnViewClockWise : public Command {
+class CommandTurnViewClockWise : public BaseCommandDisplay {
public:
CommandTurnViewClockWise(IAppContext* context);
void execute() override;
- bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "current-doc-turn-view-cw";
};
} // namespace Mayo
diff --git a/src/app/commands_file.cpp b/src/app/commands_file.cpp
index 133d4d82..e8a77db9 100644
--- a/src/app/commands_file.cpp
+++ b/src/app/commands_file.cpp
@@ -15,6 +15,7 @@
#include "recent_files.h"
#include "theme.h"
+#include
#include
#include
#include
@@ -167,6 +168,7 @@ void FileCommandTools::closeDocument(IAppContext* context, Document::Identifier
void FileCommandTools::openDocumentsFromList(IAppContext* context, Span listFilePath)
{
+ assert(context != nullptr);
auto app = context->guiApp()->application();
auto appModule = AppModule::get();
for (const FilePath& fp : listFilePath) {
@@ -312,7 +314,7 @@ void CommandRecentFiles::recreateEntries()
menu->clear();
int idFile = 0;
auto appModule = AppModule::get();
- const RecentFiles& recentFiles = appModule->properties()->recentFiles.value();
+ const RecentFiles& recentFiles = appModule->properties()->recentFiles;
for (const RecentFile& recentFile : recentFiles) {
const QString strFilePath = filepathTo(recentFile.filepath);
const QString strEntryRecentFile = Command::tr("%1 | %2").arg(++idFile).arg(strFilePath);
@@ -384,7 +386,8 @@ void CommandImportInCurrentDocument::execute()
bool CommandImportInCurrentDocument::getEnabledStatus() const
{
- return this->app()->documentCount() != 0;
+ return this->app()->documentCount() != 0
+ && this->context()->currentPage() == IAppContext::Page::Documents;
}
CommandExportSelectedApplicationItems::CommandExportSelectedApplicationItems(IAppContext* context)
@@ -416,7 +419,8 @@ void CommandExportSelectedApplicationItems::execute()
Command::tr("Select Output File"),
filepathTo(lastSettings.openDir),
listWriterFileFilter.join(QLatin1String(";;")),
- &lastSettings.selectedFilter);
+ &lastSettings.selectedFilter
+ );
if (strFilepath.isEmpty())
return;
@@ -444,7 +448,8 @@ void CommandExportSelectedApplicationItems::execute()
bool CommandExportSelectedApplicationItems::getEnabledStatus() const
{
- return this->app()->documentCount() != 0;
+ return this->app()->documentCount() != 0
+ && this->context()->currentPage() == IAppContext::Page::Documents;
}
CommandCloseCurrentDocument::CommandCloseCurrentDocument(IAppContext* context)
@@ -485,7 +490,7 @@ void CommandCloseCurrentDocument::updateActionText(Document::Identifier docId)
const QString docName = to_QString(docPtr ? docPtr->name() : std::string{});
const QString textActionClose =
docPtr ?
- Command::tr("Close \"%1\"").arg(strFilepathQuoted(docName)) :
+ Command::tr("Close %1").arg(strFilepathQuoted(docName)) :
Command::tr("Close");
this->action()->setText(textActionClose);
this->action()->setToolTip(textActionClose);
@@ -556,7 +561,7 @@ void CommandCloseAllDocumentsExceptCurrent::updateActionText(Document::Identifie
const QString docName = to_QString(docPtr ? docPtr->name() : std::string{});
const QString textActionClose =
docPtr ?
- Command::tr("Close all except \"%1\"").arg(strFilepathQuoted(docName)) :
+ Command::tr("Close all except %1").arg(strFilepathQuoted(docName)) :
Command::tr("Close all except current");
this->action()->setText(textActionClose);
}
diff --git a/src/app/commands_file.h b/src/app/commands_file.h
index bc06dcf7..d984e978 100644
--- a/src/app/commands_file.h
+++ b/src/app/commands_file.h
@@ -23,6 +23,8 @@ class CommandNewDocument : public Command {
public:
CommandNewDocument(IAppContext* context);
void execute() override;
+
+ static constexpr std::string_view Name = "new-doc";
};
class CommandOpenDocuments : public Command {
@@ -30,6 +32,8 @@ class CommandOpenDocuments : public Command {
CommandOpenDocuments(IAppContext* context);
void execute() override;
bool eventFilter(QObject* watched, QEvent* event) override;
+
+ static constexpr std::string_view Name = "open-docs";
};
class CommandRecentFiles : public Command {
@@ -38,6 +42,8 @@ class CommandRecentFiles : public Command {
CommandRecentFiles(IAppContext* context, QMenu* containerMenu);
void execute() override;
void recreateEntries();
+
+ static constexpr std::string_view Name = "recent-files";
};
class CommandImportInCurrentDocument : public Command {
@@ -45,6 +51,8 @@ class CommandImportInCurrentDocument : public Command {
CommandImportInCurrentDocument(IAppContext* context);
void execute() override;
bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "import";
};
class CommandExportSelectedApplicationItems : public Command {
@@ -52,6 +60,8 @@ class CommandExportSelectedApplicationItems : public Command {
CommandExportSelectedApplicationItems(IAppContext* context);
void execute() override;
bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "export";
};
class CommandCloseCurrentDocument : public Command {
@@ -60,6 +70,8 @@ class CommandCloseCurrentDocument : public Command {
void execute() override;
bool getEnabledStatus() const override;
+ static constexpr std::string_view Name = "close-doc";
+
private:
void updateActionText(Document::Identifier docId);
};
@@ -69,6 +81,8 @@ class CommandCloseAllDocuments : public Command {
CommandCloseAllDocuments(IAppContext* context);
void execute() override;
bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "close-all-docs";
};
class CommandCloseAllDocumentsExceptCurrent : public Command {
@@ -77,6 +91,8 @@ class CommandCloseAllDocumentsExceptCurrent : public Command {
void execute() override;
bool getEnabledStatus() const override;
+ static constexpr std::string_view Name = "close-all-docs-except-current";
+
private:
void updateActionText(Document::Identifier docId);
};
@@ -85,6 +101,8 @@ class CommandQuitApplication : public Command {
public:
CommandQuitApplication(IAppContext* context);
void execute() override;
+
+ static constexpr std::string_view Name = "quit-app";
};
} // namespace Mayo
diff --git a/src/app/commands_help.cpp b/src/app/commands_help.cpp
index b7fe8f3f..92cb882e 100644
--- a/src/app/commands_help.cpp
+++ b/src/app/commands_help.cpp
@@ -8,10 +8,12 @@
#include "dialog_about.h"
#include "qtwidgets_utils.h"
+#include "qstring_conv.h"
+#include
#include
#include
-#include
+#include
namespace Mayo {
@@ -32,13 +34,16 @@ CommandAbout::CommandAbout(IAppContext* context)
: Command(context)
{
auto action = new QAction(this);
- action->setText(Command::tr("About %1").arg(QApplication::applicationName()));
+ action->setText(Command::tr("About %1").arg(QCoreApplication::applicationName()));
this->setAction(action);
}
void CommandAbout::execute()
{
auto dlg = new DialogAbout(this->widgetMain());
+ for (const auto& libInfo : CommandSystemInformation::libraryInfos())
+ dlg->addLibraryInfo(libInfo.name, libInfo.version);
+
QtWidgetsUtils::asyncDialogExec(dlg);
}
diff --git a/src/app/commands_help.h b/src/app/commands_help.h
index ca880669..23c49a92 100644
--- a/src/app/commands_help.h
+++ b/src/app/commands_help.h
@@ -7,6 +7,7 @@
#pragma once
#include "commands_api.h"
+#include "../base/span.h"
namespace Mayo {
@@ -14,12 +15,38 @@ class CommandReportBug : public Command {
public:
CommandReportBug(IAppContext* context);
void execute() override;
+
+ static constexpr std::string_view Name = "report-bug";
+};
+
+class CommandSystemInformation : public Command {
+public:
+ CommandSystemInformation(IAppContext* context);
+ void execute() override;
+
+ static QString data();
+
+ struct LibraryInfo {
+ std::string name;
+ std::string version;
+ std::string versionDetails;
+ };
+ static void addLibraryInfo(
+ std::string_view libName,
+ std::string_view version,
+ std::string_view versionDetails = ""
+ );
+ static Span libraryInfos();
+
+ static constexpr std::string_view Name = "system-info";
};
class CommandAbout : public Command {
public:
CommandAbout(IAppContext* context);
void execute() override;
+
+ static constexpr std::string_view Name = "about";
};
} // namespace Mayo
diff --git a/src/app/commands_tools.cpp b/src/app/commands_tools.cpp
index a8d52bb9..fd8af3c3 100644
--- a/src/app/commands_tools.cpp
+++ b/src/app/commands_tools.cpp
@@ -40,7 +40,8 @@ void CommandSaveViewImage::execute()
bool CommandSaveViewImage::getEnabledStatus() const
{
- return this->app()->documentCount() != 0;
+ return this->app()->documentCount() != 0
+ && this->context()->currentPage() == IAppContext::Page::Documents;
}
CommandInspectXde::CommandInspectXde(IAppContext* context)
@@ -77,7 +78,8 @@ bool CommandInspectXde::getEnabledStatus() const
!spanSelectedAppItem.empty() ? spanSelectedAppItem.front() : ApplicationItem();
return spanSelectedAppItem.size() == 1
&& firstAppItem.isValid()
- && firstAppItem.document()->isXCafDocument();
+ && firstAppItem.document()->isXCafDocument()
+ && this->context()->currentPage() == IAppContext::Page::Documents;
}
CommandEditOptions::CommandEditOptions(IAppContext* context)
diff --git a/src/app/commands_tools.h b/src/app/commands_tools.h
index 5e3e803e..9ade0add 100644
--- a/src/app/commands_tools.h
+++ b/src/app/commands_tools.h
@@ -15,6 +15,8 @@ class CommandSaveViewImage : public Command {
CommandSaveViewImage(IAppContext* context);
void execute() override;
bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "save-view-image";
};
class CommandInspectXde : public Command {
@@ -22,12 +24,16 @@ class CommandInspectXde : public Command {
CommandInspectXde(IAppContext* context);
void execute() override;
bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "inspect-xde";
};
class CommandEditOptions : public Command {
public:
CommandEditOptions(IAppContext* context);
void execute() override;
+
+ static constexpr std::string_view Name = "edit-options";
};
} // namespace Mayo
diff --git a/src/app/commands_window.cpp b/src/app/commands_window.cpp
index 971497d2..ad939fbf 100644
--- a/src/app/commands_window.cpp
+++ b/src/app/commands_window.cpp
@@ -48,33 +48,30 @@ CommandLeftSidebarWidgetToggle::CommandLeftSidebarWidgetToggle(IAppContext* cont
action->setToolTip(Command::tr("Show/Hide Left Sidebar"));
action->setShortcut(Qt::ALT + Qt::Key_0);
action->setCheckable(true);
- action->setChecked(context->widgetLeftSidebar()->isVisible());
+ action->setChecked(context->pageDocuments_widgetLeftSideBar()->isVisible());
this->setAction(action);
this->updateAction();
- context->widgetLeftSidebar()->installEventFilter(this);
+ context->pageDocuments_widgetLeftSideBar()->installEventFilter(this);
}
void CommandLeftSidebarWidgetToggle::execute()
{
- const bool isVisible = this->context()->widgetLeftSidebar()->isVisible();
- this->context()->widgetLeftSidebar()->setVisible(!isVisible);
+ QWidget* widget = this->context()->pageDocuments_widgetLeftSideBar();
+ widget->setVisible(!widget->isVisible());
}
bool CommandLeftSidebarWidgetToggle::getEnabledStatus() const
{
- return this->context()->modeWidgetMain() != IAppContext::ModeWidgetMain::Home;
+ return this->context()->currentPage() != IAppContext::Page::Home;
}
bool CommandLeftSidebarWidgetToggle::eventFilter(QObject* watched, QEvent* event)
{
- if (watched == this->context()->widgetLeftSidebar()) {
- if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {
+ if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {
+ if (watched == this->context()->pageDocuments_widgetLeftSideBar()) {
this->updateAction();
return true;
}
- else {
- return false;
- }
}
return Command::eventFilter(watched, event);
@@ -82,7 +79,7 @@ bool CommandLeftSidebarWidgetToggle::eventFilter(QObject* watched, QEvent* event
void CommandLeftSidebarWidgetToggle::updateAction()
{
- if (this->context()->widgetLeftSidebar()->isVisible()) {
+ if (this->context()->pageDocuments_widgetLeftSideBar()->isVisible()) {
this->action()->setText(Command::tr("Hide Left Sidebar"));
this->action()->setIcon(mayoTheme()->icon(Theme::Icon::BackSquare));
}
@@ -94,6 +91,69 @@ void CommandLeftSidebarWidgetToggle::updateAction()
this->action()->setToolTip(this->action()->text());
}
+CommandSwitchMainWidgetMode::CommandSwitchMainWidgetMode(IAppContext* context)
+ : Command(context)
+{
+ auto action = new QAction(this);
+ action->setToolTip(Command::tr("Go To Home Page"));
+ action->setShortcut(Qt::CTRL + Qt::Key_0);
+ this->setAction(action);
+ this->updateAction();
+ context->widgetPage(IAppContext::Page::Home)->installEventFilter(this);
+ context->widgetPage(IAppContext::Page::Documents)->installEventFilter(this);
+}
+
+void CommandSwitchMainWidgetMode::execute()
+{
+ auto newPage = IAppContext::Page::Unknown;
+ switch (this->context()->currentPage()) {
+ case IAppContext::Page::Home:
+ newPage = IAppContext::Page::Documents;
+ break;
+ case IAppContext::Page::Documents:
+ newPage = IAppContext::Page::Home;
+ break;
+ case IAppContext::Page::Unknown:
+ break;
+ }
+
+ this->context()->setCurrentPage(newPage);
+ this->context()->updateControlsEnabledStatus();
+}
+
+bool CommandSwitchMainWidgetMode::getEnabledStatus() const
+{
+ return this->app()->documentCount() != 0;
+}
+
+bool CommandSwitchMainWidgetMode::eventFilter(QObject* watched, QEvent* event)
+{
+ if (event->type() == QEvent::Show) {
+ if (watched == this->context()->widgetPage(IAppContext::Page::Home)
+ || watched == this->context()->widgetPage(IAppContext::Page::Documents))
+ {
+ this->updateAction();
+ return true;
+ }
+ }
+
+ return Command::eventFilter(watched, event);
+}
+
+void CommandSwitchMainWidgetMode::updateAction()
+{
+ switch (this->context()->currentPage()) {
+ case IAppContext::Page::Home:
+ this->action()->setText(Command::tr("Go To Documents"));
+ break;
+ case IAppContext::Page::Documents:
+ this->action()->setText(Command::tr("Go To Home Page"));
+ break;
+ case IAppContext::Page::Unknown:
+ break;
+ }
+}
+
CommandPreviousDocument::CommandPreviousDocument(IAppContext* context)
: Command(context)
{
@@ -113,7 +173,9 @@ void CommandPreviousDocument::execute()
bool CommandPreviousDocument::getEnabledStatus() const
{
- return this->app()->documentCount() != 0 && this->currentDocumentIndex() > 0;
+ return this->app()->documentCount() != 0
+ && this->context()->currentPage() == IAppContext::Page::Documents
+ && this->currentDocumentIndex() > 0;
}
CommandNextDocument::CommandNextDocument(IAppContext* context)
@@ -136,7 +198,9 @@ void CommandNextDocument::execute()
bool CommandNextDocument::getEnabledStatus() const
{
const int appDocumentCount = this->app()->documentCount();
- return appDocumentCount != 0 && this->currentDocumentIndex() < appDocumentCount - 1;
+ return appDocumentCount != 0
+ && this->context()->currentPage() == IAppContext::Page::Documents
+ && this->currentDocumentIndex() < appDocumentCount - 1;
}
} // namespace Mayo
diff --git a/src/app/commands_window.h b/src/app/commands_window.h
index 008ffbc8..1488fdef 100644
--- a/src/app/commands_window.h
+++ b/src/app/commands_window.h
@@ -15,18 +15,39 @@ class CommandMainWidgetToggleFullscreen : public Command {
CommandMainWidgetToggleFullscreen(IAppContext* context);
void execute() override;
+ static constexpr std::string_view Name = "toggle-fullscreen";
+
private:
Qt::WindowStates m_previousWindowState = Qt::WindowNoState;
};
+// Provides command to toggle visibility of the left-side bar widget belonging to
+// the "Documents" main page
class CommandLeftSidebarWidgetToggle : public Command {
public:
CommandLeftSidebarWidgetToggle(IAppContext* context);
+
+ void execute() override;
+ bool getEnabledStatus() const override;
+
+ bool eventFilter(QObject* watched, QEvent* event) override;
+
+ static constexpr std::string_view Name = "toggle-left-sidebar";
+
+private:
+ void updateAction();
+};
+
+class CommandSwitchMainWidgetMode : public Command {
+public:
+ CommandSwitchMainWidgetMode(IAppContext* context);
void execute() override;
bool getEnabledStatus() const override;
bool eventFilter(QObject* watched, QEvent* event) override;
+ static constexpr std::string_view Name = "switch-main-widget-mode";
+
private:
void updateAction();
};
@@ -36,6 +57,8 @@ class CommandPreviousDocument : public Command {
CommandPreviousDocument(IAppContext* context);
void execute() override;
bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "previous-doc";
};
class CommandNextDocument : public Command {
@@ -43,6 +66,8 @@ class CommandNextDocument : public Command {
CommandNextDocument(IAppContext* context);
void execute() override;
bool getEnabledStatus() const override;
+
+ static constexpr std::string_view Name = "next-doc";
};
} // namespace Mayo
diff --git a/src/app/console.cpp b/src/app/console.cpp
index cc70ce10..183cac75 100644
--- a/src/app/console.cpp
+++ b/src/app/console.cpp
@@ -11,7 +11,7 @@
# include
# include
#else
-# include //ioctl() and TIOCGWINSZ
+# include // ioctl() and TIOCGWINSZ
# include // for STDOUT_FILENO
# include
#endif
diff --git a/src/app/dialog_about.cpp b/src/app/dialog_about.cpp
index 4c11e362..86d674bb 100644
--- a/src/app/dialog_about.cpp
+++ b/src/app/dialog_about.cpp
@@ -7,11 +7,9 @@
#include "dialog_about.h"
#include "ui_dialog_about.h"
+#include "qstring_conv.h"
#include "version.h"
#include
-#ifdef HAVE_GMIO
-# include
-#endif
namespace Mayo {
@@ -24,15 +22,13 @@ DialogAbout::DialogAbout(QWidget* parent)
tr("%1 By %2").arg(QApplication::applicationName(), QApplication::organizationName())
);
- m_ui->label_Version->setText(m_ui->label_Version->text().arg(strVersion).arg(QT_POINTER_SIZE * 8));
+ const QString strVersionFull =
+ QString("%1 commit:%2 revnum:%3")
+ .arg(strVersion).arg(strVersionCommitId).arg(versionRevisionNumber);
+ m_ui->label_Version->setText(m_ui->label_Version->text().arg(strVersionFull).arg(QT_POINTER_SIZE * 8));
m_ui->label_BuildDateTime->setText(m_ui->label_BuildDateTime->text().arg(__DATE__, __TIME__));
m_ui->label_Qt->setText(m_ui->label_Qt->text().arg(QT_VERSION_STR));
m_ui->label_Occ->setText(m_ui->label_Occ->text().arg(OCC_VERSION_COMPLETE));
-#ifdef HAVE_GMIO
- m_ui->label_Gmio->setText(m_ui->label_Gmio->text().arg(GMIO_VERSION_STR));
-#else
- m_ui->label_Gmio->hide();
-#endif
}
DialogAbout::~DialogAbout()
@@ -40,4 +36,11 @@ DialogAbout::~DialogAbout()
delete m_ui;
}
+void DialogAbout::addLibraryInfo(std::string_view libName, std::string_view libVersion)
+{
+ auto label = new QLabel(this);
+ label->setText(tr("%1 %2").arg(to_QString(libName), to_QString(libVersion)));
+ m_ui->layout_Infos->addWidget(label);;
+}
+
} // namespace Mayo
diff --git a/src/app/dialog_about.h b/src/app/dialog_about.h
index 34bb8114..86428322 100644
--- a/src/app/dialog_about.h
+++ b/src/app/dialog_about.h
@@ -7,6 +7,7 @@
#pragma once
#include
+#include
namespace Mayo {
@@ -16,6 +17,8 @@ class DialogAbout : public QDialog {
DialogAbout(QWidget* parent = nullptr);
~DialogAbout();
+ void addLibraryInfo(std::string_view libName, std::string_view libVersion);
+
private:
class Ui_DialogAbout* m_ui = nullptr;
};
diff --git a/src/app/dialog_about.ui b/src/app/dialog_about.ui
index 13f1837b..af20ac50 100644
--- a/src/app/dialog_about.ui
+++ b/src/app/dialog_about.ui
@@ -6,8 +6,8 @@
0
0
- 288
- 175
+ 270
+ 160
@@ -35,7 +35,7 @@
- :/images/appicon_64.png
+ :/images/appicon_64.png
Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
@@ -46,7 +46,6 @@
- 75
true
@@ -86,28 +85,21 @@
-
-
-
-
+
+
-
Qt %1
- -
+
-
OpenCascade %1
- -
-
-
- gmio %1
-
-
-
-
@@ -136,7 +128,7 @@
-
+
diff --git a/src/app/dialog_inspect_xde.cpp b/src/app/dialog_inspect_xde.cpp
index 90ea81b6..4bdff451 100644
--- a/src/app/dialog_inspect_xde.cpp
+++ b/src/app/dialog_inspect_xde.cpp
@@ -13,6 +13,7 @@
#include "../base/settings.h"
#include "../base/tkernel_utils.h"
#include "app_module.h"
+#include "filepath_conv.h"
#include "qmeta_tdf_label.h"
#include "qstring_conv.h"
#include "qstring_utils.h"
@@ -120,9 +121,14 @@ static void loadLabelAttributes(const TDF_Label& label, QTreeWidgetItem* treeIte
}
else if (attrId == TNaming_NamedShape::GetID()) {
const auto& namedShape = static_cast(*ptrAttr);
+ const TopoDS_Shape shape = namedShape.Get();
text = "TNaming_NamedShape";
- value = DialogInspectXde::tr("ShapeType=%1, Evolution=%2")
- .arg(MetaEnum::name(namedShape.Get().ShapeType()).data())
+ value = DialogInspectXde::tr("ShapeType=%1, ShapeLocation=%2, Evolution=%3")
+ .arg(MetaEnum::name(shape.ShapeType()).data())
+ .arg(shape.Location().Transformation().Form() != gp_Identity ?
+ QStringUtils::text(shape.Location(), appDefaultTextOptions())
+ : QString("id")
+ )
.arg(MetaEnum::name(namedShape.Evolution()).data());
}
else {
@@ -295,9 +301,15 @@ static void loadLabelMaterialProperties(
// This helper allows "lazy" loading of the image files
class ImageFileTreeWidgetItem : public QTreeWidgetItem {
public:
- void setImageFilePath(int col, const QString& strFilePath)
+ void setImage(int col, const FilePath& filePath)
{
- const ItemData item{strFilePath, {}};
+ const ItemData item{filePath, {}, {}};
+ m_mapColumnItemData.insert({ col, item });
+ }
+
+ void setImage(int col, const QByteArray& data)
+ {
+ const ItemData item{{}, data, {}};
m_mapColumnItemData.insert({ col, item });
}
@@ -312,12 +324,23 @@ class ImageFileTreeWidgetItem : public QTreeWidgetItem {
return {};
if (ptrItem->strToolTip.isEmpty()) {
- const QPixmap pixmap(ptrItem->strFilePath);
+ QPixmap pixmap;
+ uintmax_t imageSize = 0;
+
+ if (!ptrItem->filePath.empty()) {
+ pixmap.load(filepathTo(ptrItem->filePath));
+ imageSize = filepathFileSize(ptrItem->filePath);
+ }
+ else {
+ pixmap.loadFromData(ptrItem->fileData);
+ imageSize = ptrItem->fileData.size();
+ }
+
if (!pixmap.isNull()) {
QBuffer bufferPixmap;
- const QPixmap pixmapClamped = pixmap.scaledToWidth(std::min(pixmap.width(), 400));
+ const int pixmapWidth = std::min(pixmap.width(), int(400 * qGuiApp->devicePixelRatio()));
+ const QPixmap pixmapClamped = pixmap.scaledToWidth(pixmapWidth);
pixmapClamped.save(&bufferPixmap, "PNG");
- const auto imageSize = QFileInfo(ptrItem->strFilePath).size();
const QString strImageSize = QStringUtils::bytesText(imageSize, appDefaultTextOptions().locale);
ptrItem->strToolTip =
QString("%4
")
@@ -325,7 +348,7 @@ class ImageFileTreeWidgetItem : public QTreeWidgetItem {
.arg(pixmapClamped.width())
.arg(pixmapClamped.height())
.arg(DialogInspectXde::tr("File Size: %1
Dimensions: %2x%3 Depth: %4")
- .arg(strImageSize).arg(pixmap.width()).arg(pixmap.height()).arg(pixmap.depth()))
+ .arg(strImageSize).arg(pixmap.width()).arg(pixmap.height()).arg(pixmap.depth()))
;
}
else {
@@ -338,7 +361,8 @@ class ImageFileTreeWidgetItem : public QTreeWidgetItem {
private:
struct ItemData {
- QString strFilePath;
+ FilePath filePath;
+ QByteArray fileData;
QString strToolTip;
};
@@ -350,11 +374,36 @@ static QTreeWidgetItem* createPropertyTreeItem(const QString& text, const Handle
if (imgTexture.IsNull())
return static_cast(nullptr);
- const QString strTextureFilePath = to_QString(imgTexture->FilePath());
auto item = new ImageFileTreeWidgetItem;
item->setText(0, text);
- item->setText(1, strTextureFilePath);
- item->setImageFilePath(1, strTextureFilePath);
+ if (!imgTexture->FilePath().IsEmpty()) {
+ // Texture is provided through a file reference
+ const FilePath filePath = filepathCanonical(filepathFrom(imgTexture->FilePath()));
+ const QString strFilePath = filepathTo(filePath);
+ const auto fileOffset = imgTexture->FileOffset();
+ if (fileOffset > 0) {
+ // Texture is defined in a file portion
+ item->setText(1, DialogInspectXde::tr("%1,offset:%2").arg(strFilePath).arg(fileOffset));
+ QFile file(strFilePath);
+ if (file.open(QIODevice::ReadOnly)) {
+ file.seek(fileOffset);
+ const QByteArray buff = file.read(imgTexture->FileLength());
+ item->setImage(1, buff);
+ }
+ }
+ else {
+ // Texture is defined in a file
+ item->setText(1, strFilePath);
+ item->setImage(1, filePath);
+ }
+ }
+ else if (imgTexture->DataBuffer() && !imgTexture->DataBuffer()->IsEmpty()) {
+ // Texture is provided by some embedded data
+ item->setText(1, DialogInspectXde::tr(""));
+ const Handle(NCollection_Buffer)& buff = imgTexture->DataBuffer();
+ item->setImage(1, QByteArray::fromRawData(reinterpret_cast(buff->Data()), buff->Size()));
+ }
+
return item;
}
#endif
@@ -378,7 +427,11 @@ static void loadLabelVisMaterialProperties(
item->addChild(createPropertyTreeItem("BaseColor", material->BaseColor()));
item->addChild(createPropertyTreeItem("AlphaMode", MetaEnum::name(material->AlphaMode())));
item->addChild(createPropertyTreeItem("AlphaCutOff", material->AlphaCutOff()));
+#if OCC_VERSION_HEX >= OCC_VERSION_CHECK(7, 6, 0)
+ item->addChild(createPropertyTreeItem("FaceCulling", MetaEnum::name(material->FaceCulling())));
+#else
item->addChild(createPropertyTreeItem("IsDoubleSided", material->IsDoubleSided()));
+#endif
if (!material->RawName().IsNull())
item->addChild(createPropertyTreeItem("RawName", to_QString(material->RawName())));
diff --git a/src/app/dialog_options.cpp b/src/app/dialog_options.cpp
index da01a316..10f147d4 100644
--- a/src/app/dialog_options.cpp
+++ b/src/app/dialog_options.cpp
@@ -251,7 +251,8 @@ DialogOptions::DialogOptions(Settings* settings, QWidget* parent)
const QVariant variantNodeId = current.data(ItemSettingNodeId_Role);
const QModelIndex indexFirst = settingsModel->index(0, 0);
const QModelIndexList indexList = settingsModel->match(
- indexFirst, ItemSettingNodeId_Role, variantNodeId, 1, Qt::MatchExactly);
+ indexFirst, ItemSettingNodeId_Role, variantNodeId, 1, Qt::MatchExactly
+ );
if (!indexList.isEmpty())
m_ui->listWidget_Settings->scrollTo(indexList.front(), QAbstractItemView::PositionAtTop);
});
diff --git a/src/app/gui_document_list_model.cpp b/src/app/gui_document_list_model.cpp
index 5bbd27c3..7ebb5f9c 100644
--- a/src/app/gui_document_list_model.cpp
+++ b/src/app/gui_document_list_model.cpp
@@ -6,6 +6,8 @@
#include "gui_document_list_model.h"
+#include "filepath_conv.h"
+#include "qstring_conv.h"
#include "../base/application.h"
#include "../base/document.h"
#include "../gui/gui_application.h"
@@ -28,18 +30,18 @@ GuiDocumentListModel::GuiDocumentListModel(const GuiApplication* guiApp, QObject
QVariant GuiDocumentListModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid() || index.row() >= this->rowCount())
- return QVariant();
+ return {};
const DocumentPtr& doc = m_vecGuiDocument.at(index.row())->document();
switch (role) {
case Qt::ToolTipRole:
- return QString::fromStdString(doc->filePath().u8string());
+ return filepathTo(filepathCanonical(doc->filePath()));
case Qt::DisplayRole:
case Qt::EditRole:
- return QString::fromStdString(doc->name());
+ return to_QString(doc->name());
}
- return QVariant();
+ return {};
}
int GuiDocumentListModel::rowCount(const QModelIndex& /*parent*/) const
@@ -49,7 +51,9 @@ int GuiDocumentListModel::rowCount(const QModelIndex& /*parent*/) const
void GuiDocumentListModel::appendGuiDocument(const GuiDocument* guiDoc)
{
- const int row = this->rowCount();
+ // NOTE: don't use rowCount() as it's virtual and appendGuiDocument() is called in constructor
+ // of this class(virtual dispatch would be bypassed)
+ const auto row = int(m_vecGuiDocument.size());
this->beginInsertRows(QModelIndex(), row, row);
m_vecGuiDocument.emplace_back(guiDoc);
this->endInsertRows();
@@ -71,7 +75,8 @@ void GuiDocumentListModel::onDocumentNameChanged(const DocumentPtr& doc, const s
auto itFound = std::find_if(
m_vecGuiDocument.cbegin(),
m_vecGuiDocument.cend(),
- [&](const GuiDocument* guiDoc) { return guiDoc->document() == doc; });
+ [&](const GuiDocument* guiDoc) { return guiDoc->document() == doc; }
+ );
if (itFound != m_vecGuiDocument.cend()) {
const int row = itFound - m_vecGuiDocument.begin();
const QModelIndex itemIndex = this->index(row);
diff --git a/src/app/iwidget_main_page.h b/src/app/iwidget_main_page.h
new file mode 100644
index 00000000..7987fda4
--- /dev/null
+++ b/src/app/iwidget_main_page.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+** Copyright (c) 2023, Fougue Ltd.
+** All rights reserved.
+** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt
+****************************************************************************/
+
+#pragma once
+
+#include
+
+namespace Mayo {
+
+class CommandContainer;
+
+// Provides an interface for main pages within the Mayo application
+// As its core, Mayo UI is basically a stack of widgets
+// Such widgets are called "main pages" or just "pages" and only a single one is active at a time
+class IWidgetMainPage : public QWidget {
+ Q_OBJECT
+public:
+ // Builds UI objects(eg this might calls setupUi() on Qt-generated widgets)
+ IWidgetMainPage(QWidget* parent = nullptr)
+ : QWidget(parent)
+ {}
+
+ // Completes initialization of the page(eg by linking some buttons to existing commands)
+ virtual void initialize(const CommandContainer* cmdContainer) = 0;
+
+ // Update the activation("enabled" status) of the controls(ie any widget) belonging to this page
+ virtual void updatePageControlsActivation() = 0;
+
+signals:
+ // Signal emitted when a "global" or "complete" activation at the whole application level is
+ // required by this page
+ // This might, for example, be consecutive to event internal to this page
+ void updateGlobalControlsActivationRequired();
+};
+
+} // namespace Mayo
diff --git a/src/app/main.cpp b/src/app/main.cpp
index 76e4b510..d7eed7ab 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -8,6 +8,7 @@
#include "../base/document_tree_node_properties_provider.h"
#include "../base/io_system.h"
#include "../base/settings.h"
+#include "../io_assimp/io_assimp.h"
#include "../io_dxf/io_dxf.h"
#include "../io_gmio/io_gmio.h"
#include "../io_image/io_image.h"
@@ -22,6 +23,7 @@
#include "../gui/gui_application.h"
#include "app_module.h"
#include "cli_export.h"
+#include "commands_help.h"
#include "console.h"
#include "document_tree_node_properties_providers.h"
#include "filepath_conv.h"
@@ -82,6 +84,7 @@ struct CommandLineArguments {
std::vector listFilepathToExport;
std::vector listFilepathToOpen;
bool cliProgressReport = true;
+ bool showSystemInformation = false;
};
// Provides customization of Qt message handler
@@ -228,6 +231,12 @@ static CommandLineArguments processCommandLine()
);
cmdParser.addOption(cmdCliNoProgress);
+ const QCommandLineOption cmdSysInfo(
+ QStringList{ "system-info" },
+ Main::tr("Show detailed system information and quit")
+ );
+ cmdParser.addOption(cmdSysInfo);
+
cmdParser.addPositionalArgument(
Main::tr("files"),
Main::tr("Files to open at startup, optionally"),
@@ -268,6 +277,7 @@ static CommandLineArguments processCommandLine()
args.includeDebugLogs = cmdParser.isSet(cmdDebugLogs);
#endif
args.cliProgressReport = !cmdParser.isSet(cmdCliNoProgress);
+ args.showSystemInformation = cmdParser.isSet(cmdSysInfo);
return args;
}
@@ -342,7 +352,7 @@ static std::string_view qtTranslate(const TextId& text, int n)
}
// Helper to query the OpenGL version string
-static std::string queryGlVersionString()
+[[maybe_unused]] static std::string queryGlVersionString()
{
QOpenGLContext glContext;
if (!glContext.create())
@@ -362,7 +372,7 @@ static std::string queryGlVersionString()
// Helper to parse a string containing a semantic version eg "4.6.5 CodeNamed"
// Note: only major and minor versions are detected
-static QVersionNumber parseSemanticVersionString(std::string_view strVersion)
+[[maybe_unused]] static QVersionNumber parseSemanticVersionString(std::string_view strVersion)
{
if (strVersion.empty())
return {};
@@ -396,7 +406,6 @@ static void initGui(GuiApplication* guiApp)
if (!propForceOpenGlFallbackWidget && hasQGuiApplication) { // QOpenGL requires QGuiApplication
const std::string strGlVersion = queryGlVersionString();
const QVersionNumber glVersion = parseSemanticVersionString(strGlVersion);
- qInfo() << fmt::format("OpenGL v{}.{}", glVersion.majorVersion(), glVersion.minorVersion()).c_str();
if (!glVersion.isNull() && glVersion.majorVersion() >= 2) { // Requires at least OpenGL version >= 2.0
setFunctionCreateGraphicsDriver(&QOpenGLWidgetOccView::createCompatibleGraphicsDriver);
IWidgetOccView::setCreator(&QOpenGLWidgetOccView::create);
@@ -452,12 +461,16 @@ static int runApp(QCoreApplication* qtApp)
appModule->settings()->setStorage(std::make_unique());
{
// Load translation files
- const QString qmFilePath = QString(":/i18n/mayo_%1.qm").arg(appModule->languageCode());
- auto translator = new QTranslator(qtApp);
- if (translator->load(qmFilePath))
- qtApp->installTranslator(translator);
- else
- qWarning() << Main::tr("Failed to load translation file [path=%1]").arg(qmFilePath);
+ auto fnLoadQmFile = [=](const QString& qmFilePath) {
+ auto translator = new QTranslator(qtApp);
+ if (translator->load(qmFilePath))
+ qtApp->installTranslator(translator);
+ else
+ qWarning() << Main::tr("Failed to load translation file [path=%1]").arg(qmFilePath);
+ };
+ const QString appLangCode = appModule->languageCode();
+ fnLoadQmFile(QString(":/i18n/mayo_%1.qm").arg(appLangCode));
+ fnLoadQmFile(QString(":/i18n/qtbase_%1.qm").arg(appLangCode));
}
// Initialize Base application
@@ -480,6 +493,7 @@ static int runApp(QCoreApplication* qtApp)
ioSystem->addFactoryReader(std::make_unique());
ioSystem->addFactoryReader(std::make_unique());
ioSystem->addFactoryReader(std::make_unique());
+ ioSystem->addFactoryReader(IO::AssimpFactoryReader::create());
ioSystem->addFactoryWriter(std::make_unique());
ioSystem->addFactoryWriter(std::make_unique());
ioSystem->addFactoryWriter(std::make_unique());
@@ -489,7 +503,21 @@ static int runApp(QCoreApplication* qtApp)
appModule->properties()->IO_bindParameters(ioSystem);
appModule->properties()->retranslate();
+ // Register library infos
+ CommandSystemInformation::addLibraryInfo(
+ IO::AssimpLib::strName(), IO::AssimpLib::strVersion(), IO::AssimpLib::strVersionDetails()
+ );
+ CommandSystemInformation::addLibraryInfo(
+ IO::GmioLib::strName(), IO::GmioLib::strVersion(), IO::GmioLib::strVersionDetails()
+ );
+
// Process CLI
+ if (args.showSystemInformation) {
+ CommandSystemInformation cmdSysInfo(nullptr);
+ cmdSysInfo.execute();
+ return qtApp->exec();
+ }
+
if (!args.listFilepathToExport.empty()) {
if (args.listFilepathToOpen.empty())
fnCriticalExit(Main::tr("No input files -> nothing to export"));
@@ -553,16 +581,10 @@ int main(int argc, char* argv[])
{
qInstallMessageHandler(&Mayo::LogMessageHandler::qtHandler);
- // OpenCascade TKOpenGl depends on XLib for Linux(excepting Android) and BSD systems(excepting macOS)
- // See for example implementation of Aspect_DisplayConnection where XLib is explicitly used
- // On systems running eg Wayland this would cause problems(see https://github.com/fougue/mayo/issues/178)
- // As a workaround the Qt platform is forced to xcb
-#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || (defined(Q_OS_BSD4) && !defined(Q_OS_MACOS))
- qputenv("QT_QPA_PLATFORM", "xcb");
-#endif
-
// Helper function to check if application arguments contain any option listed in 'listOption'
- auto fnArgsContainAnyOf = [=](std::initializer_list listOption) {
+ // IMPORTANT: capture by reference, because QApplication constructor may alter argc(due to
+ // parsing of arguments)
+ auto fnArgsContainAnyOf = [&](std::initializer_list listOption) {
for (int i = 1; i < argc; ++i) {
for (const char* option : listOption) {
if (std::strcmp(argv[i], option) == 0)
@@ -572,6 +594,24 @@ int main(int argc, char* argv[])
return false;
};
+ // If the arguments(argv) contain any of the following option, then Mayo has to run in CLI mode
+ const bool isAppCliMode = fnArgsContainAnyOf({ "-e", "--export", "-h", "--help", "-v", "--version" });
+
+ // OpenCascade TKOpenGl depends on XLib for Linux(excepting Android) and BSD systems(excepting macOS)
+ // See for example implementation of Aspect_DisplayConnection where XLib is explicitly used
+ // On systems running eg Wayland this would cause problems(see https://github.com/fougue/mayo/issues/178)
+ // As a workaround the Qt platform is forced to xcb
+#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || (defined(Q_OS_BSD4) && !defined(Q_OS_MACOS))
+ if (
+ !isAppCliMode
+ && !qEnvironmentVariableIsSet("QT_QPA_PLATFORM")
+ && !fnArgsContainAnyOf({ "-platform" })
+ )
+ {
+ qputenv("QT_QPA_PLATFORM", "xcb");
+ }
+#endif
+
// Configure and create Qt application object
#if defined(Q_OS_WIN)
// Never use ANGLE on Windows, since OCCT 3D Viewer does not expect this
@@ -581,7 +621,6 @@ int main(int argc, char* argv[])
QCoreApplication::setOrganizationDomain("www.fougue.pro");
QCoreApplication::setApplicationName("Mayo");
QCoreApplication::setApplicationVersion(QString::fromUtf8(Mayo::strVersion));
- const bool isAppCliMode = fnArgsContainAnyOf({ "-e", "--export", "-h", "--help", "-v", "--version" });
std::unique_ptr ptrApp(
isAppCliMode ? new QCoreApplication(argc, argv) : new QApplication(argc, argv)
);
diff --git a/src/app/mainwindow.cpp b/src/app/mainwindow.cpp
index ce0c4531..2d8e10f9 100644
--- a/src/app/mainwindow.cpp
+++ b/src/app/mainwindow.cpp
@@ -8,13 +8,7 @@
#include "ui_mainwindow.h"
#include "../base/application.h"
-#include "../base/application_item_selection_model.h"
-#include "../base/document.h"
#include "../base/global.h"
-#include "../base/messenger.h"
-#include "../base/settings.h"
-#include "../graphics/graphics_object_driver.h"
-#include "../graphics/graphics_utils.h"
#include "../gui/gui_application.h"
#include "../gui/gui_document.h"
#include "app_context.h"
@@ -25,175 +19,58 @@
#include "commands_window.h"
#include "commands_help.h"
#include "dialog_task_manager.h"
-#include "document_property_group.h"
-#include "filepath_conv.h"
-#include "gui_document_list_model.h"
-#include "item_view_buttons.h"
-#include "qstring_conv.h"
#include "qtgui_utils.h"
#include "qtwidgets_utils.h"
#include "theme.h"
-#include "widget_file_system.h"
-#include "widget_gui_document.h"
+#include "widget_main_control.h"
+#include "widget_main_home.h"
#include "widget_message_indicator.h"
-#include "widget_model_tree.h"
-#include "widget_occ_view.h"
-#include "widget_properties_editor.h"
#ifdef Q_OS_WIN
# include "windows/win_taskbar_global_progress.h"
#endif
-#include
#include
namespace Mayo {
-MainWindow::MainWindow(GuiApplication* guiApp, QWidget *parent)
+MainWindow::MainWindow(GuiApplication* guiApp, QWidget* parent)
: QMainWindow(parent),
m_guiApp(guiApp),
m_ui(new Ui_MainWindow)
{
m_ui->setupUi(this);
- m_ui->widget_ModelTree->registerGuiApplication(guiApp);
-
- m_ui->splitter_Main->setChildrenCollapsible(false);
- m_ui->splitter_Main->setStretchFactor(0, 1);
- m_ui->splitter_Main->setStretchFactor(1, 3);
-
- m_ui->splitter_ModelTree->setStretchFactor(0, 1);
- m_ui->splitter_ModelTree->setStretchFactor(1, 2);
-
- m_ui->stack_LeftContents->setCurrentIndex(0);
-
- m_ui->widget_Properties->setRowHeightFactor(1.4);
- m_ui->widget_Properties->clear();
-
- mayoTheme()->setupHeaderComboBox(m_ui->combo_LeftContents);
- mayoTheme()->setupHeaderComboBox(m_ui->combo_GuiDocuments);
+ this->addPage(IAppContext::Page::Home, new WidgetMainHome(this));
+ this->addPage(IAppContext::Page::Documents, new WidgetMainControl(guiApp, this));
+ // AppContext requires WidgetMainControl object, ensure it has been created beforehand
m_appContext = new AppContext(this);
+ m_cmdContainer.setAppContext(m_appContext);
+
+ // Some commands requires WidgetMainControl UI page to exist, ensure it has been created beforehand
this->createCommands();
this->createMenus();
- m_ui->btn_PreviousGuiDocument->setDefaultAction(this->getCommandAction("previous-doc"));
- m_ui->btn_NextGuiDocument->setDefaultAction(this->getCommandAction("next-doc"));
- m_ui->btn_CloseGuiDocument->setDefaultAction(this->getCommandAction("close-doc"));
- m_ui->btn_CloseLeftSideBar->setDefaultAction(this->getCommandAction("toggle-left-sidebar"));
+ // WidgetMainControl page depends on some Command objects, ensure they have been created beforehand
+ for (auto [code, page] : m_mapWidgetPage)
+ page->initialize(&m_cmdContainer);
- // "HomeFiles" actions
- QObject::connect(
- m_ui->widget_HomeFiles, &WidgetHomeFiles::newDocumentRequested,
- this->getCommand("new-doc"), &Command::execute
- );
- QObject::connect(
- m_ui->widget_HomeFiles, &WidgetHomeFiles::openDocumentsRequested,
- this->getCommand("open-docs"), &Command::execute
- );
- QObject::connect(
- m_ui->widget_HomeFiles, &WidgetHomeFiles::recentFileOpenRequested,
- this, &MainWindow::openDocument
- );
- // "Window" actions and navigation in documents
- QObject::connect(
- m_ui->combo_GuiDocuments, qOverload(&QComboBox::currentIndexChanged),
- this, &MainWindow::onCurrentDocumentIndexChanged
- );
- QObject::connect(
- m_ui->widget_FileSystem, &WidgetFileSystem::locationActivated,
- this, &MainWindow::onWidgetFileSystemLocationActivated
- );
- // ...
- QObject::connect(
- m_ui->combo_LeftContents, qOverload(&QComboBox::currentIndexChanged),
- this, &MainWindow::onLeftContentsPageChanged
- );
- QObject::connect(
- m_ui->listView_OpenedDocuments, &QListView::clicked,
- this, [=](const QModelIndex& index) { this->setCurrentDocumentIndex(index.row()); }
- );
- guiApp->application()->signalDocumentFilePathChanged.connectSlot([=](const DocumentPtr& doc, const FilePath& fp) {
- if (this->currentWidgetGuiDocument()->documentIdentifier() == doc->identifier())
- m_ui->widget_FileSystem->setLocation(filepathTo(fp));
- });
AppModule::get()->signalMessage.connectSlot(&MainWindow::onMessage, this);
guiApp->signalGuiDocumentAdded.connectSlot(&MainWindow::onGuiDocumentAdded, this);
- guiApp->selectionModel()->signalChanged.connectSlot(&MainWindow::onApplicationItemSelectionChanged, this);
- // Creation of annex objects
- {
- // Opened documents GUI
- auto listViewBtns = new ItemViewButtons(m_ui->listView_OpenedDocuments, this);
- auto actionCloseDoc = this->getCommandAction("close-doc");
- listViewBtns->addButton(1, actionCloseDoc->icon(), actionCloseDoc->toolTip());
- listViewBtns->setButtonDetection(1, -1, QVariant());
- listViewBtns->setButtonDisplayColumn(1, 0);
- listViewBtns->setButtonDisplayModes(1, ItemViewButtons::DisplayOnDetection);
- listViewBtns->setButtonItemSide(1, ItemViewButtons::ItemRightSide);
- const int iconSize = this->style()->pixelMetric(QStyle::PM_ListViewIconSize);
- listViewBtns->setButtonIconSize(1, QSize(iconSize * 0.66, iconSize * 0.66));
- listViewBtns->installDefaultItemDelegate();
- QObject::connect(listViewBtns, &ItemViewButtons::buttonClicked, this, [=](int btnId, QModelIndex index) {
- if (btnId == 1) {
- auto widgetDoc = this->widgetGuiDocument(index.row());
- if (widgetDoc)
- FileCommandTools::closeDocument(m_appContext, widgetDoc->documentIdentifier());
- }
- });
- }
+ guiApp->signalGuiDocumentErased.connectSlot(&MainWindow::onGuiDocumentErased, this);
new DialogTaskManager(&m_taskMgr, this);
- // BEWARE MainWindow::onGuiDocumentAdded() must be called before
- // MainWindow::onCurrentDocumentIndexChanged()
- auto guiDocModel = new GuiDocumentListModel(guiApp, this);
- m_ui->combo_GuiDocuments->setModel(guiDocModel);
- m_ui->listView_OpenedDocuments->setModel(guiDocModel);
-
- // Finalize setup
- m_ui->widget_LeftHeader->installEventFilter(this);
- m_ui->widget_ControlGuiDocuments->installEventFilter(this);
- m_ui->stack_GuiDocuments->installEventFilter(this);
- this->onLeftContentsPageChanged(m_ui->stack_LeftContents->currentIndex());
this->updateControlsActivation();
- m_ui->widget_MouseCoords->hide();
-
- this->onCurrentDocumentIndexChanged(-1);
}
MainWindow::~MainWindow()
{
+ // Force deletion of Command objects as some of them are event filters of MainWindow widgets
+ m_cmdContainer.clear();
delete m_ui;
}
-bool MainWindow::eventFilter(QObject* watched, QEvent* event)
-{
- auto fnSizeBtn = [](const QWidget* container, const QWidget* widgetHeightRef) {
- const int btnSideLen = widgetHeightRef->frameGeometry().height();
- const QList listBtn = container->findChildren();
- for (QAbstractButton* btn : listBtn)
- btn->setFixedSize(btnSideLen, btnSideLen);
- };
- const QEvent::Type eventType = event->type();
- if (watched == m_ui->widget_ControlGuiDocuments && eventType == QEvent::Show) {
- fnSizeBtn(m_ui->widget_ControlGuiDocuments, m_ui->combo_GuiDocuments);
- return true;
- }
-
- if (watched == m_ui->widget_LeftHeader && eventType == QEvent::Show) {
- fnSizeBtn(m_ui->widget_LeftHeader, m_ui->combo_LeftContents);
- return true;
- }
-
- if (watched == m_ui->stack_GuiDocuments) {
- if (eventType == QEvent::Enter || eventType == QEvent::Leave) {
- m_ui->widget_MouseCoords->setHidden(eventType == QEvent::Leave);
- return true;
- }
- }
-
- return false;
-}
-
void MainWindow::showEvent(QShowEvent* event)
{
QMainWindow::showEvent(event);
@@ -207,158 +84,122 @@ void MainWindow::showEvent(QShowEvent* event)
#endif
}
+void MainWindow::addPage(IAppContext::Page page, IWidgetMainPage* pageWidget)
+{
+ assert(m_mapWidgetPage.find(page) == m_mapWidgetPage.cend());
+ assert(m_ui->stack_Main->indexOf(pageWidget) == -1);
+ m_mapWidgetPage.insert({ page, pageWidget });
+ m_ui->stack_Main->addWidget(pageWidget);
+ QObject::connect(
+ pageWidget, &IWidgetMainPage::updateGlobalControlsActivationRequired,
+ this, &MainWindow::updateControlsActivation
+ );
+}
+
void MainWindow::createCommands()
{
// "File" commands
- this->addCommand("new-doc");
- this->addCommand("open-docs");
- this->addCommand("recent-files", m_ui->menu_File);
- this->addCommand("import");
- this->addCommand("export");
- this->addCommand("close-doc");
- this->addCommand("close-all-docs");
- this->addCommand("close-all-docs-except-current");
- this->addCommand("quit");
+ this->addCommand();
+ this->addCommand();
+ this->addCommand(m_ui->menu_File);
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
// "Display" commands
- this->addCommand("change-projection");
- this->addCommand("change-display-mode", m_ui->menu_Display);
- this->addCommand("toggle-origin-trihedron");
- this->addCommand("toggle-performance-stats");
- this->addCommand("current-doc-zoom-in");
- this->addCommand("current-doc-zoom-out");
- this->addCommand("current-doc-turn-view-ccw");
- this->addCommand("current-doc-turn-view-cw");
+ this->addCommand();
+ this->addCommand(m_ui->menu_Display);
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
// "Tools" commands
- this->addCommand("save-view-image");
- this->addCommand("inspect-xde");
- this->addCommand("edit-options");
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
// "Window" commands
- this->addCommand("toggle-left-sidebar");
- this->addCommand("toggle-fullscreen");
- this->addCommand("previous-doc");
- this->addCommand("next-doc");
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
// "Help" commands
- this->addCommand("report-bug");
- this->addCommand("about");
+ this->addCommand();
+ this->addCommand();
+ this->addCommand();
}
void MainWindow::createMenus()
{
- // Helper function to retrieve the QAction associated to the name of a command
- auto fnGetAction = [=](std::string_view commandName) {
- return this->getCommandAction(commandName);
+ // Helper function to add in 'menu' the QAction associated to 'commandName'
+ auto fnAddAction = [=](QMenu* menu, std::string_view commandName) {
+ menu->addAction(m_cmdContainer.findCommandAction(commandName));
};
+ // TODO Create menu bar programmatically(not hard-code in .ui file)
+
{ // File
auto menu = m_ui->menu_File;
- menu->addAction(fnGetAction("new-doc"));
- menu->addAction(fnGetAction("open-docs"));
- menu->addAction(fnGetAction("recent-files"));
+ fnAddAction(menu, CommandNewDocument::Name);
+ fnAddAction(menu, CommandOpenDocuments::Name);
+ fnAddAction(menu, CommandRecentFiles::Name);
menu->addSeparator();
- menu->addAction(fnGetAction("import"));
- menu->addAction(fnGetAction("export"));
+ fnAddAction(menu, CommandImportInCurrentDocument::Name);
+ fnAddAction(menu, CommandExportSelectedApplicationItems::Name);
menu->addSeparator();
- menu->addAction(fnGetAction("close-doc"));
- menu->addAction(fnGetAction("close-all-docs-except-current"));
- menu->addAction(fnGetAction("close-all-docs"));
+ fnAddAction(menu, CommandCloseCurrentDocument::Name);
+ fnAddAction(menu, CommandCloseAllDocumentsExceptCurrent::Name);
+ fnAddAction(menu, CommandCloseAllDocuments::Name);
menu->addSeparator();
- menu->addAction(fnGetAction("quit"));
+ fnAddAction(menu, CommandQuitApplication::Name);
}
{ // Display
auto menu = m_ui->menu_Display;
- menu->addAction(fnGetAction("change-projection"));
- menu->addAction(fnGetAction("change-display-mode"));
- menu->addAction(fnGetAction("toggle-origin-trihedron"));
- menu->addAction(fnGetAction("toggle-performance-stats"));
+ fnAddAction(menu, CommandChangeProjection::Name);
+ fnAddAction(menu, CommandChangeDisplayMode::Name);
+ fnAddAction(menu, CommandToggleOriginTrihedron::Name);
+ fnAddAction(menu, CommandTogglePerformanceStats::Name);
menu->addSeparator();
- menu->addAction(fnGetAction("current-doc-zoom-in"));
- menu->addAction(fnGetAction("current-doc-zoom-out"));
- menu->addAction(fnGetAction("current-doc-turn-view-ccw"));
- menu->addAction(fnGetAction("current-doc-turn-view-cw"));
+ fnAddAction(menu, CommandZoomInCurrentDocument::Name);
+ fnAddAction(menu, CommandZoomOutCurrentDocument::Name);
+ fnAddAction(menu, CommandTurnViewCounterClockWise::Name);
+ fnAddAction(menu, CommandTurnViewClockWise::Name);
}
{ // Tools
auto menu = m_ui->menu_Tools;
- menu->addAction(fnGetAction("save-view-image"));
- menu->addAction(fnGetAction("inspect-xde"));
+ fnAddAction(menu, CommandSaveViewImage::Name);
+ fnAddAction(menu, CommandInspectXde::Name);
menu->addSeparator();
- menu->addAction(fnGetAction("edit-options"));
+ fnAddAction(menu, CommandEditOptions::Name);
}
{ // Window
auto menu = m_ui->menu_Window;
- menu->addAction(fnGetAction("toggle-left-sidebar"));
- menu->addAction(fnGetAction("toggle-fullscreen"));
+ fnAddAction(menu, CommandLeftSidebarWidgetToggle::Name);
+ fnAddAction(menu, CommandMainWidgetToggleFullscreen::Name);
menu->addSeparator();
- menu->addAction(fnGetAction("previous-doc"));
- menu->addAction(fnGetAction("next-doc"));
+ fnAddAction(menu, CommandSwitchMainWidgetMode::Name);
+ fnAddAction(menu, CommandPreviousDocument::Name);
+ fnAddAction(menu, CommandNextDocument::Name);
}
{ // Help
auto menu = m_ui->menu_Help;
- menu->addAction(fnGetAction("report-bug"));
+ fnAddAction(menu, CommandReportBug::Name);
+ fnAddAction(menu, CommandSystemInformation::Name);
menu->addSeparator();
- menu->addAction(fnGetAction("about"));
- }
-}
-
-void MainWindow::onApplicationItemSelectionChanged()
-{
- WidgetModelTree* uiModelTree = m_ui->widget_ModelTree;
- WidgetPropertiesEditor* uiProps = m_ui->widget_Properties;
-
- uiProps->clear();
- Span spanAppItem = m_guiApp->selectionModel()->selectedItems();
- if (spanAppItem.size() == 1) {
- const ApplicationItem& appItem = spanAppItem.front();
- if (appItem.isDocument()) {
- auto dataProps = new DocumentPropertyGroup(appItem.document());
- uiProps->editProperties(dataProps, uiProps->addGroup(tr("Data")));
- m_ptrCurrentNodeDataProperties.reset(dataProps);
- }
- else if (appItem.isDocumentTreeNode()) {
- const DocumentTreeNode& docTreeNode = appItem.documentTreeNode();
- auto dataProps = AppModule::get()->properties(docTreeNode);
- if (dataProps) {
- uiProps->editProperties(dataProps.get(), uiProps->addGroup(tr("Data")));
- dataProps->signalPropertyChanged.connectSlot([=]{ uiModelTree->refreshItemText(appItem); });
- m_ptrCurrentNodeDataProperties = std::move(dataProps);
- }
-
- GuiDocument* guiDoc = m_guiApp->findGuiDocument(appItem.document());
- std::vector vecGfxObject;
- guiDoc->foreachGraphicsObject(docTreeNode.id(), [&](GraphicsObjectPtr gfxObject) {
- vecGfxObject.push_back(std::move(gfxObject));
- });
- auto commonGfxDriver = GraphicsObjectDriver::getCommon(vecGfxObject);
- if (commonGfxDriver) {
- auto gfxProps = commonGfxDriver->properties(vecGfxObject);
- if (gfxProps) {
- uiProps->editProperties(gfxProps.get(), uiProps->addGroup(tr("Graphics")));
- gfxProps->signalPropertyChanged.connectSlot([=]{ guiDoc->graphicsScene()->redraw(); });
- m_ptrCurrentNodeGraphicsProperties = std::move(gfxProps);
- }
- }
- }
-
- auto app = m_guiApp->application();
- if (AppModule::get()->properties()->linkWithDocumentSelector) {
- const int index = app->findIndexOfDocument(appItem.document());
- if (index != -1)
- this->setCurrentDocumentIndex(index);
- }
- }
- else {
- // TODO
- uiProps->clear();
+ fnAddAction(menu, CommandAbout::Name);
}
-
- this->updateControlsActivation();
}
void MainWindow::onOperationFinished(bool ok, const QString &msg)
@@ -396,110 +237,34 @@ void MainWindow::onGuiDocumentAdded(GuiDocument* guiDoc)
fnConfigureHighlightStyle(gfxScene->drawerHighlight(Prs3d_TypeOfHighlight_LocalSelected).get());
fnConfigureHighlightStyle(gfxScene->drawerHighlight(Prs3d_TypeOfHighlight_Selected).get());
- // Configure 3D view behavior with respect to application settings
- auto appModule = AppModule::get();
- auto appProps = appModule->properties();
- auto widget = new WidgetGuiDocument(guiDoc);
- guiDoc->setDevicePixelRatio(widget->devicePixelRatioF());
- auto widgetCtrl = widget->controller();
- widgetCtrl->setInstantZoomFactor(appProps->instantZoomFactor);
- widgetCtrl->setNavigationStyle(appProps->navigationStyle);
- if (appProps->defaultShowOriginTrihedron) {
- guiDoc->toggleOriginTrihedronVisibility();
- gfxScene->redraw();
- }
-
- appModule->settings()->signalChanged.connectSlot([=](const Property* setting) {
- if (setting == &appProps->instantZoomFactor)
- widgetCtrl->setInstantZoomFactor(appProps->instantZoomFactor);
- else if (setting == &appProps->navigationStyle)
- widgetCtrl->setNavigationStyle(appProps->navigationStyle);
- });
-
- // React to mouse move in 3D view:
- // * update highlighting
- // * compute and display 3D mouse coordinates(by silent picking)
- widgetCtrl->signalMouseMoved.connectSlot([=](int xPos, int yPos) {
- const double dpRatio = this->devicePixelRatioF();
- gfxScene->highlightAt(xPos * dpRatio, yPos * dpRatio, guiDoc->v3dView());
- widget->view()->redraw();
- auto selector = gfxScene->mainSelector();
- selector->Pick(xPos, yPos, guiDoc->v3dView());
- const gp_Pnt pos3d =
- selector->NbPicked() > 0 ?
- selector->PickedPoint(1) :
- GraphicsUtils::V3dView_to3dPosition(guiDoc->v3dView(), xPos, yPos);
- m_ui->label_ValuePosX->setText(QString::number(pos3d.X(), 'f', 3));
- m_ui->label_ValuePosY->setText(QString::number(pos3d.Y(), 'f', 3));
- m_ui->label_ValuePosZ->setText(QString::number(pos3d.Z(), 'f', 3));
- });
-
- m_ui->stack_GuiDocuments->addWidget(widget);
+ this->updateCurrentPage();
this->updateControlsActivation();
- const int newDocIndex = m_guiApp->application()->documentCount() - 1;
- QTimer::singleShot(0, this, [=]{ this->setCurrentDocumentIndex(newDocIndex); });
-}
-
-void MainWindow::onWidgetFileSystemLocationActivated(const QFileInfo& loc)
-{
- this->openDocument(filepathFrom(loc));
}
-void MainWindow::onLeftContentsPageChanged(int pageId)
+void MainWindow::onGuiDocumentErased(GuiDocument* /*guiDoc*/)
{
- m_ui->stack_LeftContents->setCurrentIndex(pageId);
- QWidget* placeHolder = this->recreateLeftHeaderPlaceHolder();
- if (m_ui->stack_LeftContents->currentWidget() == m_ui->page_ModelTree && placeHolder) {
- const int btnSideLen = m_ui->combo_LeftContents->frameGeometry().height();
- auto btnSettings = new QToolButton(placeHolder);
- btnSettings->setAutoRaise(true);
- btnSettings->setFixedSize(btnSideLen, btnSideLen);
- btnSettings->setIcon(mayoTheme()->icon(Theme::Icon::Gear));
- btnSettings->setToolTip(tr("Options"));
- placeHolder->layout()->addWidget(btnSettings);
- btnSettings->setMenu(this->createMenuModelTreeSettings());
- btnSettings->setPopupMode(QToolButton::InstantPopup);
- }
- else {
- delete placeHolder;
- }
-}
-
-void MainWindow::onCurrentDocumentIndexChanged(int idx)
-{
- m_ui->stack_GuiDocuments->setCurrentIndex(idx);
- QAbstractItemView* view = m_ui->listView_OpenedDocuments;
- view->setCurrentIndex(view->model()->index(idx, 0));
-
+ this->updateCurrentPage();
this->updateControlsActivation();
-
- const DocumentPtr docPtr = m_guiApp->application()->findDocumentByIndex(idx);
- const FilePath docFilePath = docPtr ? docPtr->filePath() : FilePath();
- m_ui->widget_FileSystem->setLocation(filepathTo(docFilePath));
}
-void MainWindow::onMessage(Messenger::MessageType msgType, const QString& text)
+void MainWindow::onMessage(MessageType msgType, const QString& text)
{
switch (msgType) {
- case Messenger::MessageType::Trace:
+ case MessageType::Trace:
+ qDebug() << text;
break;
- case Messenger::MessageType::Info:
+ case MessageType::Info:
WidgetMessageIndicator::showInfo(text, this);
break;
- case Messenger::MessageType::Warning:
+ case MessageType::Warning:
QtWidgetsUtils::asyncMsgBoxWarning(this, tr("Warning"), text);
break;
- case Messenger::MessageType::Error:
+ case MessageType::Error:
QtWidgetsUtils::asyncMsgBoxCritical(this, tr("Error"), text);
break;
}
}
-void MainWindow::openDocument(const FilePath& fp)
-{
- FileCommandTools::openDocument(m_appContext, fp);
-}
-
void MainWindow::openDocumentsFromList(Span listFilePath)
{
FileCommandTools::openDocumentsFromList(m_appContext, listFilePath);
@@ -507,99 +272,34 @@ void MainWindow::openDocumentsFromList(Span listFilePath)
void MainWindow::updateControlsActivation()
{
- const QWidget* currMainPage = m_ui->stack_Main->currentWidget();
- const int appDocumentsCount = m_guiApp->application()->documentCount();
- const bool appDocumentsEmpty = appDocumentsCount == 0;
- QWidget* newMainPage = appDocumentsEmpty ? m_ui->page_MainHome : m_ui->page_MainControl;
- if (currMainPage != newMainPage)
- m_ui->stack_Main->setCurrentWidget(newMainPage);
-
- for (auto [name, cmd] : m_mapCommand) {
+ m_cmdContainer.foreachCommand([](std::string_view, Command* cmd) {
cmd->action()->setEnabled(cmd->getEnabledStatus());
- }
-
- m_ui->combo_GuiDocuments->setEnabled(!appDocumentsEmpty);
-}
-
-int MainWindow::currentDocumentIndex() const
-{
- return m_ui->combo_GuiDocuments->currentIndex();
-}
-
-void MainWindow::setCurrentDocumentIndex(int idx)
-{
- m_ui->combo_GuiDocuments->setCurrentIndex(idx);
-}
-
-WidgetGuiDocument* MainWindow::widgetGuiDocument(int idx) const
-{
- return qobject_cast(m_ui->stack_GuiDocuments->widget(idx));
-}
-
-WidgetGuiDocument* MainWindow::currentWidgetGuiDocument() const
-{
- return this->widgetGuiDocument(this->currentDocumentIndex());
-}
-
-QWidget* MainWindow::findLeftHeaderPlaceHolder() const
-{
- return m_ui->widget_LeftHeader->findChild(
- "LeftHeaderPlaceHolder", Qt::FindDirectChildrenOnly
- );
+ });
}
-QWidget* MainWindow::recreateLeftHeaderPlaceHolder()
+void MainWindow::updateCurrentPage()
{
- QWidget* placeHolder = this->findLeftHeaderPlaceHolder();
- delete placeHolder;
- placeHolder = new QWidget(m_ui->widget_LeftHeader);
- placeHolder->setObjectName("LeftHeaderPlaceHolder");
- auto layoutPlaceHolder = new QHBoxLayout(placeHolder);
- layoutPlaceHolder->setContentsMargins(0, 0, 0, 0);
- layoutPlaceHolder->setSpacing(0);
- m_ui->Layout_WidgetLeftHeader->insertWidget(2, placeHolder);
- return placeHolder;
+ const IAppContext::Page currentPage = m_appContext->currentPage();
+ const bool appDocumentsEmpty = m_guiApp->guiDocuments().empty();
+ const auto newPage = appDocumentsEmpty ? IAppContext::Page::Home : IAppContext::Page::Documents;
+ if (currentPage != newPage)
+ m_appContext->setCurrentPage(newPage);
}
-QMenu* MainWindow::createMenuModelTreeSettings()
+IWidgetMainPage* MainWindow::widgetMainPage(IAppContext::Page page) const
{
- auto menu = new QMenu(this->findLeftHeaderPlaceHolder());
- menu->setToolTipsVisible(true);
-
- // Link with document selector
- auto appModule = AppModule::get();
- QAction* action = menu->addAction(to_QString(appModule->properties()->linkWithDocumentSelector.name().tr()));
- action->setCheckable(true);
- QObject::connect(action, &QAction::triggered, this, [=](bool on) {
- appModule->properties()->linkWithDocumentSelector.setValue(on);
- });
-
- // Model tree user actions
- menu->addSeparator();
- const WidgetModelTree_UserActions userActions = m_ui->widget_ModelTree->createUserActions(menu);
- for (QAction* usrAction : userActions.items)
- menu->addAction(usrAction);
-
- // Sync before menu show
- QObject::connect(menu, &QMenu::aboutToShow, this, [=]{
- action->setChecked(appModule->properties()->linkWithDocumentSelector);
- if (userActions.fnSyncItems)
- userActions.fnSyncItems();
- });
-
- return menu;
+ auto it = m_mapWidgetPage.find(page);
+ return it != m_mapWidgetPage.cend() ? it->second : nullptr;
}
-Command* MainWindow::getCommand(std::string_view name) const
+WidgetMainHome* MainWindow::widgetPageHome() const
{
- auto it = m_mapCommand.find(name);
- return it != m_mapCommand.cend() ? it->second : nullptr;
+ return dynamic_cast(this->widgetMainPage(IAppContext::Page::Home));
}
-QAction* MainWindow::getCommandAction(std::string_view name) const
+WidgetMainControl* MainWindow::widgetPageDocuments() const
{
- auto cmd = this->getCommand(name);
- return cmd ? cmd->action() : nullptr;
+ return dynamic_cast(this->widgetMainPage(IAppContext::Page::Documents));
}
} // namespace Mayo
diff --git a/src/app/mainwindow.h b/src/app/mainwindow.h
index 608aa5c7..c82dd1db 100644
--- a/src/app/mainwindow.h
+++ b/src/app/mainwindow.h
@@ -6,22 +6,21 @@
#pragma once
+#include "commands_api.h"
#include "../base/filepath.h"
#include "../base/messenger.h"
-#include "../base/property.h"
#include "../base/task_manager.h"
#include "../base/text_id.h"
#include
-#include
-class QFileInfo;
+#include
namespace Mayo {
-class Command;
class GuiApplication;
class GuiDocument;
-class IAppContext;
-class WidgetGuiDocument;
+class IWidgetMainPage;
+class WidgetMainControl;
+class WidgetMainHome;
// Provides the root widget of the application GUI
// It creates and owns the various available commands(actions)
@@ -32,63 +31,41 @@ class MainWindow : public QMainWindow {
MainWindow(GuiApplication* guiApp, QWidget* parent = nullptr);
~MainWindow();
- void openDocument(const FilePath& fp);
void openDocumentsFromList(Span listFilePath);
- bool eventFilter(QObject* watched, QEvent* event) override;
-
protected:
void showEvent(QShowEvent* event) override;
private:
+ void addPage(IAppContext::Page page, IWidgetMainPage* pageWidget);
+
void createCommands();
void createMenus();
+ template void addCommand(Args... p) {
+ m_cmdContainer.addNamedCommand(std::forward(p)...);
+ }
- Command* getCommand(std::string_view name) const;
- QAction* getCommandAction(std::string_view name) const;
- template CmdType* addCommand(std::string_view name, Args... p);
-
- void onApplicationItemSelectionChanged();
void onOperationFinished(bool ok, const QString& msg);
void onGuiDocumentAdded(GuiDocument* guiDoc);
- void onWidgetFileSystemLocationActivated(const QFileInfo& loc);
- void onLeftContentsPageChanged(int pageId);
- void onCurrentDocumentIndexChanged(int idx);
- void onMessage(Messenger::MessageType msgType, const QString& text);
+ void onGuiDocumentErased(GuiDocument* guiDoc);
- void updateControlsActivation();
+ void onMessage(MessageType msgType, const QString& text);
- int currentDocumentIndex() const;
- void setCurrentDocumentIndex(int idx);
+ void updateControlsActivation();
+ void updateCurrentPage();
- WidgetGuiDocument* widgetGuiDocument(int idx) const;
- WidgetGuiDocument* currentWidgetGuiDocument() const;
- QWidget* findLeftHeaderPlaceHolder() const;
- QWidget* recreateLeftHeaderPlaceHolder();
- QMenu* createMenuModelTreeSettings();
+ IWidgetMainPage* widgetMainPage(IAppContext::Page page) const;
+ WidgetMainHome* widgetPageHome() const;
+ WidgetMainControl* widgetPageDocuments() const;
friend class AppContext;
IAppContext* m_appContext = nullptr;
GuiApplication* m_guiApp = nullptr;
+ CommandContainer m_cmdContainer;
TaskManager m_taskMgr;
class Ui_MainWindow* m_ui = nullptr;
- std::unordered_map m_mapCommand;
- std::unique_ptr m_ptrCurrentNodeDataProperties;
- std::unique_ptr m_ptrCurrentNodeGraphicsProperties;
+ std::unordered_map m_mapWidgetPage;
};
-
-
-// --
-// -- Implementation
-// --
-
-template CmdType* MainWindow::addCommand(std::string_view name, Args... p)
-{
- auto cmd = new CmdType(m_appContext, p...);
- m_mapCommand.insert({ name, cmd });
- return cmd;
-}
-
} // namespace Mayo
diff --git a/src/app/mainwindow.ui b/src/app/mainwindow.ui
index d0cf6e20..dc2ab8b7 100644
--- a/src/app/mainwindow.ui
+++ b/src/app/mainwindow.ui
@@ -33,519 +33,8 @@
-
- 0
+ -1
-
-
-
- 20
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
-
-
- :/images/appicon_128.png
-
-
- Qt::AlignHCenter|Qt::AlignTop
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Fixed
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- Qt::Horizontal
-
-
- false
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
-
-
-
-
- 1
- 0
-
-
-
-
- 0
- 22
-
-
-
-
-
- Model tree
-
-
- -
-
- Opened documents
-
-
- -
-
- File system
-
-
-
-
- -
-
-
- QFrame::Plain
-
-
- Qt::Vertical
-
-
-
- -
-
-
- Close Left Side Bar
-
-
-
- :/images/themes/classic/left-arrow-cross_16.png:/images/themes/classic/left-arrow-cross_16.png
-
-
- true
-
-
-
-
-
-
- -
-
-
- 0
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
- Qt::Vertical
-
-
-
-
- 0
- 1
-
-
-
-
-
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- Qt::ScrollBarAlwaysOff
-
-
- QAbstractItemView::NoEditTriggers
-
-
- Qt::ElideMiddle
-
-
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 1
-
- -
-
-
-
- 1
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- :/images/themes/classic/back_32.png:/images/themes/classic/back_32.png
-
-
-
- 12
- 12
-
-
-
- true
-
-
-
- -
-
-
-
- :/images/themes/classic/next_32.png:/images/themes/classic/next_32.png
-
-
-
- 12
- 12
-
-
-
- true
-
-
-
- -
-
-
-
- 0
- 22
-
-
-
- QComboBox::AdjustToContents
-
-
-
- -
-
-
- QFrame::Plain
-
-
- Qt::Vertical
-
-
-
- -
-
-
-
- :/images/themes/classic/cross_32.png:/images/themes/classic/cross_32.png
-
-
-
- 12
- 12
-
-
-
- true
-
-
-
- -
-
-
- QFrame::Plain
-
-
- Qt::Vertical
-
-
-
- -
-
-
-
- 1
- 0
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
- 0
-
-
-
-
-
- X=
-
-
-
- -
-
-
- ?
-
-
-
-
-
- -
-
-
- 0
-
-
-
-
-
- Y=
-
-
-
- -
-
-
- ?
-
-
-
-
-
- -
-
-
- 0
-
-
-
-
-
- Z=
-
-
-
- -
-
-
- ?
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- 2
- 0
-
-
-
-
-
-
-
-
-
-
@@ -592,34 +81,6 @@
-
-
- WidgetModelTree
- QWidget
-
- 1
-
-
- WidgetFileSystem
- QWidget
-
- 1
-
-
- WidgetPropertiesEditor
- QWidget
- widget_properties_editor.h
- 1
-
-
- WidgetHomeFiles
- QWidget
-
- 1
-
-
-
-
-
+
diff --git a/src/app/qstring_conv.h b/src/app/qstring_conv.h
index 2b7c5277..85ce484a 100644
--- a/src/app/qstring_conv.h
+++ b/src/app/qstring_conv.h
@@ -38,6 +38,11 @@ template<> struct StringConv {
static auto to(const std::string& str) { return QString::fromStdString(str); }
};
+// std::wstring -> QString
+template<> struct StringConv {
+ static auto to(const std::wstring& str) { return QString::fromStdWString(str); }
+};
+
// std::string_view -> QString
template<> struct StringConv {
static auto to(std::string_view str) { return QString::fromUtf8(str.data(), int(str.size())); }
@@ -97,6 +102,11 @@ template<> struct StringConv {
}
};
+// QString -> std::wstring
+template<> struct StringConv {
+ static auto to(const QString& str) { return str.toStdWString(); }
+};
+
// QString -> TCollection_ExtendedString
template<> struct StringConv {
static auto to(const QString& str) {
diff --git a/src/app/qtgui_utils.cpp b/src/app/qtgui_utils.cpp
index 776e6930..8b3dcb1a 100644
--- a/src/app/qtgui_utils.cpp
+++ b/src/app/qtgui_utils.cpp
@@ -56,7 +56,8 @@ QColor lerp(const QColor& a, const QColor& b, double t)
MathUtils::lerp(a.red(), b.red(), t),
MathUtils::lerp(a.green(), b.green(), t),
MathUtils::lerp(a.blue(), b.blue(), t),
- MathUtils::lerp(a.alpha(), b.alpha(), t));
+ MathUtils::lerp(a.alpha(), b.alpha(), t)
+ );
}
QColor linearColorAt(const QGradient& gradient, double t)
@@ -130,14 +131,14 @@ int screenPixelWidth(double screenRatio, const QScreen* screen)
{
screen = !screen ? QGuiApplication::primaryScreen() : screen;
const int screenWidth = screen ? screen->geometry().width() : 800;
- return std::round(screenWidth * screenRatio);
+ return qRound(screenWidth * screenRatio);
}
int screenPixelHeight(double screenRatio, const QScreen* screen)
{
screen = !screen ? QGuiApplication::primaryScreen() : screen;
const int screenHeight = screen ? screen->geometry().height() : 600;
- return std::round(screenHeight * screenRatio);
+ return qRound(screenHeight * screenRatio);
}
QSize screenPixelSize(double widthRatio, double heightRatio, const QScreen* screen)
diff --git a/src/app/qtwidgets_utils.cpp b/src/app/qtwidgets_utils.cpp
index b5bb2469..69227a44 100644
--- a/src/app/qtwidgets_utils.cpp
+++ b/src/app/qtwidgets_utils.cpp
@@ -112,4 +112,9 @@ void QtWidgetsUtils::moveWidgetLeftTo(QWidget* widget, const QWidget* nextTo, in
widget->move(nextTo->mapToParent(QPoint(-frameGeom.width() - margin, 0)));
}
+void QtWidgetsUtils::collapseWidget(QWidget *widget, bool on)
+{
+ widget->setMaximumHeight(on ? 0 : 16777215/*Qt_defaultMaximumWidth*/);
+}
+
} // namespace Mayo
diff --git a/src/app/qtwidgets_utils.h b/src/app/qtwidgets_utils.h
index 5296f502..0760943f 100644
--- a/src/app/qtwidgets_utils.h
+++ b/src/app/qtwidgets_utils.h
@@ -57,6 +57,8 @@ class QtWidgetsUtils {
static void moveWidgetRightTo(QWidget* widget, const QWidget* nextTo, int margin);
// Move position of 'widget' so it's displayed stuck to the left of 'nextTo'
static void moveWidgetLeftTo(QWidget* widget, const QWidget* nextTo, int margin = 0);
+
+ static void collapseWidget(QWidget* widget, bool on);
};
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
diff --git a/src/app/theme.cpp b/src/app/theme.cpp
index 59e27789..aab70de8 100644
--- a/src/app/theme.cpp
+++ b/src/app/theme.cpp
@@ -78,6 +78,7 @@ static QString iconFileName(Theme::Icon icn)
case Theme::Icon::Export: return "export.svg";
case Theme::Icon::Expand: return "expand.svg";
case Theme::Icon::Cross: return "cross.svg";
+ case Theme::Icon::Grid: return "grid.svg";
case Theme::Icon::Link: return "link.svg";
case Theme::Icon::Back: return "back.svg";
case Theme::Icon::Next: return "next.svg";
diff --git a/src/app/theme.h b/src/app/theme.h
index fe1fd569..229f1b75 100644
--- a/src/app/theme.h
+++ b/src/app/theme.h
@@ -45,6 +45,7 @@ class Theme {
Export,
Expand,
Cross,
+ Grid,
Link,
Back,
Next,
diff --git a/src/app/widget_clip_planes.cpp b/src/app/widget_clip_planes.cpp
index 563e0e68..fd2387e7 100644
--- a/src/app/widget_clip_planes.cpp
+++ b/src/app/widget_clip_planes.cpp
@@ -11,6 +11,7 @@
#include "../base/math_utils.h"
#include "../base/settings.h"
#include "../base/tkernel_utils.h"
+#include "../graphics/graphics_texture2d.h"
#include "../graphics/graphics_utils.h"
#include "app_module.h"
#include "ui_widget_clip_planes.h"
@@ -19,7 +20,6 @@
#include
#include
#include
-#include
#include
#include
@@ -205,7 +205,7 @@ void WidgetClipPlanes::setPlaneOn(const Handle_Graphic3d_ClipPlane& plane, bool
{
plane->SetOn(on);
if (!GraphicsUtils::V3dView_hasClipPlane(m_view.v3dView(), plane))
- m_view.v3dView()->AddClipPlane(plane);
+ m_view->AddClipPlane(plane);
}
void WidgetClipPlanes::setPlaneRange(ClipPlaneData* data, const Range& range)
@@ -244,7 +244,7 @@ void WidgetClipPlanes::createPlaneCappingTexture()
auto fileContentsData = reinterpret_cast(fileContents.constData());
Handle_Image_AlienPixMap imageCapping = new Image_AlienPixMap;
imageCapping->Load(fileContentsData, fileContents.size(), filenameUtf8.constData());
- m_textureCapping = new Graphic3d_Texture2Dmanual(imageCapping);
+ m_textureCapping = new GraphicsTexture2D(imageCapping);
m_textureCapping->EnableModulate();
m_textureCapping->EnableRepeat();
m_textureCapping->GetParams()->SetScale(Graphic3d_Vec2(0.05f, -0.05f));
diff --git a/src/app/widget_clip_planes.h b/src/app/widget_clip_planes.h
index 9375b667..13b15625 100644
--- a/src/app/widget_clip_planes.h
+++ b/src/app/widget_clip_planes.h
@@ -20,6 +20,7 @@ class QDoubleSpinBox;
namespace Mayo {
+// Widget panel dedicated to clip planes in 3D view
class WidgetClipPlanes : public QWidget {
Q_OBJECT
public:
diff --git a/src/app/widget_explode_assembly.h b/src/app/widget_explode_assembly.h
index c4f0af40..9ff605ef 100644
--- a/src/app/widget_explode_assembly.h
+++ b/src/app/widget_explode_assembly.h
@@ -12,6 +12,7 @@ namespace Mayo {
class GuiDocument;
+// Widget panel dedicated to exploding of assemblies within a GuiDocument object
class WidgetExplodeAssembly : public QWidget {
Q_OBJECT
public:
diff --git a/src/app/widget_grid.cpp b/src/app/widget_grid.cpp
new file mode 100644
index 00000000..3c521680
--- /dev/null
+++ b/src/app/widget_grid.cpp
@@ -0,0 +1,368 @@
+/****************************************************************************
+** Copyright (c) 2023, Fougue Ltd.
+** All rights reserved.
+** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt
+****************************************************************************/
+
+#include "widget_grid.h"
+#include "ui_widget_grid.h"
+#include "property_editor_factory.h"
+#include "qtgui_utils.h"
+#include "qtwidgets_utils.h"
+#include "../base/unit_system.h"
+#include "../graphics/graphics_utils.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Mayo {
+
+namespace {
+
+QPixmap colorSquarePixmap(const Quantity_Color& color) {
+ return IPropertyEditorFactory::colorSquarePixmap(QtGuiUtils::toQColor(color));
+}
+
+QPixmap colorSquarePixmap(const QColor& color) {
+ return IPropertyEditorFactory::colorSquarePixmap(color);
+}
+
+} // namespace
+
+WidgetGrid::WidgetGrid(GraphicsViewPtr viewPtr, QWidget* parent)
+ : QWidget(parent),
+ m_ui(new Ui_WidgetGrid),
+ m_viewPtr(viewPtr)
+{
+ const Handle_V3d_Viewer& viewer = viewPtr->Viewer();
+
+ // Intial configuration
+ m_ui->setupUi(this);
+
+ constexpr double maxFloat64 = std::numeric_limits::max();
+ const double valueEditorMaxWidth = m_ui->edit_RectSizeX->fontMetrics().averageCharWidth() * 15;
+ for (auto editor : this->findChildren()) {
+ editor->setRange(-maxFloat64, maxFloat64);
+ editor->setMaximumWidth(valueEditorMaxWidth);
+ }
+
+ m_ui->edit_RectRotation->setRange(-180, 180);
+ m_ui->edit_CircRotation->setRange(-180, 180);
+ m_ui->edit_RectStepX->setMinimum(0.);
+ m_ui->edit_RectStepY->setMinimum(0.);
+ m_ui->edit_RectSizeX->setMinimum(0.);
+ m_ui->edit_RectSizeY->setMinimum(0.);
+ m_ui->edit_CircRadiusStep->setMinimum(0.);
+ m_ui->edit_CircRadius->setMinimum(0.);
+
+ QtWidgetsUtils::collapseWidget(m_ui->widget_Config, true);
+ QtWidgetsUtils::collapseWidget(m_ui->widget_Graphics, true);
+
+ // Install grid visibility
+ m_ui->check_ShowGrid->setChecked(GraphicsUtils::V3dViewer_isGridActive(viewer));
+
+ // Install grid type
+ switch (viewer->GridType()) {
+ case Aspect_GT_Rectangular:
+ m_ui->combo_Type->setCurrentIndex(0);
+ m_ui->stack_Config->setCurrentWidget(m_ui->page_Rectangular);
+ break;
+ case Aspect_GT_Circular:
+ m_ui->combo_Type->setCurrentIndex(1);
+ m_ui->stack_Config->setCurrentWidget(m_ui->page_Circular);
+ break;
+ } // endswitch
+
+ // Install rectangular grid params
+ {
+ double xOrigin, yOrigin;
+ double xStep, yStep;
+ double rotAngle;
+ viewer->RectangularGridValues(xOrigin, yOrigin, xStep, yStep, rotAngle);
+ m_ui->edit_RectOriginX->setValue(xOrigin);
+ m_ui->edit_RectOriginY->setValue(yOrigin);
+ m_ui->edit_RectStepX->setValue(xStep);
+ m_ui->edit_RectStepY->setValue(yStep);
+ m_ui->edit_RectRotation->setValue(UnitSystem::degrees(rotAngle * Quantity_Radian));
+
+ double xSize, ySize;
+ double offset;
+ viewer->RectangularGridGraphicValues(xSize, ySize, offset);
+ m_ui->edit_RectSizeX->setValue(xSize);
+ m_ui->edit_RectSizeY->setValue(ySize);
+ m_ui->edit_RectOffset->setValue(offset);
+ }
+
+ // Install circular grid params
+ {
+ double xOrigin, yOrigin;
+ double radiusStep;
+ int divisionCount;
+ double rotAngle;
+ viewer->CircularGridValues(xOrigin, yOrigin, radiusStep, divisionCount, rotAngle);
+ m_ui->edit_CircOriginX->setValue(xOrigin);
+ m_ui->edit_CircOriginY->setValue(yOrigin);
+ m_ui->edit_CircRadiusStep->setValue(radiusStep);
+ m_ui->edit_CircDivision->setValue(divisionCount);
+ m_ui->edit_CircRotation->setValue(UnitSystem::degrees(rotAngle * Quantity_Radian));
+
+ double radius;
+ double offset;
+ viewer->CircularGridGraphicValues(radius, offset);
+ m_ui->edit_CircRadius->setValue(radius);
+ m_ui->edit_CircOffset->setValue(offset);
+ }
+
+ // Install grid privileged plane
+ const gp_Ax2 plane = viewer->PrivilegedPlane().Ax2();
+ if (plane.IsCoplanar(gp::XOY(), Precision::Confusion(), Precision::Angular()))
+ m_ui->combo_Plane->setCurrentIndex(0);
+ else if (plane.IsCoplanar(gp::ZOX(), Precision::Confusion(), Precision::Angular()))
+ m_ui->combo_Plane->setCurrentIndex(1);
+ else if (plane.IsCoplanar(gp::YOZ(), Precision::Confusion(), Precision::Angular()))
+ m_ui->combo_Plane->setCurrentIndex(2);
+ else
+ m_ui->combo_Plane->setCurrentIndex(3);
+
+ // Install grid draw mode
+ Handle_Aspect_Grid gridAspect = GraphicsUtils::V3dViewer_grid(viewer);
+ if (gridAspect) {
+ if (gridAspect->DrawMode() == Aspect_GDM_Lines)
+ m_ui->combo_DrawMode->setCurrentIndex(0);
+ else if (gridAspect->DrawMode() == Aspect_GDM_Points)
+ m_ui->combo_DrawMode->setCurrentIndex(1);
+ }
+
+ // Install grid draw colors
+ auto gridColors = GraphicsUtils::V3dViewer_gridColors(viewer);
+ m_ui->btn_Color->setIcon(colorSquarePixmap(gridColors.base));
+ m_ui->btn_ColorTenth->setIcon(colorSquarePixmap(gridColors.tenth));
+ m_gridColorTenth = gridColors.tenth;
+
+ // Install widgets enable status
+ m_ui->combo_Plane->setEnabled(GraphicsUtils::V3dViewer_isGridActive(viewer));
+ m_ui->widget_Main->setEnabled(GraphicsUtils::V3dViewer_isGridActive(viewer));
+ auto planeComboModel = static_cast(m_ui->combo_Plane->model());
+ if (planeComboModel)
+ planeComboModel->item(3)->setFlags(Qt::NoItemFlags); // Custom plane
+ else
+ qWarning() << Q_FUNC_INFO << "QComboBox model isn't of type QStandardItemModel";
+
+ // Signal/slot connections
+ auto sigComboBoxActivated_int = qOverload(&QComboBox::activated);
+ QObject::connect(
+ m_ui->check_ShowGrid, &QCheckBox::clicked, this, &WidgetGrid::activateGrid
+ );
+ QObject::connect(m_ui->combo_Type, sigComboBoxActivated_int, this, [=](int typeIndex) {
+ m_ui->stack_Config->setCurrentIndex(typeIndex);
+ auto gridColors = GraphicsUtils::V3dViewer_gridColors(viewer);
+ viewer->ActivateGrid(
+ toGridType(m_ui->combo_Type->currentIndex()),
+ toGridDrawMode(m_ui->combo_DrawMode->currentIndex())
+ );
+ GraphicsUtils::V3dViewer_setGridColors(viewer, gridColors);
+ m_viewPtr.redraw();
+ });
+ QObject::connect(m_ui->combo_Plane, sigComboBoxActivated_int, this, [=](int planeIndex) {
+ viewer->SetPrivilegedPlane(toPlaneAxis(planeIndex));
+ m_viewPtr.redraw();
+ });
+ QObject::connect(m_ui->combo_DrawMode, sigComboBoxActivated_int, this, [=](int modeIndex) {
+ GraphicsUtils::V3dViewer_grid(viewer)->SetDrawMode(toGridDrawMode(modeIndex));
+ m_viewPtr.redraw();
+ });
+ QObject::connect(m_ui->btn_Config, &QToolButton::clicked, this, [=](bool on) {
+ QtWidgetsUtils::collapseWidget(m_ui->widget_Config, !on);
+ if (on)
+ m_ui->label_Type->setMinimumWidth(m_ui->label_RectOrigin->width());
+
+ m_ui->btn_Config->setArrowType(on ? Qt::DownArrow : Qt::RightArrow);
+ emit this->sizeAdjustmentRequested();
+ });
+ QObject::connect(m_ui->btn_Graphics, &QToolButton::clicked, this, [=](bool on) {
+ QtWidgetsUtils::collapseWidget(m_ui->widget_Graphics, !on);
+ m_ui->btn_Graphics->setArrowType(on ? Qt::DownArrow : Qt::RightArrow);
+ emit this->sizeAdjustmentRequested();
+ });
+ QObject::connect(
+ m_ui->btn_Color, &QToolButton::clicked, this, [=]{ this->chooseGridColor(GridColorType::Base); }
+ );
+ QObject::connect(
+ m_ui->btn_ColorTenth, &QToolButton::clicked, this, [=]{ this->chooseGridColor(GridColorType::Tenth); }
+ );
+ QObject::connect(
+ m_ui->check_ColorTenth, &QAbstractButton::toggled, this, &WidgetGrid::enableGridColorTenth
+ );
+
+ auto sigGridParamChanged_double = qOverload(&QDoubleSpinBox::valueChanged);
+ auto sigGridParamChanged_int = qOverload(&QSpinBox::valueChanged);
+ QObject::connect(m_ui->edit_RectOriginX, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_RectOriginY, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_RectStepX, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_RectStepY, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_RectRotation, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_RectSizeX, sigGridParamChanged_double, this, &WidgetGrid::applyGridGraphicsParams);
+ QObject::connect(m_ui->edit_RectSizeY, sigGridParamChanged_double, this, &WidgetGrid::applyGridGraphicsParams);
+ QObject::connect(m_ui->edit_RectOffset, sigGridParamChanged_double, this, &WidgetGrid::applyGridGraphicsParams);
+
+ QObject::connect(m_ui->edit_CircOriginX, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_CircOriginY, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_CircRadiusStep, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_CircDivision, sigGridParamChanged_int, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_CircRotation, sigGridParamChanged_double, this, &WidgetGrid::applyGridParams);
+ QObject::connect(m_ui->edit_CircRadius, sigGridParamChanged_double, this, &WidgetGrid::applyGridGraphicsParams);
+ QObject::connect(m_ui->edit_CircOffset, sigGridParamChanged_double, this, &WidgetGrid::applyGridGraphicsParams);
+}
+
+WidgetGrid::~WidgetGrid()
+{
+ delete m_ui;
+}
+
+Aspect_GridType WidgetGrid::toGridType(int comboBoxItemIndex)
+{
+ return comboBoxItemIndex == 0 ? Aspect_GT_Rectangular : Aspect_GT_Circular;
+}
+
+Aspect_GridDrawMode WidgetGrid::toGridDrawMode(int comboBoxItemIndex)
+{
+ switch (comboBoxItemIndex) {
+ case 0: return Aspect_GDM_Lines;
+ case 1: return Aspect_GDM_Points;
+ default: return Aspect_GDM_None;
+ }
+}
+
+const gp_Ax2& WidgetGrid::toPlaneAxis(int comboBoxItemIndex)
+{
+ switch (comboBoxItemIndex) {
+ case 0: return gp::XOY();
+ case 1: return gp::ZOX();
+ case 2: return gp::YOZ();
+ default: return gp::XOY();
+ }
+}
+
+void WidgetGrid::activateGrid(bool on)
+{
+ const Handle_V3d_Viewer& viewer = m_viewPtr->Viewer();
+ if (on) {
+ viewer->ActivateGrid(
+ toGridType(m_ui->combo_Type->currentIndex()),
+ toGridDrawMode(m_ui->combo_DrawMode->currentIndex())
+ );
+ }
+ else {
+ viewer->DeactivateGrid();
+ }
+
+ m_viewPtr.redraw();
+ m_ui->combo_Plane->setEnabled(on);
+ m_ui->widget_Main->setEnabled(on);
+}
+
+void WidgetGrid::applyGridParams()
+{
+ auto fnCorrectedGridStep = [](double gridStep) {
+ return !qFuzzyIsNull(gridStep) ? gridStep : 0.01;
+ };
+ const Handle_V3d_Viewer& viewer = m_viewPtr->Viewer();
+ auto gridType = toGridType(m_ui->combo_Type->currentIndex());
+ if (gridType == Aspect_GT_Rectangular) {
+ viewer->SetRectangularGridValues(
+ m_ui->edit_RectOriginX->value(),
+ m_ui->edit_RectOriginY->value(),
+ fnCorrectedGridStep(m_ui->edit_RectStepX->value()),
+ fnCorrectedGridStep(m_ui->edit_RectStepY->value()),
+ UnitSystem::radians(m_ui->edit_RectRotation->value() * Quantity_Degree)
+ );
+ }
+ else if (gridType == Aspect_GT_Circular) {
+ viewer->SetCircularGridValues(
+ m_ui->edit_CircOriginX->value(),
+ m_ui->edit_CircOriginY->value(),
+ fnCorrectedGridStep(m_ui->edit_CircRadiusStep->value()),
+ m_ui->edit_CircDivision->value(),
+ UnitSystem::radians(m_ui->edit_CircRotation->value() * Quantity_Degree)
+ );
+ }
+
+ m_viewPtr.redraw();
+}
+
+void WidgetGrid::applyGridGraphicsParams()
+{
+ const Handle_V3d_Viewer& viewer = m_viewPtr->Viewer();
+ auto gridType = toGridType(m_ui->combo_Type->currentIndex());
+ if (gridType == Aspect_GT_Rectangular) {
+ viewer->SetRectangularGridGraphicValues(
+ m_ui->edit_RectSizeX->value(),
+ m_ui->edit_RectSizeY->value(),
+ m_ui->edit_RectOffset->value()
+ );
+ }
+ else if (gridType == Aspect_GT_Circular) {
+ viewer->SetCircularGridGraphicValues(
+ m_ui->edit_CircRadius->value(),
+ m_ui->edit_RectOffset->value()
+ );
+ }
+
+ m_viewPtr.redraw();
+}
+
+void WidgetGrid::chooseGridColor(GridColorType colorType)
+{
+ const Handle_V3d_Viewer& viewer = m_viewPtr->Viewer();
+ auto gridColors = GraphicsUtils::V3dViewer_gridColors(viewer);
+ // Helper function to apply some base/tenth grid color
+ auto fnApplyGridColor = [=](const Quantity_Color& color) {
+ if (colorType == GridColorType::Base) {
+ const auto colorTenth = m_ui->check_ColorTenth->isChecked() ? gridColors.tenth : color;
+ GraphicsUtils::V3dViewer_setGridColors(viewer, { color, colorTenth });
+ }
+ else {
+ GraphicsUtils::V3dViewer_setGridColors(viewer, { gridColors.base, color });
+ }
+
+ m_viewPtr.redraw();
+ };
+
+ // Setup dialog to select a color
+ auto dlg = new QColorDialog(this);
+ auto onEntryGridColor = colorType == GridColorType::Base ? gridColors.base : gridColors.tenth;
+ dlg->setCurrentColor(QtGuiUtils::toQColor(onEntryGridColor));
+
+ QObject::connect(dlg, &QColorDialog::currentColorChanged, this, [=](const QColor& color) {
+ fnApplyGridColor(QtGuiUtils::toColor(color));
+ });
+ QObject::connect(dlg, &QDialog::accepted, this, [=]{
+ auto btn = colorType == GridColorType::Base ? m_ui->btn_Color : m_ui->btn_ColorTenth;
+ btn->setIcon(colorSquarePixmap(dlg->selectedColor()));
+ if (colorType == GridColorType::Tenth)
+ m_gridColorTenth = QtGuiUtils::toColor(dlg->selectedColor());
+ });
+ QObject::connect(dlg, &QDialog::rejected, this, [=]{
+ auto btn = colorType == GridColorType::Base ? m_ui->btn_Color : m_ui->btn_ColorTenth;
+ btn->setIcon(colorSquarePixmap(onEntryGridColor));
+ fnApplyGridColor(onEntryGridColor);
+ });
+
+ QtWidgetsUtils::asyncDialogExec(dlg);
+}
+
+void WidgetGrid::enableGridColorTenth(bool on)
+{
+ const Handle_V3d_Viewer& viewer = m_viewPtr->Viewer();
+ m_ui->label_ColorTenth->setEnabled(on);
+ m_ui->btn_ColorTenth->setEnabled(on);
+ auto gridColors = GraphicsUtils::V3dViewer_gridColors(viewer);
+ const auto gridColorTenth = on ? m_gridColorTenth : gridColors.base;
+ GraphicsUtils::V3dViewer_setGridColors(viewer, { gridColors.base, gridColorTenth });
+ m_viewPtr.redraw();
+}
+
+} // namespace Mayo
diff --git a/src/app/widget_grid.h b/src/app/widget_grid.h
new file mode 100644
index 00000000..d1d0b2e5
--- /dev/null
+++ b/src/app/widget_grid.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+** Copyright (c) 2023, Fougue Ltd.
+** All rights reserved.
+** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt
+****************************************************************************/
+
+#pragma once
+
+#include "../graphics/graphics_view_ptr.h"
+
+#include
+class gp_Ax2;
+
+namespace Mayo {
+
+// Widget panel dedicated to management of the grid in 3D view
+class WidgetGrid : public QWidget {
+ Q_OBJECT
+public:
+ WidgetGrid(GraphicsViewPtr viewPtr, QWidget* parent = nullptr);
+ ~WidgetGrid();
+
+signals:
+ void sizeAdjustmentRequested();
+
+private:
+ enum class GridColorType { Base, Tenth };
+
+ static Aspect_GridType toGridType(int comboBoxItemIndex);
+ static Aspect_GridDrawMode toGridDrawMode(int comboBoxItemIndex);
+ static const gp_Ax2& toPlaneAxis(int comboBoxItemIndex);
+
+ void activateGrid(bool on);
+ void applyGridParams();
+ void applyGridGraphicsParams();
+ void chooseGridColor(GridColorType colorType);
+ void enableGridColorTenth(bool on);
+
+ class Ui_WidgetGrid* m_ui = nullptr;
+ GraphicsViewPtr m_viewPtr;
+ Quantity_Color m_gridColorTenth;
+};
+
+} // namespace Mayo
diff --git a/src/app/widget_grid.ui b/src/app/widget_grid.ui
new file mode 100644
index 00000000..388d0078
--- /dev/null
+++ b/src/app/widget_grid.ui
@@ -0,0 +1,738 @@
+
+
+ Mayo::WidgetGrid
+
+
+
+ 0
+ 0
+ 196
+ 336
+
+
+
+ Form
+
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+ -
+
+
-
+
+
+ Show Grid
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
-
+
+ Plane: XOY
+
+
+ -
+
+ Plane: ZOX
+
+
+ -
+
+ Plane: YOZ
+
+
+ -
+
+ Plane: Custom
+
+
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ 2
+
+
-
+
+
-
+
+
+ Configuration
+
+
+ true
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+ Qt::RightArrow
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 100
+ 20
+
+
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
-
+
+
+ Type
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
-
+
+ Rectangular
+
+
+ -
+
+ Circular
+
+
+
+
+ -
+
+
+ 1
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
-
+
+
+
+ 1
+ 0
+
+
+
+ Y
+
+
+
+ -
+
+
+ Step
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ X
+
+
+ 0.010000000000000
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ Y
+
+
+ 0.010000000000000
+
+
+
+ -
+
+
+ Size
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ X
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ Y
+
+
+
+ -
+
+
+ Rotation
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ °
+
+
+ -180.000000000000000
+
+
+ 180.000000000000000
+
+
+ 5.000000000000000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Offset
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ X
+
+
+
+ -
+
+
+ Origin
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+ -
+
+
+ Origin
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ X
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ Y
+
+
+
+ -
+
+
+ Radius
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+ Radius Step
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+ Division
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ -
+
+
+ Rotation
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ °
+
+
+ -180.000000000000000
+
+
+ 180.000000000000000
+
+
+ 5.000000000000000
+
+
+
+ -
+
+
+ Offset
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
-
+
+
+ Graphics
+
+
+ true
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+ Qt::RightArrow
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 100
+ 20
+
+
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
-
+
+
+ ...
+
+
+
+ -
+
+
+ Tenth Color
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Mode
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
-
+
+ Lines
+
+
+ -
+
+ Points
+
+
+
+
+ -
+
+
+ Color
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ check_ShowGrid
+ combo_Plane
+ btn_Config
+ combo_Type
+ edit_RectOriginX
+ edit_RectOriginY
+ edit_RectStepX
+ edit_RectStepY
+ edit_RectSizeX
+ edit_RectSizeY
+ edit_RectRotation
+ edit_RectOffset
+ btn_Graphics
+ combo_DrawMode
+ btn_Color
+ btn_ColorTenth
+ edit_CircOriginX
+ edit_CircOriginY
+ edit_CircRadius
+ edit_CircRadiusStep
+ edit_CircDivision
+ edit_CircRotation
+ edit_CircOffset
+
+
+
+
diff --git a/src/app/widget_gui_document.cpp b/src/app/widget_gui_document.cpp
index 949bb4d4..66bc1c5e 100644
--- a/src/app/widget_gui_document.cpp
+++ b/src/app/widget_gui_document.cpp
@@ -15,6 +15,7 @@
#include "theme.h"
#include "widget_clip_planes.h"
#include "widget_explode_assembly.h"
+#include "widget_grid.h"
#include "widget_measure.h"
#include "widget_occ_view.h"
#include "widget_occ_view_controller.h"
@@ -145,16 +146,24 @@ WidgetGuiDocument::WidgetGuiDocument(GuiDocument* guiDoc, QWidget* parent)
auto layoutBtns = new QHBoxLayout(widgetBtnsContents);
layoutBtns->setSpacing(Internal_widgetMargin + 2);
layoutBtns->setContentsMargins(2, 2, 2, 2);
+
m_btnFitAll = this->createViewBtn(widgetBtnsContents, Theme::Icon::Expand, tr("Fit All"));
+
+ m_btnGrid = this->createViewBtn(widgetBtnsContents, Theme::Icon::Grid, tr("Edit Grid"));
+ m_btnGrid->setCheckable(true);
+
m_btnEditClipping = this->createViewBtn(widgetBtnsContents, Theme::Icon::ClipPlane, tr("Edit clip planes"));
m_btnEditClipping->setCheckable(true);
+
m_btnExplode = this->createViewBtn(widgetBtnsContents, Theme::Icon::Multiple, tr("Explode assemblies"));
m_btnExplode->setCheckable(true);
+
m_btnMeasure = this->createViewBtn(widgetBtnsContents, Theme::Icon::Measure, tr("Measure shapes"));
m_btnMeasure->setCheckable(true);
layoutBtns->addWidget(m_btnFitAll);
this->recreateMenuViewProjections(widgetBtnsContents);
+ layoutBtns->addWidget(m_btnGrid);
layoutBtns->addWidget(m_btnEditClipping);
layoutBtns->addWidget(m_btnExplode);
layoutBtns->addWidget(m_btnMeasure);
@@ -168,6 +177,10 @@ WidgetGuiDocument::WidgetGuiDocument(GuiDocument* guiDoc, QWidget* parent)
QObject::connect(m_btnFitAll, &ButtonFlat::clicked, this, [=]{
m_guiDoc->runViewCameraAnimation(&GraphicsUtils::V3dView_fitAll);
});
+ QObject::connect(
+ m_btnGrid, &ButtonFlat::checked,
+ this, &WidgetGuiDocument::toggleWidgetGrid
+ );
QObject::connect(
m_btnEditClipping, &ButtonFlat::checked,
this, &WidgetGuiDocument::toggleWidgetClipPlanes
@@ -218,6 +231,7 @@ void WidgetGuiDocument::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
this->layoutViewControls();
+ this->layoutWidgetPanel(m_widgetGrid);
this->layoutWidgetPanel(m_widgetClipPlanes);
this->layoutWidgetPanel(m_widgetExplodeAsm);
this->layoutWidgetPanel(m_widgetMeasure);
@@ -241,6 +255,28 @@ void WidgetGuiDocument::updageWidgetPanelControls(QWidget* panelWidget, ButtonFl
}
}
+void adjustWidgetSize(QWidget* widget)
+{
+ widget->updateGeometry();
+ if (static_cast(widget->parentWidget()))
+ widget->parentWidget()->adjustSize();
+}
+
+void WidgetGuiDocument::toggleWidgetGrid(bool on)
+{
+ if (!m_widgetGrid && on) {
+ m_widgetGrid = new WidgetGrid(m_guiDoc->graphicsView());
+ auto container = this->createWidgetPanelContainer(m_widgetGrid);
+ QObject::connect(
+ m_widgetGrid, &WidgetGrid::sizeAdjustmentRequested,
+ container, [=]{ adjustWidgetSize(m_widgetGrid); },
+ Qt::QueuedConnection
+ );
+ }
+
+ this->updageWidgetPanelControls(m_widgetGrid, m_btnGrid);
+}
+
void WidgetGuiDocument::toggleWidgetClipPlanes(bool on)
{
if (m_widgetClipPlanes) {
@@ -273,7 +309,7 @@ void WidgetGuiDocument::toggleWidgetMeasure(bool on)
auto container = this->createWidgetPanelContainer(m_widgetMeasure);
QObject::connect(
m_widgetMeasure, &WidgetMeasure::sizeAdjustmentRequested,
- container, &QWidget::adjustSize,
+ container, [=]{ adjustWidgetSize(m_widgetMeasure); },
Qt::QueuedConnection
);
}
@@ -289,7 +325,7 @@ void WidgetGuiDocument::exclusiveButtonCheck(ButtonFlat* btnCheck)
if (!btnCheck || !btnCheck->isChecked())
return;
- ButtonFlat* arrayToggleBtn[] = { m_btnEditClipping, m_btnExplode, m_btnMeasure };
+ ButtonFlat* arrayToggleBtn[] = { m_btnGrid, m_btnEditClipping, m_btnExplode, m_btnMeasure };
for (ButtonFlat* btn : arrayToggleBtn) {
assert(btn->isCheckable());
if (btn != btnCheck)
diff --git a/src/app/widget_gui_document.h b/src/app/widget_gui_document.h
index bdc4abf2..c26456ec 100644
--- a/src/app/widget_gui_document.h
+++ b/src/app/widget_gui_document.h
@@ -20,6 +20,7 @@ class ButtonFlat;
class GuiDocument;
class WidgetClipPlanes;
class WidgetExplodeAssembly;
+class WidgetGrid;
class WidgetMeasure;
class IWidgetOccView;
@@ -43,6 +44,7 @@ class WidgetGuiDocument : public QWidget {
QWidget* createWidgetPanelContainer(QWidget* widgetContents);
void updageWidgetPanelControls(QWidget* panelWidget, ButtonFlat* btnPanel);
+ void toggleWidgetGrid(bool on);
void toggleWidgetClipPlanes(bool on);
void toggleWidgetExplode(bool on);
void toggleWidgetMeasure(bool on);
@@ -61,10 +63,12 @@ class WidgetGuiDocument : public QWidget {
WidgetOccViewController* m_controller = nullptr;
WidgetClipPlanes* m_widgetClipPlanes = nullptr;
WidgetExplodeAssembly* m_widgetExplodeAsm = nullptr;
+ WidgetGrid* m_widgetGrid = nullptr;
WidgetMeasure* m_widgetMeasure = nullptr;
QRect m_rectControls;
ButtonFlat* m_btnFitAll = nullptr;
+ ButtonFlat* m_btnGrid = nullptr;
ButtonFlat* m_btnEditClipping = nullptr;
ButtonFlat* m_btnExplode = nullptr;
ButtonFlat* m_btnMeasure = nullptr;
diff --git a/src/app/widget_home_files.h b/src/app/widget_home_files.h
index 1f02721f..93cdb222 100644
--- a/src/app/widget_home_files.h
+++ b/src/app/widget_home_files.h
@@ -20,7 +20,7 @@ class WidgetHomeFiles : public QWidget {
signals:
void newDocumentRequested();
void openDocumentsRequested();
- void recentFileOpenRequested(const FilePath& fp);
+ void recentFileOpenRequested(const Mayo::FilePath& fp);
protected:
void resizeEvent(QResizeEvent* event) override;
diff --git a/src/app/widget_main_control.cpp b/src/app/widget_main_control.cpp
new file mode 100644
index 00000000..51eca635
--- /dev/null
+++ b/src/app/widget_main_control.cpp
@@ -0,0 +1,419 @@
+/****************************************************************************
+** Copyright (c) 2023, Fougue Ltd.
+** All rights reserved.
+** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt
+****************************************************************************/
+
+#include "widget_main_control.h"
+#include "ui_widget_main_control.h"
+
+#include "../base/application.h"
+#include "../graphics/graphics_utils.h"
+#include "../gui/gui_application.h"
+
+#include "app_module.h"
+#include "commands_api.h"
+#include "commands_file.h"
+#include "commands_window.h"
+#include "document_property_group.h"
+#include "filepath_conv.h"
+#include "gui_document_list_model.h"
+#include "item_view_buttons.h"
+#include "qstring_conv.h"
+#include "theme.h"
+#include "widget_file_system.h"
+#include "widget_gui_document.h"
+#include "widget_model_tree.h"
+#include "widget_occ_view.h"
+#include "widget_properties_editor.h"
+
+#include
+#include
+#include
+
+namespace Mayo {
+
+WidgetMainControl::WidgetMainControl(GuiApplication* guiApp, QWidget* parent)
+ : IWidgetMainPage(parent),
+ m_ui(new Ui_WidgetMainControl),
+ m_guiApp(guiApp)
+{
+ assert(m_guiApp != nullptr);
+
+ m_ui->setupUi(this);
+
+ m_ui->widget_ModelTree->registerGuiApplication(guiApp);
+
+ m_ui->splitter_Main->setChildrenCollapsible(false);
+ m_ui->splitter_Main->setStretchFactor(0, 1);
+ m_ui->splitter_Main->setStretchFactor(1, 3);
+
+ m_ui->splitter_ModelTree->setStretchFactor(0, 1);
+ m_ui->splitter_ModelTree->setStretchFactor(1, 2);
+
+ m_ui->stack_LeftContents->setCurrentIndex(0);
+
+ m_ui->widget_Properties->setRowHeightFactor(1.4);
+ m_ui->widget_Properties->clear();
+
+ mayoTheme()->setupHeaderComboBox(m_ui->combo_LeftContents);
+ mayoTheme()->setupHeaderComboBox(m_ui->combo_GuiDocuments);
+
+ // "Window" actions and navigation in documents
+ QObject::connect(
+ m_ui->combo_GuiDocuments, qOverload(&QComboBox::currentIndexChanged),
+ this, &WidgetMainControl::onCurrentDocumentIndexChanged
+ );
+ QObject::connect(
+ m_ui->widget_FileSystem, &WidgetFileSystem::locationActivated,
+ this, &WidgetMainControl::onWidgetFileSystemLocationActivated
+ );
+ // ...
+ QObject::connect(
+ m_ui->combo_LeftContents, qOverload(&QComboBox::currentIndexChanged),
+ this, &WidgetMainControl::onLeftContentsPageChanged
+ );
+ QObject::connect(
+ m_ui->listView_OpenedDocuments, &QListView::clicked,
+ this, [=](const QModelIndex& index) { this->setCurrentDocumentIndex(index.row()); }
+ );
+
+ guiApp->application()->signalDocumentFilePathChanged.connectSlot([=](const DocumentPtr& doc, const FilePath& fp) {
+ if (this->currentWidgetGuiDocument()->documentIdentifier() == doc->identifier())
+ m_ui->widget_FileSystem->setLocation(filepathTo(fp));
+ });
+ guiApp->selectionModel()->signalChanged.connectSlot(&WidgetMainControl::onApplicationItemSelectionChanged, this);
+ guiApp->signalGuiDocumentAdded.connectSlot(&WidgetMainControl::onGuiDocumentAdded, this);
+
+ // Creation of annex objects
+ m_listViewBtns = new ItemViewButtons(m_ui->listView_OpenedDocuments, this);
+ m_listViewBtns->installDefaultItemDelegate();
+
+ // BEWARE MainWindow::onGuiDocumentAdded() must be called before
+ // MainWindow::onCurrentDocumentIndexChanged()
+ auto guiDocModel = new GuiDocumentListModel(guiApp, this);
+ m_ui->combo_GuiDocuments->setModel(guiDocModel);
+ m_ui->listView_OpenedDocuments->setModel(guiDocModel);
+
+ // Finalize setup
+ m_ui->widget_LeftHeader->installEventFilter(this);
+ m_ui->widget_ControlGuiDocuments->installEventFilter(this);
+ m_ui->stack_GuiDocuments->installEventFilter(this);
+ this->onLeftContentsPageChanged(m_ui->stack_LeftContents->currentIndex());
+ m_ui->widget_MouseCoords->hide();
+
+ this->onCurrentDocumentIndexChanged(-1);
+}
+
+WidgetMainControl::~WidgetMainControl()
+{
+ delete m_ui;
+}
+
+void WidgetMainControl::initialize(const CommandContainer* cmdContainer)
+{
+ assert(cmdContainer != nullptr);
+
+ m_appContext = cmdContainer->appContext();
+ auto fnFindAction = [=](std::string_view cmdName) {
+ QAction* action = cmdContainer->findCommandAction(cmdName);
+ assert(action != nullptr);
+ return action;
+ };
+ m_ui->btn_PreviousGuiDocument->setDefaultAction(fnFindAction(CommandPreviousDocument::Name));
+ m_ui->btn_NextGuiDocument->setDefaultAction(fnFindAction(CommandNextDocument::Name));
+ m_ui->btn_CloseGuiDocument->setDefaultAction(fnFindAction(CommandCloseCurrentDocument::Name));
+ m_ui->btn_CloseLeftSideBar->setDefaultAction(fnFindAction(CommandLeftSidebarWidgetToggle::Name));
+
+ // Opened documents GUI
+ auto actionCloseDoc = fnFindAction(CommandCloseCurrentDocument::Name);
+ m_listViewBtns->addButton(1, actionCloseDoc->icon(), actionCloseDoc->toolTip());
+ m_listViewBtns->setButtonDetection(1, -1, QVariant());
+ m_listViewBtns->setButtonDisplayColumn(1, 0);
+ m_listViewBtns->setButtonDisplayModes(1, ItemViewButtons::DisplayOnDetection);
+ m_listViewBtns->setButtonItemSide(1, ItemViewButtons::ItemRightSide);
+ const int iconSize = this->style()->pixelMetric(QStyle::PM_ListViewIconSize);
+ m_listViewBtns->setButtonIconSize(1, QSize(iconSize * 0.66, iconSize * 0.66));
+ QObject::connect(m_listViewBtns, &ItemViewButtons::buttonClicked, this, [=](int btnId, QModelIndex index) {
+ if (btnId == 1) {
+ assert(this->widgetGuiDocument(index.row()) != nullptr);
+ FileCommandTools::closeDocument(
+ cmdContainer->appContext(),
+ this->widgetGuiDocument(index.row())->documentIdentifier()
+ );
+ }
+ });
+}
+
+void WidgetMainControl::updatePageControlsActivation()
+{
+ const int appDocumentsCount = m_guiApp->application()->documentCount();
+ const bool appDocumentsEmpty = appDocumentsCount == 0;
+ m_ui->combo_GuiDocuments->setEnabled(!appDocumentsEmpty);
+}
+
+QWidget* WidgetMainControl::widgetLeftSideBar() const
+{
+ return m_ui->widget_Left;
+}
+
+bool WidgetMainControl::eventFilter(QObject* watched, QEvent* event)
+{
+ auto fnSizeBtn = [](const QWidget* container, const QWidget* widgetHeightRef) {
+ const int btnSideLen = widgetHeightRef->frameGeometry().height();
+ const QList listBtn = container->findChildren();
+ for (QAbstractButton* btn : listBtn)
+ btn->setFixedSize(btnSideLen, btnSideLen);
+ };
+ const QEvent::Type eventType = event->type();
+ if (watched == m_ui->widget_ControlGuiDocuments && eventType == QEvent::Show) {
+ fnSizeBtn(m_ui->widget_ControlGuiDocuments, m_ui->combo_GuiDocuments);
+ return true;
+ }
+
+ if (watched == m_ui->widget_LeftHeader && eventType == QEvent::Show) {
+ fnSizeBtn(m_ui->widget_LeftHeader, m_ui->combo_LeftContents);
+ return true;
+ }
+
+ if (watched == m_ui->stack_GuiDocuments) {
+ if (eventType == QEvent::Enter || eventType == QEvent::Leave) {
+ m_ui->widget_MouseCoords->setHidden(eventType == QEvent::Leave);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QMenu* WidgetMainControl::createMenuModelTreeSettings()
+{
+ auto menu = new QMenu(this->findLeftHeaderPlaceHolder());
+ menu->setToolTipsVisible(true);
+
+ // Link with document selector
+ auto appModule = AppModule::get();
+ QAction* action = menu->addAction(to_QString(appModule->properties()->linkWithDocumentSelector.name().tr()));
+ action->setCheckable(true);
+ QObject::connect(action, &QAction::triggered, this, [=](bool on) {
+ appModule->properties()->linkWithDocumentSelector.setValue(on);
+ });
+
+ // Model tree user actions
+ menu->addSeparator();
+ const WidgetModelTree_UserActions userActions = m_ui->widget_ModelTree->createUserActions(menu);
+ for (QAction* usrAction : userActions.items)
+ menu->addAction(usrAction);
+
+ // Sync before menu show
+ QObject::connect(menu, &QMenu::aboutToShow, this, [=]{
+ action->setChecked(appModule->properties()->linkWithDocumentSelector);
+ if (userActions.fnSyncItems)
+ userActions.fnSyncItems();
+ });
+
+ return menu;
+}
+
+void WidgetMainControl::onApplicationItemSelectionChanged()
+{
+ WidgetModelTree* uiModelTree = m_ui->widget_ModelTree;
+ WidgetPropertiesEditor* uiProps = m_ui->widget_Properties;
+
+ uiProps->clear();
+ Span spanAppItem = m_guiApp->selectionModel()->selectedItems();
+ if (spanAppItem.size() == 1) {
+ const ApplicationItem& appItem = spanAppItem.front();
+ if (appItem.isDocument()) {
+ auto dataProps = new DocumentPropertyGroup(appItem.document());
+ uiProps->editProperties(dataProps, uiProps->addGroup(tr("Data")));
+ m_ptrCurrentNodeDataProperties.reset(dataProps);
+ }
+ else if (appItem.isDocumentTreeNode()) {
+ const DocumentTreeNode& docTreeNode = appItem.documentTreeNode();
+ auto dataProps = AppModule::get()->properties(docTreeNode);
+ if (dataProps) {
+ uiProps->editProperties(dataProps.get(), uiProps->addGroup(tr("Data")));
+ dataProps->signalPropertyChanged.connectSlot([=]{ uiModelTree->refreshItemText(appItem); });
+ m_ptrCurrentNodeDataProperties = std::move(dataProps);
+ }
+
+ GuiDocument* guiDoc = m_guiApp->findGuiDocument(appItem.document());
+ std::vector vecGfxObject;
+ guiDoc->foreachGraphicsObject(docTreeNode.id(), [&](GraphicsObjectPtr gfxObject) {
+ vecGfxObject.push_back(std::move(gfxObject));
+ });
+ auto commonGfxDriver = GraphicsObjectDriver::getCommon(vecGfxObject);
+ if (commonGfxDriver) {
+ auto gfxProps = commonGfxDriver->properties(vecGfxObject);
+ if (gfxProps) {
+ uiProps->editProperties(gfxProps.get(), uiProps->addGroup(tr("Graphics")));
+ gfxProps->signalPropertyChanged.connectSlot([=]{ guiDoc->graphicsScene()->redraw(); });
+ m_ptrCurrentNodeGraphicsProperties = std::move(gfxProps);
+ }
+ }
+ }
+
+ auto app = m_guiApp->application();
+ if (AppModule::get()->properties()->linkWithDocumentSelector) {
+ const int index = app->findIndexOfDocument(appItem.document());
+ if (index != -1)
+ this->setCurrentDocumentIndex(index);
+ }
+ }
+ else {
+ // TODO
+ uiProps->clear();
+ }
+
+ emit this->updateGlobalControlsActivationRequired();
+}
+
+
+void WidgetMainControl::onLeftContentsPageChanged(int pageId)
+{
+ m_ui->stack_LeftContents->setCurrentIndex(pageId);
+ QWidget* placeHolder = this->recreateLeftHeaderPlaceHolder();
+ if (m_ui->stack_LeftContents->currentWidget() == m_ui->page_ModelTree && placeHolder) {
+ const int btnSideLen = m_ui->combo_LeftContents->frameGeometry().height();
+ auto btnSettings = new QToolButton(placeHolder);
+ btnSettings->setAutoRaise(true);
+ btnSettings->setFixedSize(btnSideLen, btnSideLen);
+ btnSettings->setIcon(mayoTheme()->icon(Theme::Icon::Gear));
+ btnSettings->setToolTip(tr("Options"));
+ placeHolder->layout()->addWidget(btnSettings);
+ btnSettings->setMenu(this->createMenuModelTreeSettings());
+ btnSettings->setPopupMode(QToolButton::InstantPopup);
+ }
+ else {
+ delete placeHolder;
+ }
+}
+
+QWidget* WidgetMainControl::findLeftHeaderPlaceHolder() const
+{
+ return m_ui->widget_LeftHeader->findChild(
+ "LeftHeaderPlaceHolder", Qt::FindDirectChildrenOnly
+ );
+}
+
+QWidget* WidgetMainControl::recreateLeftHeaderPlaceHolder()
+{
+ QWidget* placeHolder = this->findLeftHeaderPlaceHolder();
+ delete placeHolder;
+ placeHolder = new QWidget(m_ui->widget_LeftHeader);
+ placeHolder->setObjectName("LeftHeaderPlaceHolder");
+ auto layoutPlaceHolder = new QHBoxLayout(placeHolder);
+ layoutPlaceHolder->setContentsMargins(0, 0, 0, 0);
+ layoutPlaceHolder->setSpacing(0);
+ m_ui->Layout_WidgetLeftHeader->insertWidget(2, placeHolder);
+ return placeHolder;
+}
+
+WidgetGuiDocument* WidgetMainControl::widgetGuiDocument(int idx) const
+{
+ assert(idx == -1 || (0 <= idx && idx < m_ui->stack_GuiDocuments->count()));
+ return qobject_cast(m_ui->stack_GuiDocuments->widget(idx));
+}
+
+WidgetGuiDocument* WidgetMainControl::currentWidgetGuiDocument() const
+{
+ return this->widgetGuiDocument(this->currentDocumentIndex());
+}
+
+int WidgetMainControl::indexOfWidgetGuiDocument(WidgetGuiDocument* widgetDoc) const
+{
+ return m_ui->stack_GuiDocuments->indexOf(widgetDoc);
+}
+
+void WidgetMainControl::removeWidgetGuiDocument(WidgetGuiDocument* widgetDoc)
+{
+ if (widgetDoc) {
+ m_ui->stack_GuiDocuments->removeWidget(widgetDoc);
+ widgetDoc->deleteLater();
+ }
+}
+
+int WidgetMainControl::widgetGuiDocumentCount() const
+{
+ return m_ui->stack_GuiDocuments->count();
+}
+
+void WidgetMainControl::onGuiDocumentAdded(GuiDocument* guiDoc)
+{
+ auto gfxScene = guiDoc->graphicsScene();
+
+ // Configure 3D view behavior with respect to application settings
+ auto appModule = AppModule::get();
+ auto appProps = appModule->properties();
+ auto widget = new WidgetGuiDocument(guiDoc);
+ guiDoc->setDevicePixelRatio(widget->devicePixelRatioF());
+ auto widgetCtrl = widget->controller();
+ widgetCtrl->setInstantZoomFactor(appProps->instantZoomFactor);
+ widgetCtrl->setNavigationStyle(appProps->navigationStyle);
+ if (appProps->defaultShowOriginTrihedron) {
+ guiDoc->toggleOriginTrihedronVisibility();
+ gfxScene->redraw();
+ }
+
+ appModule->settings()->signalChanged.connectSlot([=](const Property* setting) {
+ if (setting == &appProps->instantZoomFactor)
+ widgetCtrl->setInstantZoomFactor(appProps->instantZoomFactor);
+ else if (setting == &appProps->navigationStyle)
+ widgetCtrl->setNavigationStyle(appProps->navigationStyle);
+ });
+
+ // React to mouse move in 3D view:
+ // * update highlighting
+ // * compute and display 3D mouse coordinates(by silent picking)
+ widgetCtrl->signalMouseMoved.connectSlot([=](int xPos, int yPos) {
+ const double dpRatio = this->devicePixelRatioF();
+ gfxScene->highlightAt(xPos * dpRatio, yPos * dpRatio, guiDoc->v3dView());
+ widget->view()->redraw();
+ auto selector = gfxScene->mainSelector();
+ selector->Pick(xPos, yPos, guiDoc->v3dView());
+ const gp_Pnt pos3d =
+ selector->NbPicked() > 0 ?
+ selector->PickedPoint(1) :
+ GraphicsUtils::V3dView_to3dPosition(guiDoc->v3dView(), xPos, yPos);
+ m_ui->label_ValuePosX->setText(QString::number(pos3d.X(), 'f', 3));
+ m_ui->label_ValuePosY->setText(QString::number(pos3d.Y(), 'f', 3));
+ m_ui->label_ValuePosZ->setText(QString::number(pos3d.Z(), 'f', 3));
+ });
+
+ m_ui->stack_GuiDocuments->addWidget(widget);
+ const int newDocIndex = m_guiApp->application()->documentCount() - 1;
+ QTimer::singleShot(0, this, [=]{ this->setCurrentDocumentIndex(newDocIndex); });
+}
+
+int WidgetMainControl::currentDocumentIndex() const
+{
+ return m_ui->combo_GuiDocuments->currentIndex();
+}
+
+void WidgetMainControl::setCurrentDocumentIndex(int idx)
+{
+ m_ui->combo_GuiDocuments->setCurrentIndex(idx);
+}
+
+void WidgetMainControl::onWidgetFileSystemLocationActivated(const QFileInfo& loc)
+{
+ FileCommandTools::openDocument(m_appContext, filepathFrom(loc));
+}
+
+void WidgetMainControl::onCurrentDocumentIndexChanged(int idx)
+{
+ m_ui->stack_GuiDocuments->setCurrentIndex(idx);
+ QAbstractItemView* view = m_ui->listView_OpenedDocuments;
+ view->setCurrentIndex(view->model()->index(idx, 0));
+
+ emit this->updateGlobalControlsActivationRequired();
+
+ const DocumentPtr docPtr = m_guiApp->application()->findDocumentByIndex(idx);
+ const FilePath docFilePath = docPtr ? docPtr->filePath() : FilePath();
+ m_ui->widget_FileSystem->setLocation(filepathTo(docFilePath));
+
+ emit this->currentDocumentIndexChanged(idx);
+}
+
+} // namespace Mayo
diff --git a/src/app/widget_main_control.h b/src/app/widget_main_control.h
new file mode 100644
index 00000000..9c9d5270
--- /dev/null
+++ b/src/app/widget_main_control.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+** Copyright (c) 2023, Fougue Ltd.
+** All rights reserved.
+** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt
+****************************************************************************/
+
+#pragma once
+
+#include "../base/property.h"
+#include "iwidget_main_page.h"
+
+#include
+
+class QFileInfo;
+class QMenu;
+
+namespace Mayo {
+
+class IAppContext;
+class CommandContainer;
+class GuiApplication;
+class GuiDocument;
+class ItemViewButtons;
+class WidgetGuiDocument;
+
+// Provides a main page to control opened documents in the application
+// Comes with the model tree, 3D view associated to each document, ...
+class WidgetMainControl : public IWidgetMainPage {
+ Q_OBJECT
+public:
+ WidgetMainControl(GuiApplication* guiApp, QWidget* parent = nullptr);
+ ~WidgetMainControl();
+
+ void initialize(const CommandContainer* cmdContainer) override;
+ void updatePageControlsActivation() override;
+
+ // Widget at the left side of the app providing access to the model tree, file system, ...
+ QWidget* widgetLeftSideBar() const;
+
+ int widgetGuiDocumentCount() const;
+ WidgetGuiDocument* widgetGuiDocument(int idx) const;
+ WidgetGuiDocument* currentWidgetGuiDocument() const;
+ int indexOfWidgetGuiDocument(WidgetGuiDocument* widgetDoc) const;
+ void removeWidgetGuiDocument(WidgetGuiDocument* widgetDoc);
+
+ int currentDocumentIndex() const;
+ void setCurrentDocumentIndex(int idx);
+
+ bool eventFilter(QObject* watched, QEvent* event) override;
+
+signals:
+ void currentDocumentIndexChanged(int docIndex);
+
+private:
+ QMenu* createMenuModelTreeSettings();
+
+ void onApplicationItemSelectionChanged();
+ void onLeftContentsPageChanged(int pageId);
+ void onWidgetFileSystemLocationActivated(const QFileInfo& loc);
+
+ QWidget* findLeftHeaderPlaceHolder() const;
+ QWidget* recreateLeftHeaderPlaceHolder();
+
+ void onGuiDocumentAdded(GuiDocument* guiDoc);
+
+ void onCurrentDocumentIndexChanged(int idx);
+
+ class Ui_WidgetMainControl* m_ui = nullptr;
+ GuiApplication* m_guiApp = nullptr;
+ IAppContext* m_appContext = nullptr;
+ ItemViewButtons* m_listViewBtns = nullptr;
+ std::unique_ptr m_ptrCurrentNodeDataProperties;
+ std::unique_ptr m_ptrCurrentNodeGraphicsProperties;
+};
+
+} // namespace Mayo
diff --git a/src/app/widget_main_control.ui b/src/app/widget_main_control.ui
new file mode 100644
index 00000000..27ac8e57
--- /dev/null
+++ b/src/app/widget_main_control.ui
@@ -0,0 +1,491 @@
+
+
+ Mayo::WidgetMainControl
+
+
+
+ 0
+ 0
+ 947
+ 560
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+