mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-08 21:14:14 +02:00
Merge remote-tracking branch 'upstream/master' into feature/customise-toolbar-options
# Conflicts: # tests/qttest/CMakeLists.txt
This commit is contained in:
@@ -104,6 +104,14 @@ add_test(NAME tst_textgrid COMMAND tst_textgrid)
|
||||
target_include_directories(tst_textgrid PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_textgrid PRIVATE Qt::Test)
|
||||
|
||||
# textlines.h is header-only: the lines of a text as they are laid out,
|
||||
# for the exports that write a text line by line.
|
||||
add_executable(tst_textlines tst_textlines.cpp)
|
||||
add_test(NAME tst_textlines COMMAND tst_textlines)
|
||||
set_tests_properties(tst_textlines PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
|
||||
target_include_directories(tst_textlines PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_textlines PRIVATE Qt::Test Qt::Gui)
|
||||
|
||||
# elementviewgrid.h is header-only: the symbol editor's snap step at each
|
||||
# zoom (bugtracker #112), tested without an editor window.
|
||||
add_executable(tst_elementviewgrid tst_elementviewgrid.cpp)
|
||||
@@ -155,6 +163,18 @@ set_tests_properties(tst_textanchor PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offs
|
||||
target_include_directories(tst_textanchor PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_textanchor PRIVATE Qt::Test Qt::Widgets)
|
||||
|
||||
# TextResizeHandles: the corner handles that change the width of a text box,
|
||||
# on a plain QGraphicsTextItem with the "textWidth" property QET texts have.
|
||||
add_executable(
|
||||
tst_textresizehandles
|
||||
tst_textresizehandles.cpp
|
||||
${QET_DIR}/sources/QetGraphicsItemModeler/qetgraphicshandleritem.cpp
|
||||
${QET_DIR}/sources/QetGraphicsItemModeler/textresizehandles.cpp)
|
||||
add_test(NAME tst_textresizehandles COMMAND tst_textresizehandles)
|
||||
set_tests_properties(tst_textresizehandles PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
|
||||
target_include_directories(tst_textresizehandles PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_textresizehandles PRIVATE Qt::Test Qt::Widgets)
|
||||
|
||||
# legacyelementtypes.h is header-only: the rewrite of element type names in
|
||||
# nomenclature queries saved before June 2022.
|
||||
add_executable(tst_legacyelementtypes tst_legacyelementtypes.cpp)
|
||||
@@ -227,8 +247,25 @@ set_tests_properties(tst_commandsearch PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=o
|
||||
target_include_directories(tst_commandsearch PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_commandsearch PRIVATE Qt::Test Qt::Widgets)
|
||||
|
||||
# ShortcutsConfigPage: icon and menu columns, category filter, finding a
|
||||
# command by pressing its key, and the copied list.
|
||||
add_executable(
|
||||
tst_shortcutspage
|
||||
tst_shortcutspage.cpp
|
||||
${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.cpp
|
||||
${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.h
|
||||
${QET_DIR}/sources/ui/configpage/configpage.h
|
||||
${QET_DIR}/sources/shortcutmanager.cpp
|
||||
${QET_DIR}/sources/qeticons.cpp
|
||||
${QET_DIR}/sources/qetpalette.cpp
|
||||
${QET_DIR}/sources/qetstyle.cpp)
|
||||
add_test(NAME tst_shortcutspage COMMAND tst_shortcutspage)
|
||||
set_tests_properties(tst_shortcutspage PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
|
||||
target_include_directories(tst_shortcutspage PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_shortcutspage PRIVATE Qt::Test Qt::Widgets Qt::Svg)
|
||||
|
||||
# ToolbarSettings and its configuration page: defaults change nothing,
|
||||
# saved values reach every open window's toolbars, panels are left alone.
|
||||
# saved values reach every open window that took them, panels are left alone.
|
||||
add_executable(
|
||||
tst_toolbarsettings
|
||||
tst_toolbarsettings.cpp
|
||||
@@ -407,6 +444,18 @@ target_link_libraries(tst_conductorselfretrace PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_conductorselfretrace PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# A free text with a width keeps it when the project is saved again (the
|
||||
# attribute is written only when set) and is drawn wrapped to it.
|
||||
add_executable(
|
||||
tst_freetextwidth
|
||||
tst_freetextwidth.cpp)
|
||||
add_test(NAME tst_freetextwidth COMMAND tst_freetextwidth)
|
||||
set_tests_properties(tst_freetextwidth PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
|
||||
add_dependencies(tst_freetextwidth qelectrotech)
|
||||
target_link_libraries(tst_freetextwidth PRIVATE Qt::Test Qt::Gui Qt::Xml)
|
||||
target_compile_definitions(tst_freetextwidth PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# The wiring list export (--export-cables) keeps every wire drawn to a folio
|
||||
# report when a report has several, joins only one-to-one report pairs, and
|
||||
# prints the folio number rather than its template. fixtures/wiring_list_arrows.qet
|
||||
@@ -421,6 +470,16 @@ target_compile_definitions(tst_wiringlistexport PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# --export-dxf writes a wrapped symbol text line by line, as it is drawn.
|
||||
add_executable(
|
||||
tst_dxfwrappedtext
|
||||
tst_dxfwrappedtext.cpp)
|
||||
add_test(NAME tst_dxfwrappedtext COMMAND tst_dxfwrappedtext)
|
||||
add_dependencies(tst_dxfwrappedtext qelectrotech)
|
||||
target_link_libraries(tst_dxfwrappedtext PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_dxfwrappedtext PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# --export-pdf puts an A3 folio on an A3 page whichever way it is turned:
|
||||
# QPageSize matches standard sheets upright only, so a wide folio is matched
|
||||
# upright and turned. fixtures/pdf_page_a3.qet has one folio each way.
|
||||
@@ -549,6 +608,17 @@ if(QET_HAS_SCRIPTING)
|
||||
target_compile_definitions(tst_scriptlayoutreads PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# The project database keeps the width of a free text (independent_text),
|
||||
# read with qet.query() on fixtures/free_text_width.qet.
|
||||
add_executable(
|
||||
tst_scriptfreetextwidth
|
||||
tst_scriptfreetextwidth.cpp)
|
||||
add_test(NAME tst_scriptfreetextwidth COMMAND tst_scriptfreetextwidth)
|
||||
add_dependencies(tst_scriptfreetextwidth qelectrotech)
|
||||
target_link_libraries(tst_scriptfreetextwidth PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_scriptfreetextwidth PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# QET_SETTINGS_DIR moves the settings into an INI file there (#1178): a
|
||||
# script places a symbol only the folder's settings file can resolve.
|
||||
add_executable(
|
||||
@@ -611,6 +681,18 @@ if(QET_HAS_SCRIPTING)
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# Undoing a crop of a picture restores its crop rectangle too, so a
|
||||
# project saved after the undo does not record the undone crop.
|
||||
add_executable(
|
||||
tst_imagecropundo
|
||||
tst_imagecropundo.cpp)
|
||||
add_test(NAME tst_imagecropundo COMMAND tst_imagecropundo)
|
||||
add_dependencies(tst_imagecropundo qelectrotech)
|
||||
target_link_libraries(tst_imagecropundo PRIVATE Qt::Test Qt::Gui)
|
||||
target_compile_definitions(tst_imagecropundo PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# The same symbol placed twice in a project whose collection does not
|
||||
# start with its "import" category (examples/lmdg.qet).
|
||||
add_executable(
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<project title="DXF wrapped texts" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7b10}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/2/26</property>
|
||||
<property name="saveddate-eu" show="1">02-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-02</property>
|
||||
<property name="savedfilename" show="1">dxf_wrapped_text</property>
|
||||
<property name="savedtime" show="1">18:39</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="coil" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="commutator" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="plc" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="protection" xrefpos="AlignBottom"/>
|
||||
</xrefs>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a01}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<elements>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="" type="embed://import/wrap.elmt" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a02}" x="300" y="300" z="10">
|
||||
<terminals/>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="true" rotation="0" rotation_point_center="false" text_from="UserText" text_width="70" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7b01}" x="0" y="10"><text>WrapAlpha WrapBeta WrapGamma WrapDelta WrapEpsilon</text></dynamic_elmt_text>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="true" rotation="0" rotation_point_center="false" text_from="UserText" text_width="-1" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7b02}" x="0" y="60"><text>LineAlpha LineBeta LineGamma LineDelta LineEpsilon</text></dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
</elements>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="en">Imported elements</name>
|
||||
</names>
|
||||
<element name="wrap.elmt">
|
||||
<definition height="80" hotspot_x="10" hotspot_y="10" link_type="simple" type="element" version="0.100.0" width="120">
|
||||
<uuid uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a05}"/>
|
||||
<names>
|
||||
<name lang="en">Wrapped symbol text</name>
|
||||
</names>
|
||||
<description>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="0" x2="10" y1="0" y2="0"/>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="true" rotation="0" z="2" text_from="UserText" text_width="70" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7b01}" x="0" y="10"><text>WrapAlpha WrapBeta WrapGamma WrapDelta WrapEpsilon</text></dynamic_text>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="true" rotation="0" z="3" text_from="UserText" text_width="-1" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7b02}" x="0" y="60"><text>LineAlpha LineBeta LineGamma LineDelta LineEpsilon</text></dynamic_text>
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,41 @@
|
||||
<project title="Free text widths" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a00}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/2/26</property>
|
||||
<property name="saveddate-eu" show="1">02-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-02</property>
|
||||
<property name="savedfilename" show="1">free_text_width</property>
|
||||
<property name="savedtime" show="1">18:39</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="coil" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="commutator" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="plc" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="protection" xrefpos="AlignBottom"/>
|
||||
</xrefs>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a01}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<inputs>
|
||||
<input font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" rotation="0" text="FreeAlpha FreeBeta FreeGamma FreeDelta FreeEpsilon" text_width="70" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a03}" x="100" y="100"/>
|
||||
<input font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" rotation="0" text="OpenAlpha OpenBeta OpenGamma OpenDelta OpenEpsilon" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a04}" x="100" y="400"/>
|
||||
<input font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" rotation="0" text="<p align="center">CentAlpha CentBeta CentGamma</p>" text_width="120" uuid="{5b0f5e1c-8a43-4c1e-9e57-3f0d0f0a7a08}" x="400" y="100"/>
|
||||
</inputs>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="en">Imported elements</name>
|
||||
</names>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
/**
|
||||
--export-dxf writes a symbol text wrapped to its width line by line,
|
||||
as it is drawn, rather than as one long line. A text without a width
|
||||
is still written on one line.
|
||||
*/
|
||||
class tst_dxfwrappedtext : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void wrappedLines()
|
||||
{
|
||||
const QString project = QFINDTESTDATA("fixtures/dxf_wrapped_text.qet");
|
||||
QVERIFY2(!project.isEmpty(), "fixture project not found");
|
||||
QTemporaryDir dir;
|
||||
QVERIFY(dir.isValid());
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), {QStringLiteral("--export-dxf"), project, dir.path()});
|
||||
QVERIFY2(proc.waitForFinished(60000), "--export-dxf timed out");
|
||||
QCOMPARE(proc.exitCode(), 0);
|
||||
|
||||
QFile file(dir.filePath(QStringLiteral("01_diagram.dxf")));
|
||||
QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QStringList values = QString::fromUtf8(file.readAll()).split(QLatin1Char('\n'));
|
||||
|
||||
//One text entity per word of the text 70 px wide...
|
||||
for (const QString &word : {QStringLiteral("WrapAlpha"), QStringLiteral("WrapGamma"), QStringLiteral("WrapEpsilon")})
|
||||
QVERIFY2(values.contains(word), qPrintable(word));
|
||||
//...one for the whole text without a width
|
||||
QVERIFY(values.contains(QStringLiteral("LineAlpha LineBeta LineGamma LineDelta LineEpsilon")));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_dxfwrappedtext)
|
||||
#include "tst_dxfwrappedtext.moc"
|
||||
@@ -47,14 +47,18 @@ class tst_elementautonumids : public QObject
|
||||
{
|
||||
const QString home = m_dir.filePath(QStringLiteral("home%1").arg(m_run++));
|
||||
QDir().mkpath(home);
|
||||
// QET_SETTINGS_DIR, not a file under XDG_CONFIG_HOME: macOS and
|
||||
// Windows keep the settings elsewhere and would never read it.
|
||||
const QString settings_dir = home + QStringLiteral("/settings");
|
||||
if (!m_preferences.isEmpty()) {
|
||||
QDir().mkpath(home + QStringLiteral("/config/QElectroTech"));
|
||||
QFile settings(home + QStringLiteral("/config/QElectroTech/QElectroTech.conf"));
|
||||
QDir().mkpath(settings_dir + QStringLiteral("/QElectroTech"));
|
||||
QFile settings(settings_dir + QStringLiteral("/QElectroTech/QElectroTech.ini"));
|
||||
if (settings.open(QIODevice::WriteOnly)) {
|
||||
settings.write(m_preferences.toUtf8());
|
||||
}
|
||||
}
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QET_SETTINGS_DIR"), settings_dir);
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QDomDocument>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTextDocument>
|
||||
|
||||
/// Run the qelectrotech binary with arguments, without a display.
|
||||
static bool runQet(const QStringList &arguments)
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), arguments);
|
||||
return proc.waitForFinished(60000)
|
||||
&& proc.exitStatus() == QProcess::NormalExit
|
||||
&& proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
/// The number of lines the words starting with prefix are drawn on.
|
||||
static int drawnLines(const QString &svg, const QString &prefix)
|
||||
{
|
||||
const QRegularExpression text_re(
|
||||
QStringLiteral("<text[^>]*\\by=\"([^\"]+)\"[^>]*>\\s*%1").arg(prefix));
|
||||
QSet<QString> lines;
|
||||
auto it = text_re.globalMatch(svg);
|
||||
while (it.hasNext())
|
||||
lines.insert(it.next().captured(1));
|
||||
return lines.size();
|
||||
}
|
||||
|
||||
/**
|
||||
A free text with a width (text_width on its <input>) keeps it when the
|
||||
project is saved again, and wraps to it. A text without a width is saved
|
||||
without the attribute, as before, so older versions read it unchanged.
|
||||
*/
|
||||
class tst_freetextwidth : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY2(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)), "qelectrotech binary not found");
|
||||
m_fixture = QFINDTESTDATA("fixtures/free_text_width.qet");
|
||||
QVERIFY2(!m_fixture.isEmpty(), "fixture project not found");
|
||||
QVERIFY(m_dir.isValid());
|
||||
}
|
||||
|
||||
void widthIsSavedOnlyWhenSet()
|
||||
{
|
||||
const QString out = m_dir.filePath(QStringLiteral("resaved.qet"));
|
||||
QVERIFY2(runQet({QStringLiteral("--resave"), m_fixture, out}), "--resave failed");
|
||||
|
||||
QFile file(out);
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
QDomDocument document;
|
||||
QVERIFY(document.setContent(&file));
|
||||
|
||||
QMap<QString, QString> widths; // first word -> text_width
|
||||
const QDomNodeList inputs = document.elementsByTagName(QStringLiteral("input"));
|
||||
for (int i = 0 ; i < inputs.size() ; ++i) {
|
||||
const QDomElement input = inputs.at(i).toElement();
|
||||
QTextDocument text;
|
||||
text.setHtml(input.attribute(QStringLiteral("text")));
|
||||
const QString first_word = text.toPlainText().section(QLatin1Char(' '), 0, 0);
|
||||
widths.insert(first_word, input.hasAttribute(QStringLiteral("text_width"))
|
||||
? input.attribute(QStringLiteral("text_width"))
|
||||
: QStringLiteral("none"));
|
||||
}
|
||||
|
||||
QCOMPARE(widths.value(QStringLiteral("FreeAlpha")), QStringLiteral("70"));
|
||||
QCOMPARE(widths.value(QStringLiteral("OpenAlpha")), QStringLiteral("none"));
|
||||
//Centred lines: the width of the user, not the one setHtml() gives
|
||||
QCOMPARE(widths.value(QStringLiteral("CentAlpha")), QStringLiteral("120"));
|
||||
}
|
||||
|
||||
void textWrapsToItsWidth()
|
||||
{
|
||||
QVERIFY2(runQet({QStringLiteral("--export-svg"), m_fixture, m_dir.path()}), "--export-svg failed");
|
||||
QFile file(m_dir.filePath(QStringLiteral("01_diagram.svg")));
|
||||
QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
|
||||
const QString svg = QString::fromUtf8(file.readAll());
|
||||
|
||||
QVERIFY(drawnLines(svg, QStringLiteral("Free")) > 1);
|
||||
QCOMPARE(drawnLines(svg, QStringLiteral("Open")), 1);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_fixture;
|
||||
QTemporaryDir m_dir;
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_freetextwidth)
|
||||
#include "tst_freetextwidth.moc"
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QImage>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QRegularExpression>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
/**
|
||||
Undoing a crop restores the crop rectangle too, not only the pixels
|
||||
shown: before, a project saved after Ctrl+Z still recorded the undone
|
||||
crop, and the picture came back cropped once reopened. Runs the real
|
||||
binary on a script: add a picture, crop it, undo, save; redo, save.
|
||||
*/
|
||||
class tst_imagecropundo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
QJsonObject run(const QString &script, const QString &project)
|
||||
{
|
||||
const QString path = m_dir.filePath(QStringLiteral("probe.js"));
|
||||
const QString home = m_dir.filePath(QStringLiteral("home"));
|
||||
QDir().mkpath(home);
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly)) return {};
|
||||
f.write(script.toUtf8());
|
||||
f.close();
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
env.insert(QStringLiteral("TMPDIR"), m_dir.path());
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--run"), path, project});
|
||||
if (!proc.waitForFinished(120000)) return {};
|
||||
const QString out = QString::fromUtf8(proc.readAllStandardOutput()
|
||||
+ proc.readAllStandardError());
|
||||
const QString mark = QStringLiteral("PROBE ");
|
||||
for (const QString &line : out.split(QLatin1Char('\n'))) {
|
||||
const int i = line.indexOf(mark);
|
||||
if (i >= 0)
|
||||
return QJsonDocument::fromJson(line.mid(i + mark.size()).toUtf8()).object();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static QByteArray read(const QString &path)
|
||||
{
|
||||
QFile f(path);
|
||||
return f.open(QIODevice::ReadOnly) ? f.readAll() : QByteArray();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
QImage img(40, 30, QImage::Format_RGB32);
|
||||
img.fill(Qt::darkGreen);
|
||||
QVERIFY(img.save(m_dir.filePath(QStringLiteral("pic.png"))));
|
||||
}
|
||||
|
||||
void undoneCropIsNotSaved()
|
||||
{
|
||||
const QString undone = m_dir.filePath(QStringLiteral("undone.qet"));
|
||||
const QString redone = m_dir.filePath(QStringLiteral("redone.qet"));
|
||||
const QString script = QStringLiteral(R"JS(
|
||||
var i = qet.addImage(0, '%1', 100, 100);
|
||||
var r = {full: qet.imageCrop(0, i)};
|
||||
r.cropped_ok = qet.cropImage(0, i, 10, 5, 20, 10);
|
||||
r.cropped = qet.imageCrop(0, i);
|
||||
r.same_ok = qet.cropImage(0, i, 10, 5, 20, 10);
|
||||
r.empty_ok = qet.cropImage(0, i, 10, 5, 0, 10);
|
||||
r.outside_ok = qet.cropImage(0, i, 100, 100, 20, 10);
|
||||
qet.undo();
|
||||
r.undone = qet.imageCrop(0, i);
|
||||
qet.save('%2');
|
||||
qet.redo();
|
||||
r.redone = qet.imageCrop(0, i);
|
||||
qet.save('%3');
|
||||
qet.log('PROBE ' + JSON.stringify(r));
|
||||
)JS").arg(m_dir.filePath(QStringLiteral("pic.png")), undone, redone);
|
||||
|
||||
const QJsonObject r = run(script, QStringLiteral(QET_EXAMPLES_DIR "/741.qet"));
|
||||
QVERIFY2(!r.isEmpty(), "the script logged nothing");
|
||||
QCOMPARE(r.value("full").toString(), QStringLiteral("0,0,40,30"));
|
||||
QVERIFY(r.value("cropped_ok").toBool());
|
||||
QCOMPARE(r.value("cropped").toString(), QStringLiteral("10,5,20,10"));
|
||||
// Crops that change nothing report it, and push no undo step:
|
||||
// the undo below still undoes the real crop.
|
||||
QCOMPARE(r.value("same_ok").toBool(true), false);
|
||||
QCOMPARE(r.value("empty_ok").toBool(true), false);
|
||||
QCOMPARE(r.value("outside_ok").toBool(true), false);
|
||||
QCOMPARE(r.value("undone").toString(), QStringLiteral("0,0,40,30"));
|
||||
QCOMPARE(r.value("redone").toString(), QStringLiteral("10,5,20,10"));
|
||||
|
||||
const QByteArray undone_xml = read(undone);
|
||||
QVERIFY(undone_xml.contains("<image "));
|
||||
QVERIFY2(!undone_xml.contains("<crop "), "an undone crop was saved");
|
||||
const QRegularExpressionMatch crop =
|
||||
QRegularExpression(QStringLiteral("<crop ([^>]*)/>")).match(QString::fromUtf8(read(redone)));
|
||||
QVERIFY2(crop.hasMatch(), "the redone crop was not saved");
|
||||
for (const char *attribute : {R"(x="10")", R"(y="5")", R"(w="20")", R"(h="10")"})
|
||||
QVERIFY2(crop.captured(1).contains(QLatin1String(attribute)), attribute);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_imagecropundo)
|
||||
#include "tst_imagecropundo.moc"
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
/**
|
||||
The project database keeps the width of a free text (text_width in the
|
||||
independent_text table, NULL for the automatic width), read here with
|
||||
qet.query() from a script run on fixtures/free_text_width.qet.
|
||||
*/
|
||||
class tst_scriptfreetextwidth : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
/// The rows of the independent_text table, by the first word of the text.
|
||||
QHash<QString, QJsonObject> rows()
|
||||
{
|
||||
const QString path = m_dir.filePath(QStringLiteral("probe.js"));
|
||||
const QString home = m_dir.filePath(QStringLiteral("home"));
|
||||
QDir().mkpath(home);
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly)) return {};
|
||||
f.write("qet.log('PROBE ' + JSON.stringify(qet.query("
|
||||
"'SELECT text, text_width, width, height FROM independent_text')));\n");
|
||||
f.close();
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--run"), path, QFINDTESTDATA("fixtures/free_text_width.qet")});
|
||||
if (!proc.waitForFinished(60000)) return {};
|
||||
|
||||
const QString out = QString::fromUtf8(proc.readAllStandardOutput() + proc.readAllStandardError());
|
||||
const QString mark = QStringLiteral("PROBE ");
|
||||
QHash<QString, QJsonObject> result;
|
||||
for (const QString &line : out.split(QLatin1Char('\n'))) {
|
||||
const int i = line.indexOf(mark);
|
||||
if (i < 0) continue;
|
||||
const QJsonArray array = QJsonDocument::fromJson(line.mid(i + mark.size()).toUtf8()).array();
|
||||
for (const QJsonValue &row : array)
|
||||
result.insert(row.toObject().value(QStringLiteral("text")).toString().section(QLatin1Char(' '), 0, 0),
|
||||
row.toObject());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
QVERIFY(!QFINDTESTDATA("fixtures/free_text_width.qet").isEmpty());
|
||||
}
|
||||
|
||||
void widthIsInTheDatabase()
|
||||
{
|
||||
const QHash<QString, QJsonObject> r = rows();
|
||||
QCOMPARE(r.size(), 3);
|
||||
|
||||
QCOMPARE(r.value(QStringLiteral("FreeAlpha")).value(QStringLiteral("text_width")).toDouble(), 70.0);
|
||||
QCOMPARE(r.value(QStringLiteral("CentAlpha")).value(QStringLiteral("text_width")).toDouble(), 120.0);
|
||||
//The automatic width is NULL
|
||||
const QJsonValue open = r.value(QStringLiteral("OpenAlpha")).value(QStringLiteral("text_width"));
|
||||
QVERIFY2(open.isNull() || open.toString().isEmpty(), qPrintable(open.toVariant().toString()));
|
||||
|
||||
//The box of the row is the wrapped one: taller than one line
|
||||
QVERIFY(r.value(QStringLiteral("FreeAlpha")).value(QStringLiteral("height")).toDouble()
|
||||
> 2 * r.value(QStringLiteral("OpenAlpha")).value(QStringLiteral("height")).toDouble());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_scriptfreetextwidth)
|
||||
#include "tst_scriptfreetextwidth.moc"
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "shortcutmanager.h"
|
||||
#include "ui/configpage/shortcutsconfigpage.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QComboBox>
|
||||
#include <QKeySequenceEdit>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
#include <QPixmap>
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
#include <QTreeWidget>
|
||||
|
||||
/**
|
||||
The Shortcuts configuration page: the icon and menu columns, the
|
||||
category filter, finding a command by pressing its key, and the
|
||||
copied list.
|
||||
*/
|
||||
class tst_shortcutspage : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QMainWindow m_window;
|
||||
QAction *m_save = nullptr;
|
||||
QAction *m_run = nullptr;
|
||||
QAction *m_tool = nullptr;
|
||||
|
||||
QTreeWidgetItem *row(QTreeWidget *tree, const QString &text)
|
||||
{
|
||||
const auto found = tree->findItems(text, Qt::MatchExactly | Qt::MatchRecursive, 0);
|
||||
return found.isEmpty() ? nullptr : found.first();
|
||||
}
|
||||
|
||||
QStringList visible(QTreeWidget *tree)
|
||||
{
|
||||
QStringList texts;
|
||||
for (const QString &text : {QStringLiteral("Enregistrer"), QStringLiteral("Exécuter"), QStringLiteral("Outil")}) {
|
||||
if (QTreeWidgetItem *item = row(tree, text); item && !item->isHidden()) {
|
||||
texts << text;
|
||||
}
|
||||
}
|
||||
return texts;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
QSettings().remove(QStringLiteral("shortcuts"));
|
||||
|
||||
QPixmap pixmap(16, 16);
|
||||
pixmap.fill(Qt::red);
|
||||
|
||||
QMenu *file = m_window.menuBar()->addMenu(QStringLiteral("&Fichier"));
|
||||
QMenu *scripts = file->addMenu(QStringLiteral("&Scripts"));
|
||||
m_save = file->addAction(QIcon(pixmap), QStringLiteral("&Enregistrer"));
|
||||
m_run = scripts->addAction(QStringLiteral("Exécuter"));
|
||||
//On a toolbar only, in no menu
|
||||
m_tool = new QAction(QStringLiteral("Outil"), &m_window);
|
||||
|
||||
ShortcutManager &manager = ShortcutManager::instance();
|
||||
manager.registerAction(m_save, QStringLiteral("t.save"), QStringLiteral("Catégorie A"), QKeySequence(Qt::CTRL | Qt::Key_S));
|
||||
manager.registerAction(m_run, QStringLiteral("t.run"), QStringLiteral("Catégorie A"), QKeySequence(Qt::CTRL | Qt::Key_R));
|
||||
manager.registerAction(m_tool, QStringLiteral("t.tool"), QStringLiteral("Catégorie B"), QKeySequence());
|
||||
}
|
||||
|
||||
void menuPath()
|
||||
{
|
||||
QCOMPARE(ShortcutsConfigPage::menuPath(m_save), QStringLiteral("Fichier"));
|
||||
QCOMPARE(ShortcutsConfigPage::menuPath(m_run), QStringLiteral("Fichier › Scripts"));
|
||||
QCOMPARE(ShortcutsConfigPage::menuPath(m_tool), QString());
|
||||
QCOMPARE(ShortcutsConfigPage::menuPath(nullptr), QString());
|
||||
}
|
||||
|
||||
void iconAndMenuColumns()
|
||||
{
|
||||
ShortcutsConfigPage page(nullptr);
|
||||
auto *tree = page.findChild<QTreeWidget *>();
|
||||
QVERIFY(row(tree, QStringLiteral("Enregistrer")));
|
||||
QVERIFY(!row(tree, QStringLiteral("Enregistrer"))->icon(0).isNull());
|
||||
QVERIFY(row(tree, QStringLiteral("Outil"))->icon(0).isNull());
|
||||
QCOMPARE(row(tree, QStringLiteral("Exécuter"))->text(1), QStringLiteral("Fichier › Scripts"));
|
||||
QCOMPARE(row(tree, QStringLiteral("Outil"))->text(1), QString());
|
||||
}
|
||||
|
||||
void categoryFilter()
|
||||
{
|
||||
ShortcutsConfigPage page(nullptr);
|
||||
auto *tree = page.findChild<QTreeWidget *>();
|
||||
auto *combo = page.findChild<QComboBox *>(QStringLiteral("categoryFilterCombo"));
|
||||
QCOMPARE(visible(tree).size(), 3);
|
||||
combo->setCurrentIndex(combo->findText(QStringLiteral("Catégorie B")));
|
||||
QCOMPARE(visible(tree), QStringList{QStringLiteral("Outil")});
|
||||
combo->setCurrentIndex(0);
|
||||
QCOMPARE(visible(tree).size(), 3);
|
||||
}
|
||||
|
||||
void findByKey()
|
||||
{
|
||||
ShortcutsConfigPage page(nullptr);
|
||||
auto *tree = page.findChild<QTreeWidget *>();
|
||||
auto *key = page.findChild<QKeySequenceEdit *>(QStringLiteral("keySearchEdit"));
|
||||
key->setKeySequence(QKeySequence(Qt::CTRL | Qt::Key_S));
|
||||
QCOMPARE(visible(tree), QStringList{QStringLiteral("Enregistrer")});
|
||||
key->setKeySequence(QKeySequence(Qt::CTRL | Qt::Key_J));
|
||||
QVERIFY(visible(tree).isEmpty());
|
||||
key->clear();
|
||||
QCOMPARE(visible(tree).size(), 3);
|
||||
}
|
||||
|
||||
void copiedList()
|
||||
{
|
||||
ShortcutsConfigPage page(nullptr);
|
||||
auto *combo = page.findChild<QComboBox *>(QStringLiteral("categoryFilterCombo"));
|
||||
combo->setCurrentIndex(combo->findText(QStringLiteral("Catégorie A")));
|
||||
const QStringList lines = page.listAsText().split(QLatin1Char('\n'), Qt::SkipEmptyParts);
|
||||
QCOMPARE(lines.size(), 3);
|
||||
QCOMPARE(lines.at(0).count(QLatin1Char('\t')), 3);
|
||||
QVERIFY(lines.contains(QStringLiteral("Catégorie A\tFichier\tEnregistrer\t")
|
||||
+ QKeySequence(Qt::CTRL | Qt::Key_S).toString(QKeySequence::NativeText)));
|
||||
QVERIFY(lines.contains(QStringLiteral("Catégorie A\tFichier › Scripts\tExécuter\t")
|
||||
+ QKeySequence(Qt::CTRL | Qt::Key_R).toString(QKeySequence::NativeText)));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_shortcutspage)
|
||||
#include "tst_shortcutspage.moc"
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QTextDocument>
|
||||
#include <QTextOption>
|
||||
|
||||
#include "textlines.h"
|
||||
|
||||
class tst_textlines : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
// A text without a width gives its own lines.
|
||||
void unwrappedTextGivesItsLines()
|
||||
{
|
||||
QTextDocument document(QStringLiteral("Motor protection\nswitch Q12"));
|
||||
QCOMPARE(TextLines::layoutLines(&document),
|
||||
QStringList({QStringLiteral("Motor protection"), QStringLiteral("switch Q12")}));
|
||||
}
|
||||
|
||||
// A text without a width gives exactly the lines the DXF export wrote
|
||||
// before, toPlainText().split('\n'): trailing spaces and tabs kept,
|
||||
// non-breaking spaces as plain spaces, <br> as a line break.
|
||||
void unwrappedTextIsUnchanged_data()
|
||||
{
|
||||
QTest::addColumn<QString>("html");
|
||||
QTest::newRow("trailing spaces") << QStringLiteral("K1 \nswitch\t");
|
||||
QTest::newRow("non-breaking spaces") << QStringLiteral("<p>K1 24V DC</p>");
|
||||
QTest::newRow("line break") << QStringLiteral("<p>Wiper to 6<br>contacts</p>");
|
||||
QTest::newRow("paragraphs") << QStringLiteral("<p>relais voyant </p><p> preventa</p>");
|
||||
QTest::newRow("empty") << QString();
|
||||
}
|
||||
|
||||
void unwrappedTextIsUnchanged()
|
||||
{
|
||||
QFETCH(QString, html);
|
||||
QTextDocument document;
|
||||
if (Qt::mightBeRichText(html))
|
||||
document.setHtml(html);
|
||||
else
|
||||
document.setPlainText(html);
|
||||
|
||||
QCOMPARE(TextLines::layoutLines(&document),
|
||||
document.toPlainText().split(QLatin1Char('\n')));
|
||||
}
|
||||
|
||||
// A text wrapped to its width gives one line per drawn line, without
|
||||
// the space the line was broken at.
|
||||
void wrappedTextGivesTheDrawnLines()
|
||||
{
|
||||
QTextDocument document(QStringLiteral("Motor protection switch Q12"));
|
||||
QTextOption option = document.defaultTextOption();
|
||||
option.setWrapMode(QTextOption::WordWrap);
|
||||
document.setDefaultTextOption(option);
|
||||
document.setTextWidth(1);
|
||||
|
||||
QCOMPARE(TextLines::layoutLines(&document),
|
||||
QStringList({QStringLiteral("Motor"), QStringLiteral("protection"),
|
||||
QStringLiteral("switch"), QStringLiteral("Q12")}));
|
||||
}
|
||||
|
||||
// A wrapped paragraph has no trailing spaces left, even on its last
|
||||
// line: tabs and spaces that wrap to a line of their own (as in the
|
||||
// affuteuse_250h example) give an empty line, which is not written but
|
||||
// still keeps the line spacing.
|
||||
void wrappedTextHasNoTrailingSpaces()
|
||||
{
|
||||
QTextDocument document(QStringLiteral("\tMotor\t\t "));
|
||||
QTextOption option = document.defaultTextOption();
|
||||
option.setWrapMode(QTextOption::WordWrap);
|
||||
document.setDefaultTextOption(option);
|
||||
document.setTextWidth(80);
|
||||
|
||||
const QStringList lines = TextLines::layoutLines(&document);
|
||||
QVERIFY(lines.size() > 1);
|
||||
QVERIFY(lines.contains(QStringLiteral("Motor")));
|
||||
for (const QString &line : lines)
|
||||
QVERIFY2(line.isEmpty() || !line.back().isSpace(), qPrintable(line));
|
||||
}
|
||||
|
||||
// Non-breaking spaces also become plain spaces in a wrapped text.
|
||||
void wrappedTextHasPlainSpaces()
|
||||
{
|
||||
QTextDocument document;
|
||||
document.setHtml(QStringLiteral("<p>K1 24V and more words</p>"));
|
||||
QTextOption option = document.defaultTextOption();
|
||||
option.setWrapMode(QTextOption::WordWrap);
|
||||
document.setDefaultTextOption(option);
|
||||
document.setTextWidth(1);
|
||||
|
||||
const QStringList lines = TextLines::layoutLines(&document);
|
||||
QCOMPARE(lines.first(), QStringLiteral("K1 24V"));
|
||||
for (const QString &line : lines)
|
||||
QVERIFY(!line.contains(QChar::Nbsp));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_textlines)
|
||||
#include "tst_textlines.moc"
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <QtTest>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QGraphicsTextItem>
|
||||
#include <QSignalSpy>
|
||||
#include <QTextBlock>
|
||||
#include <QTextLayout>
|
||||
#include <QTextOption>
|
||||
#include <QUndoStack>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "QetGraphicsItemModeler/qetgraphicshandleritem.h"
|
||||
#include "QetGraphicsItemModeler/textresizehandles.h"
|
||||
|
||||
using TextResize::Corner;
|
||||
|
||||
/**
|
||||
A text with the "textWidth" property the texts of QElectroTech have.
|
||||
*/
|
||||
class WidthText : public QGraphicsTextItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(qreal textWidth READ textWidth WRITE setWidth)
|
||||
|
||||
public:
|
||||
WidthText(const QString &text) : QGraphicsTextItem(text)
|
||||
{
|
||||
QTextOption option = document()->defaultTextOption();
|
||||
option.setWrapMode(QTextOption::WordWrap);
|
||||
document()->setDefaultTextOption(option);
|
||||
}
|
||||
void setWidth(qreal width) {setTextWidth(width);}
|
||||
};
|
||||
|
||||
/// The number of lines the text is laid out on.
|
||||
static int lineCount(const QGraphicsTextItem &text)
|
||||
{
|
||||
text.document()->size();
|
||||
int lines = 0;
|
||||
for (QTextBlock block = text.document()->begin() ; block.isValid() ; block = block.next())
|
||||
lines += block.layout()->lineCount();
|
||||
return lines;
|
||||
}
|
||||
|
||||
static bool samePoint(const QPointF &a, const QPointF &b)
|
||||
{
|
||||
return qAbs(a.x() - b.x()) < 1e-6 && qAbs(a.y() - b.y()) < 1e-6;
|
||||
}
|
||||
|
||||
static QPointF sceneCorner(const QGraphicsTextItem &text, Corner corner)
|
||||
{
|
||||
return text.mapToScene(TextResize::cornerOf(text.boundingRect(), corner));
|
||||
}
|
||||
|
||||
/// Send a mouse event of type to the handle at corner, as the scene would.
|
||||
static void sendMouse(QGraphicsScene &scene, TextResizeHandles &handles,
|
||||
Corner corner, QEvent::Type type, const QPointF &scene_pos)
|
||||
{
|
||||
QGraphicsSceneMouseEvent event(type);
|
||||
event.setButton(Qt::LeftButton);
|
||||
event.setButtons(type == QEvent::GraphicsSceneMouseRelease ? Qt::NoButton : Qt::LeftButton);
|
||||
event.setScenePos(scene_pos);
|
||||
scene.sendEvent(handles.handle(corner), &event);
|
||||
}
|
||||
|
||||
/// Drag the handle at corner from where it is by offset, in scene coordinates.
|
||||
static void drag(QGraphicsScene &scene, TextResizeHandles &handles,
|
||||
WidthText &text, Corner corner, const QPointF &offset)
|
||||
{
|
||||
const QPointF start = sceneCorner(text, corner);
|
||||
sendMouse(scene, handles, corner, QEvent::GraphicsSceneMousePress, start);
|
||||
sendMouse(scene, handles, corner, QEvent::GraphicsSceneMouseMove, start + offset / 2);
|
||||
sendMouse(scene, handles, corner, QEvent::GraphicsSceneMouseMove, start + offset);
|
||||
sendMouse(scene, handles, corner, QEvent::GraphicsSceneMouseRelease, start + offset);
|
||||
}
|
||||
|
||||
class tst_textresizehandles : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
// Pure geometry: the width given by a drag does not change while the
|
||||
// text moves to keep its opposite corner in place.
|
||||
void widthForDragUsesThePressTransform()
|
||||
{
|
||||
WidthText text(QStringLiteral("Motor protection switch"));
|
||||
text.setRotation(37);
|
||||
text.setPos(10, 10);
|
||||
const QTransform press_inverse = text.sceneTransform().inverted();
|
||||
const QPointF fixed = TextResize::cornerOf(text.boundingRect(), TextResize::TopLeft);
|
||||
const QPointF mouse = text.mapToScene(QPointF(80, 3));
|
||||
|
||||
const qreal width = TextResize::widthForDrag(press_inverse, fixed, mouse, TextResize::BottomRight, 0);
|
||||
QCOMPARE(qRound(width * 1000), 80000);
|
||||
|
||||
text.setPos(500, -300);
|
||||
QCOMPARE(TextResize::widthForDrag(press_inverse, fixed, mouse, TextResize::BottomRight, 0), width);
|
||||
}
|
||||
|
||||
// The minimum width is the longest word: a text that narrow does not
|
||||
// break any word, one narrower would.
|
||||
void minimumWidthIsTheLongestWord()
|
||||
{
|
||||
WidthText text(QStringLiteral("a Motorschutzschalter b"));
|
||||
const qreal minimum = TextResize::minimumWidth(text.document());
|
||||
|
||||
WidthText word(QStringLiteral("Motorschutzschalter"));
|
||||
QVERIFY(qAbs(minimum - word.boundingRect().width()) < 0.5);
|
||||
|
||||
text.setWidth(minimum);
|
||||
QVERIFY(qAbs(text.boundingRect().width() - minimum) < 0.5);
|
||||
QCOMPARE(lineCount(text), 3);
|
||||
}
|
||||
|
||||
// The minimum width never cuts a word, even for a document that would
|
||||
// otherwise break a word anywhere.
|
||||
void minimumWidthKeepsWords()
|
||||
{
|
||||
QGraphicsTextItem text(QStringLiteral("a Motorschutzschalter"));
|
||||
QTextOption option = text.document()->defaultTextOption();
|
||||
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
||||
text.document()->setDefaultTextOption(option);
|
||||
|
||||
QGraphicsTextItem word(QStringLiteral("Motorschutzschalter"));
|
||||
QVERIFY(qAbs(TextResize::minimumWidth(text.document()) - word.boundingRect().width()) < 0.5);
|
||||
}
|
||||
|
||||
// pinCorner() puts a corner where it was, for each corner, rotation,
|
||||
// rotation point and inside a rotated parent.
|
||||
void pinCornerKeepsTheCorner()
|
||||
{
|
||||
QGraphicsRectItem parent(0, 0, 10, 10);
|
||||
parent.setRotation(30);
|
||||
for (bool with_parent : {false, true})
|
||||
for (int c = TextResize::TopLeft ; c <= TextResize::BottomLeft ; ++c)
|
||||
for (qreal angle : {0.0, 90.0, 37.0})
|
||||
for (bool centre : {false, true}) {
|
||||
WidthText text(QStringLiteral("Motor protection switch"));
|
||||
if (with_parent)
|
||||
text.setParentItem(&parent);
|
||||
text.setRotation(angle);
|
||||
text.setTransformOriginPoint(centre ? text.boundingRect().center() : QPointF());
|
||||
const Corner corner = Corner(c);
|
||||
const QPointF before = sceneCorner(text, corner);
|
||||
|
||||
text.setWidth(70);
|
||||
if (centre)
|
||||
text.setTransformOriginPoint(text.boundingRect().center());
|
||||
TextResize::pinCorner(&text, corner, before);
|
||||
QVERIFY2(samePoint(sceneCorner(text, corner), before),
|
||||
qPrintable(QStringLiteral("corner %1 angle %2 centre %3 parent %4")
|
||||
.arg(c).arg(angle).arg(centre).arg(with_parent)));
|
||||
text.setParentItem(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Dragging a handle keeps the opposite corner in place, wraps the text
|
||||
// and reports the change once, on release.
|
||||
void dragKeepsTheOppositeCorner()
|
||||
{
|
||||
for (int c = TextResize::TopLeft ; c <= TextResize::BottomLeft ; ++c)
|
||||
for (qreal angle : {0.0, 90.0, 37.0}) {
|
||||
QGraphicsScene scene;
|
||||
auto *text = new WidthText(QStringLiteral("Motor protection switch Q12"));
|
||||
scene.addItem(text);
|
||||
text->setPos(100, 100);
|
||||
text->setRotation(angle);
|
||||
auto *handles = new TextResizeHandles(text);
|
||||
QSignalSpy finished(handles, &TextResizeHandles::resizeFinished);
|
||||
|
||||
const Corner corner = Corner(c);
|
||||
const QPointF fixed = sceneCorner(*text, TextResize::opposite(corner));
|
||||
const qreal natural = text->boundingRect().width();
|
||||
const QPointF start_pos = text->pos();
|
||||
|
||||
//Narrower by 40 along the text, whatever its rotation;
|
||||
//a vertical move of the mouse is ignored.
|
||||
QTransform turn;
|
||||
turn.rotate(angle);
|
||||
const qreal sign = TextResize::isLeft(corner) ? 1 : -1;
|
||||
drag(scene, *handles, *text, corner, turn.map(QPointF(sign * 40, 25)));
|
||||
|
||||
const QString what = QStringLiteral("corner %1 angle %2").arg(c).arg(angle);
|
||||
QCOMPARE(text->textWidth(), qreal(qRound(natural - 40)));
|
||||
QVERIFY2(lineCount(*text) > 1, qPrintable(what));
|
||||
QVERIFY2(samePoint(sceneCorner(*text, TextResize::opposite(corner)), fixed), qPrintable(what));
|
||||
|
||||
QCOMPARE(finished.count(), 1);
|
||||
const QList<QVariant> args = finished.first();
|
||||
QCOMPARE(args.at(0).toReal(), qreal(-1));
|
||||
QCOMPARE(args.at(1).toReal(), text->textWidth());
|
||||
QCOMPARE(args.at(2).toPointF(), start_pos);
|
||||
QCOMPARE(args.at(3).toPointF(), text->pos());
|
||||
|
||||
//The handles follow the new box
|
||||
QVERIFY(samePoint(handles->handle(corner)->scenePos(), sceneCorner(*text, corner)));
|
||||
}
|
||||
}
|
||||
|
||||
// The handles still work after the text left its scene and came back,
|
||||
// as undoing a delete does: Qt drops scene event filters on removal.
|
||||
void dragAfterLeavingTheScene()
|
||||
{
|
||||
QGraphicsScene scene;
|
||||
auto *text = new WidthText(QStringLiteral("Motor protection switch Q12"));
|
||||
scene.addItem(text);
|
||||
auto *handles = new TextResizeHandles(text);
|
||||
|
||||
scene.removeItem(text);
|
||||
scene.addItem(text);
|
||||
|
||||
const QPointF start_pos = text->pos();
|
||||
drag(scene, *handles, *text, TextResize::BottomRight, QPointF(-40, 0));
|
||||
QVERIFY(text->textWidth() > 0);
|
||||
QCOMPARE(text->pos(), start_pos);
|
||||
delete text;
|
||||
}
|
||||
|
||||
// The handles also work when they are created before the text is in a
|
||||
// scene.
|
||||
void dragWhenCreatedOutsideAScene()
|
||||
{
|
||||
QGraphicsScene scene;
|
||||
auto *text = new WidthText(QStringLiteral("Motor protection switch Q12"));
|
||||
auto *handles = new TextResizeHandles(text);
|
||||
scene.addItem(text);
|
||||
|
||||
drag(scene, *handles, *text, TextResize::BottomRight, QPointF(-40, 0));
|
||||
QVERIFY(text->textWidth() > 0);
|
||||
}
|
||||
|
||||
// A text is never made narrower than its longest word.
|
||||
void dragStopsAtTheLongestWord()
|
||||
{
|
||||
QGraphicsScene scene;
|
||||
auto *text = new WidthText(QStringLiteral("a Motorschutzschalter b"));
|
||||
scene.addItem(text);
|
||||
auto *handles = new TextResizeHandles(text);
|
||||
drag(scene, *handles, *text, TextResize::BottomRight, QPointF(-1000, 0));
|
||||
QCOMPARE(text->textWidth(), std::ceil(TextResize::minimumWidth(text->document())));
|
||||
}
|
||||
|
||||
// A press and release without moving changes nothing.
|
||||
void clickChangesNothing()
|
||||
{
|
||||
QGraphicsScene scene;
|
||||
auto *text = new WidthText(QStringLiteral("Motor protection switch"));
|
||||
scene.addItem(text);
|
||||
auto *handles = new TextResizeHandles(text);
|
||||
QSignalSpy finished(handles, &TextResizeHandles::resizeFinished);
|
||||
drag(scene, *handles, *text, TextResize::TopRight, QPointF());
|
||||
QCOMPARE(finished.count(), 0);
|
||||
QCOMPARE(text->textWidth(), qreal(-1));
|
||||
}
|
||||
|
||||
// Double-clicking a handle goes back to the automatic width.
|
||||
void doubleClickResetsToAutomaticWidth()
|
||||
{
|
||||
QGraphicsScene scene;
|
||||
auto *text = new WidthText(QStringLiteral("Motor protection switch"));
|
||||
scene.addItem(text);
|
||||
text->setWidth(60);
|
||||
auto *handles = new TextResizeHandles(text);
|
||||
QSignalSpy finished(handles, &TextResizeHandles::resizeFinished);
|
||||
|
||||
sendMouse(scene, *handles, TextResize::BottomLeft, QEvent::GraphicsSceneMouseDoubleClick,
|
||||
sceneCorner(*text, TextResize::BottomLeft));
|
||||
QCOMPARE(text->textWidth(), qreal(-1));
|
||||
QCOMPARE(finished.count(), 1);
|
||||
QCOMPARE(finished.first().at(0).toReal(), qreal(60));
|
||||
QCOMPARE(finished.first().at(1).toReal(), qreal(-1));
|
||||
|
||||
//Already automatic: nothing to undo
|
||||
sendMouse(scene, *handles, TextResize::BottomLeft, QEvent::GraphicsSceneMouseDoubleClick,
|
||||
sceneCorner(*text, TextResize::BottomLeft));
|
||||
QCOMPARE(finished.count(), 1);
|
||||
}
|
||||
|
||||
// One undo step gives back the width and the position, one redo
|
||||
// applies them again.
|
||||
void undoRestoresWidthAndPosition()
|
||||
{
|
||||
QGraphicsScene scene;
|
||||
auto *text = new WidthText(QStringLiteral("Motor protection switch Q12"));
|
||||
scene.addItem(text);
|
||||
text->setPos(100, 100);
|
||||
text->setRotation(90);
|
||||
auto *handles = new TextResizeHandles(text);
|
||||
QUndoStack stack;
|
||||
connect(handles, &TextResizeHandles::resizeFinished, &stack,
|
||||
[&stack, text](qreal ow, qreal nw, QPointF op, QPointF np) {
|
||||
stack.push(new TextResizeCommand(text, ow, nw, op, np));
|
||||
});
|
||||
|
||||
drag(scene, *handles, *text, TextResize::TopLeft, QPointF(0, 40));
|
||||
QCOMPARE(stack.count(), 1);
|
||||
const qreal new_width = text->textWidth();
|
||||
const QPointF new_pos = text->pos();
|
||||
QVERIFY(new_pos != QPointF(100, 100));
|
||||
|
||||
stack.undo();
|
||||
QCOMPARE(text->textWidth(), qreal(-1));
|
||||
QCOMPARE(text->pos(), QPointF(100, 100));
|
||||
stack.redo();
|
||||
QCOMPARE(text->textWidth(), new_width);
|
||||
QCOMPARE(text->pos(), new_pos);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_textresizehandles)
|
||||
#include "tst_textresizehandles.moc"
|
||||
Reference in New Issue
Block a user