diff options
38 files changed, 761 insertions, 348 deletions
diff --git a/examples/quickcontrols/stylekit/styles/Haze.qml b/examples/quickcontrols/stylekit/styles/Haze.qml index b3920f7c59..db58af43ad 100644 --- a/examples/quickcontrols/stylekit/styles/Haze.qml +++ b/examples/quickcontrols/stylekit/styles/Haze.qml @@ -7,6 +7,16 @@ import Qt.labs.StyleKit Style { id: style + fonts { + systemFont { + family: "Courier New" + pointSize: 12 + } + textFieldFont { + bold: true + } + } + control { // 'control' is the fallback for all the controls. Any properties that are not // overridden by a specific control underneath will be read from here instead. diff --git a/src/labs/stylekit/Button.qml b/src/labs/stylekit/Button.qml index 734ff24d27..c4d705e09d 100644 --- a/src/labs/stylekit/Button.qml +++ b/src/labs/stylekit/Button.qml @@ -45,7 +45,7 @@ T.Button { icon: control.icon text: control.text - font: control.font + font: styleReader.font // FIXME: should be inherited from control color: styleReader.text.color alignment: styleReader.text.alignment } diff --git a/src/labs/stylekit/CMakeLists.txt b/src/labs/stylekit/CMakeLists.txt index b981bcac62..ed23c75934 100644 --- a/src/labs/stylekit/CMakeLists.txt +++ b/src/labs/stylekit/CMakeLists.txt @@ -26,7 +26,7 @@ qt_internal_add_qml_module(QtQuickStyleKit qqstylekitstorage_p.h qqstylekitstorage.cpp qqstylekitstyle_p.h qqstylekitstyle.cpp qqstylekittheme_p.h qqstylekittheme.cpp - qqstylekitthemeproperties_p.h qqstylekitthemeproperties.cpp + qqstylekitstyleandthemebase_p.h qqstylekitstyleandthemebase.cpp qqstylekitcustomtheme_p.h qqstylekitcustomtheme.cpp qqstylekitvariation_p.h qqstylekitvariation.cpp DEFINES diff --git a/src/labs/stylekit/CheckBox.qml b/src/labs/stylekit/CheckBox.qml index fa5a43f990..af55697bd1 100644 --- a/src/labs/stylekit/CheckBox.qml +++ b/src/labs/stylekit/CheckBox.qml @@ -75,7 +75,7 @@ T.CheckBox { contentItem: CheckLabel { text: control.text - font: control.font + font: styleReader.font color: styleReader.text.color horizontalAlignment: styleReader.text.alignment & Qt.AlignHorizontal_Mask verticalAlignment: styleReader.text.alignment & Qt.AlignVertical_Mask diff --git a/src/labs/stylekit/ComboBox.qml b/src/labs/stylekit/ComboBox.qml index 2592355a5d..bcb6b89f56 100644 --- a/src/labs/stylekit/ComboBox.qml +++ b/src/labs/stylekit/ComboBox.qml @@ -88,7 +88,8 @@ T.ComboBox { height: indicatorItem.height } - contentItem: T.TextField { + contentItem: TextInput { + font: styleReader.font text: control.editable ? control.editText : control.displayText enabled: control.editable autoScroll: control.editable diff --git a/src/labs/stylekit/ItemDelegate.qml b/src/labs/stylekit/ItemDelegate.qml index feca0eafe3..207d22c3b4 100644 --- a/src/labs/stylekit/ItemDelegate.qml +++ b/src/labs/stylekit/ItemDelegate.qml @@ -46,7 +46,7 @@ T.ItemDelegate { display: control.display icon: control.icon text: control.text - font: control.font + font: styleReader.font color: styleReader.text.color alignment: styleReader.text.alignment } diff --git a/src/labs/stylekit/RadioButton.qml b/src/labs/stylekit/RadioButton.qml index 2ef5b62385..ad5a6d0bed 100644 --- a/src/labs/stylekit/RadioButton.qml +++ b/src/labs/stylekit/RadioButton.qml @@ -75,7 +75,7 @@ T.RadioButton { contentItem: CheckLabel { text: control.text - font: control.font + font: styleReader.font color: styleReader.text.color horizontalAlignment: styleReader.text.alignment & Qt.AlignHorizontal_Mask verticalAlignment: styleReader.text.alignment & Qt.AlignVertical_Mask diff --git a/src/labs/stylekit/SpinBox.qml b/src/labs/stylekit/SpinBox.qml index b5476df13a..88eed43261 100644 --- a/src/labs/stylekit/SpinBox.qml +++ b/src/labs/stylekit/SpinBox.qml @@ -103,7 +103,7 @@ T.SpinBox { z: 2 text: control.displayText - font: control.font + font: styleReader.font selectionColor: control.palette.highlight selectedTextColor: control.palette.highlightedText color: styleReader.text.color diff --git a/src/labs/stylekit/Switch.qml b/src/labs/stylekit/Switch.qml index 7202f3b923..8ce306bbe4 100644 --- a/src/labs/stylekit/Switch.qml +++ b/src/labs/stylekit/Switch.qml @@ -85,7 +85,7 @@ T.Switch { contentItem: CheckLabel { text: control.text - font: control.font + font: styleReader.font color: styleReader.text.color horizontalAlignment: styleReader.text.alignment & Qt.AlignHorizontal_Mask verticalAlignment: styleReader.text.alignment & Qt.AlignVertical_Mask diff --git a/src/labs/stylekit/TextField.qml b/src/labs/stylekit/TextField.qml index 0cdf31852b..f148b52784 100644 --- a/src/labs/stylekit/TextField.qml +++ b/src/labs/stylekit/TextField.qml @@ -22,6 +22,14 @@ T.TextField { rightPadding: styleReader.rightPadding bottomPadding: styleReader.bottomPadding + // FIXME: Should work when assigned to control.font directly + font.family: styleReader.font.family + font.pointSize: styleReader.font.pointSize + font.weight: styleReader.font.weight + font.italic: styleReader.font.italic + font.underline: styleReader.font.underline + font.bold: styleReader.font.bold + color: styleReader.text.color verticalAlignment: styleReader.text.alignment & Qt.AlignVertical_Mask horizontalAlignment: styleReader.text.alignment & Qt.AlignHorizontal_Mask diff --git a/src/labs/stylekit/qqstylekitfont.cpp b/src/labs/stylekit/qqstylekitfont.cpp index 2db4eae309..ff1b3cec8d 100644 --- a/src/labs/stylekit/qqstylekitfont.cpp +++ b/src/labs/stylekit/qqstylekitfont.cpp @@ -10,222 +10,185 @@ QQStyleKitFont::QQStyleKitFont(QObject *parent) { } -#define STYLEKIT_FONT_GETTER(propertyName) \ - QFont QQStyleKitFont::propertyName() const \ - { \ - if (!m_##propertyName) { \ - m_##propertyName.reset(new QFont()); \ - } \ - return *m_##propertyName; \ - } - -STYLEKIT_FONT_GETTER(systemFont) -STYLEKIT_FONT_GETTER(buttonFont) -STYLEKIT_FONT_GETTER(checkboxFont) -STYLEKIT_FONT_GETTER(comboBoxFont) -STYLEKIT_FONT_GETTER(groupBoxFont) -STYLEKIT_FONT_GETTER(itemViewFont) -STYLEKIT_FONT_GETTER(labelFont) -STYLEKIT_FONT_GETTER(listViewFont) -STYLEKIT_FONT_GETTER(menuFont) -STYLEKIT_FONT_GETTER(menuBarFont) -STYLEKIT_FONT_GETTER(radioButtonFont) -STYLEKIT_FONT_GETTER(spinBoxFont) -STYLEKIT_FONT_GETTER(switchControlFont) -STYLEKIT_FONT_GETTER(tabBarFont) -STYLEKIT_FONT_GETTER(textAreaFont) -STYLEKIT_FONT_GETTER(textFieldFont) -STYLEKIT_FONT_GETTER(toolBarFont) -STYLEKIT_FONT_GETTER(toolTipFont) -STYLEKIT_FONT_GETTER(tumblerFont) - -void QQStyleKitFont::setSystemFont(const QFont &font) -{ - if (!m_systemFont) - m_systemFont.reset(new QFont(font)); - else - *m_systemFont = font; - emit systemFontChanged(); -} - -void QQStyleKitFont::setButtonFont(const QFont &font) -{ - if (!m_buttonFont) { - m_buttonFont.reset(new QFont(font)); - } else { - *m_buttonFont = font; - } - emit buttonFontChanged(); -} - -void QQStyleKitFont::setCheckboxFont(const QFont &font) -{ - if (!m_checkboxFont) { - m_checkboxFont.reset(new QFont(font)); - } else { - *m_checkboxFont = font; - } - emit checkboxFontChanged(); -} - -void QQStyleKitFont::setComboBoxFont(const QFont &font) -{ - if (!m_comboBoxFont) { - m_comboBoxFont.reset(new QFont(font)); - } else { - *m_comboBoxFont = font; - } - emit comboBoxFontChanged(); -} - -void QQStyleKitFont::setGroupBoxFont(const QFont &font) -{ - if (!m_groupBoxFont) { - m_groupBoxFont.reset(new QFont(font)); - } else { - *m_groupBoxFont = font; - } - emit groupBoxFontChanged(); -} +#define DEFINE_FONT_GETTER(scopeName, scopeEnum) \ + QFont QQStyleKitFont::scopeName##Font() const \ + { \ + return fontForScope(QQuickTheme::scopeEnum); \ + } + +DEFINE_FONT_GETTER(system, System) +DEFINE_FONT_GETTER(button, Button) +DEFINE_FONT_GETTER(checkBox, CheckBox) +DEFINE_FONT_GETTER(comboBox, ComboBox) +DEFINE_FONT_GETTER(groupBox, GroupBox) +DEFINE_FONT_GETTER(itemView, ItemView) +DEFINE_FONT_GETTER(label, Label) +DEFINE_FONT_GETTER(listView, ListView) +DEFINE_FONT_GETTER(menu, Menu) +DEFINE_FONT_GETTER(menuBar, MenuBar) +DEFINE_FONT_GETTER(radioButton, RadioButton) +DEFINE_FONT_GETTER(spinBox, SpinBox) +DEFINE_FONT_GETTER(switchControl, Switch) +DEFINE_FONT_GETTER(tabBar, TabBar) +DEFINE_FONT_GETTER(textArea, TextArea) +DEFINE_FONT_GETTER(textField, TextField) +DEFINE_FONT_GETTER(toolBar, ToolBar) +DEFINE_FONT_GETTER(toolTip, ToolTip) +DEFINE_FONT_GETTER(tumbler, Tumbler) + +#define DEFINE_FONT_SETTER(scopeName, scopeEnum, signal) \ + void QQStyleKitFont::set##scopeName##Font(const QFont &font) \ + { \ + setFontForScope(QQuickTheme::scopeEnum, font, &QQStyleKitFont::signal); \ + } + +DEFINE_FONT_SETTER(System, System, systemFontChanged) +DEFINE_FONT_SETTER(Button, Button, buttonFontChanged) +DEFINE_FONT_SETTER(CheckBox, CheckBox, checkBoxFontChanged) +DEFINE_FONT_SETTER(ComboBox, ComboBox, comboBoxFontChanged) +DEFINE_FONT_SETTER(GroupBox, GroupBox, groupBoxFontChanged) +DEFINE_FONT_SETTER(ItemView, ItemView, itemViewFontChanged) +DEFINE_FONT_SETTER(Label, Label, labelFontChanged) +DEFINE_FONT_SETTER(ListView, ListView, listViewFontChanged) +DEFINE_FONT_SETTER(Menu, Menu, menuFontChanged) +DEFINE_FONT_SETTER(MenuBar, MenuBar, menuBarFontChanged) +DEFINE_FONT_SETTER(RadioButton, RadioButton, radioButtonFontChanged) +DEFINE_FONT_SETTER(SpinBox, SpinBox, spinBoxFontChanged) +DEFINE_FONT_SETTER(SwitchControl, Switch, switchControlFontChanged) +DEFINE_FONT_SETTER(TabBar, TabBar, tabBarFontChanged) +DEFINE_FONT_SETTER(TextArea, TextArea, textAreaFontChanged) +DEFINE_FONT_SETTER(TextField, TextField, textFieldFontChanged) +DEFINE_FONT_SETTER(ToolBar, ToolBar, toolBarFontChanged) +DEFINE_FONT_SETTER(ToolTip, ToolTip, toolTipFontChanged) +DEFINE_FONT_SETTER(Tumbler, Tumbler, tumblerFontChanged) + +void QQStyleKitFont::setFontForScope(QQuickTheme::Scope scope, const QFont &font, void (QQStyleKitFont::*signal)()) +{ + const int index = int(scope); + if (isSet(scope) && m_local[index] == font) + return; + + QFont local = font; + // TODO: Figure out resolve mask to set here + + m_local[index] = font; + markSet(scope); + + m_effectiveDirty = true; + + emit (this->*signal)(); +} + +// The fallback font is used to resolve unset fonts +// The theme fonts fallback to the style fonts and +// style fonts fallback to the fallback style fonts +QQStyleKitFont *QQStyleKitFont::fallbackFont() const +{ + return m_fallback; +} + +void QQStyleKitFont::setFallbackFont(QQStyleKitFont *fallback) +{ + if (m_fallback == fallback) + return; + + if (m_fallback) + disconnect(m_fallback, nullptr, this, nullptr); + + m_fallback = fallback; + + markEffectiveDirty(); + ensureEffectiveUpToDate(); + + if (m_fallback) { + auto makeHandler = [this](void (QQStyleKitFont::*signal)()) { + return [this, signal] { + markEffectiveDirty(); + emit (this->*signal)(); + }; + }; + connect(m_fallback, &QQStyleKitFont::systemFontChanged, this, + makeHandler(&QQStyleKitFont::systemFontChanged)); + connect(m_fallback, &QQStyleKitFont::buttonFontChanged, this, + makeHandler(&QQStyleKitFont::buttonFontChanged)); + connect(m_fallback, &QQStyleKitFont::checkBoxFontChanged, this, + makeHandler(&QQStyleKitFont::checkBoxFontChanged)); + connect(m_fallback, &QQStyleKitFont::comboBoxFontChanged, this, + makeHandler(&QQStyleKitFont::comboBoxFontChanged)); + connect(m_fallback, &QQStyleKitFont::groupBoxFontChanged, this, + makeHandler(&QQStyleKitFont::groupBoxFontChanged)); + connect(m_fallback, &QQStyleKitFont::itemViewFontChanged, this, + makeHandler(&QQStyleKitFont::itemViewFontChanged)); + connect(m_fallback, &QQStyleKitFont::labelFontChanged, this, + makeHandler(&QQStyleKitFont::labelFontChanged)); + connect(m_fallback, &QQStyleKitFont::listViewFontChanged, this, + makeHandler(&QQStyleKitFont::listViewFontChanged)); + connect(m_fallback, &QQStyleKitFont::menuFontChanged, this, + makeHandler(&QQStyleKitFont::menuFontChanged)); + connect(m_fallback, &QQStyleKitFont::menuBarFontChanged, this, + makeHandler(&QQStyleKitFont::menuBarFontChanged)); + connect(m_fallback, &QQStyleKitFont::radioButtonFontChanged, this, + makeHandler(&QQStyleKitFont::radioButtonFontChanged)); + connect(m_fallback, &QQStyleKitFont::spinBoxFontChanged, this, + makeHandler(&QQStyleKitFont::spinBoxFontChanged)); + connect(m_fallback, &QQStyleKitFont::switchControlFontChanged, this, + makeHandler(&QQStyleKitFont::switchControlFontChanged)); + connect(m_fallback, &QQStyleKitFont::tabBarFontChanged, this, + makeHandler(&QQStyleKitFont::tabBarFontChanged)); + connect(m_fallback, &QQStyleKitFont::textAreaFontChanged, this, + makeHandler(&QQStyleKitFont::textAreaFontChanged)); + connect(m_fallback, &QQStyleKitFont::textFieldFontChanged, this, + makeHandler(&QQStyleKitFont::textFieldFontChanged)); + connect(m_fallback, &QQStyleKitFont::toolBarFontChanged, this, + makeHandler(&QQStyleKitFont::toolBarFontChanged)); + connect(m_fallback, &QQStyleKitFont::toolTipFontChanged, this, + makeHandler(&QQStyleKitFont::toolTipFontChanged)); + connect(m_fallback, &QQStyleKitFont::tumblerFontChanged, this, + makeHandler(&QQStyleKitFont::tumblerFontChanged)); + } + emit fallbackFontChanged(); +} + +QFont QQStyleKitFont::fontForScope(QQuickTheme::Scope scope) const +{ + ensureEffectiveUpToDate(); + return m_effective[int(scope)]; +} -void QQStyleKitFont::setItemViewFont(const QFont &font) +void QQStyleKitFont::ensureEffectiveUpToDate() const { - if (!m_itemViewFont) { - m_itemViewFont.reset(new QFont(font)); - } else { - *m_itemViewFont = font; - } - emit itemViewFontChanged(); -} + if (!m_effectiveDirty) + return; -void QQStyleKitFont::setLabelFont(const QFont &font) -{ - if (!m_labelFont) { - m_labelFont.reset(new QFont(font)); - } else { - *m_labelFont = font; - } - emit labelFontChanged(); -} + const int sysIdx = int(QQuickTheme::System); -void QQStyleKitFont::setListViewFont(const QFont &font) -{ - if (!m_listViewFont) { - m_listViewFont.reset(new QFont(font)); - } else { - *m_listViewFont = font; - } - emit listViewFontChanged(); -} + { + const QFont localSys = isSet(QQuickTheme::System) ? m_local[sysIdx] : QFont(); + const QFont fbSys = m_fallback ? m_fallback->fontForScope(QQuickTheme::System) : QFont(); + // TODO: Resolve mask? + m_effective[sysIdx] = localSys.resolve(fbSys); -void QQStyleKitFont::setMenuFont(const QFont &font) -{ - if (!m_menuFont) { - m_menuFont.reset(new QFont(font)); - } else { - *m_menuFont = font; } - emit menuFontChanged(); -} -void QQStyleKitFont::setMenuBarFont(const QFont &font) -{ - if (!m_menuBarFont) { - m_menuBarFont.reset(new QFont(font)); - } else { - *m_menuBarFont = font; - } - emit menuBarFontChanged(); -} + const QFont systemEff = m_effective[sysIdx]; + const QFont fallbackSystem = m_fallback ? m_fallback->fontForScope(QQuickTheme::System) : QFont(); -void QQStyleKitFont::setRadioButtonFont(const QFont &font) -{ - if (!m_radioButtonFont) { - m_radioButtonFont.reset(new QFont(font)); - } else { - *m_radioButtonFont = font; - } - emit radioButtonFontChanged(); -} - -void QQStyleKitFont::setSpinBoxFont(const QFont &font) -{ - if (!m_spinBoxFont) { - m_spinBoxFont.reset(new QFont(font)); - } else { - *m_spinBoxFont = font; - } - emit spinBoxFontChanged(); -} - -void QQStyleKitFont::setSwitchControlFont(const QFont &font) -{ - if (!m_switchControlFont) { - m_switchControlFont.reset(new QFont(font)); - } else { - *m_switchControlFont = font; - } - emit switchControlFontChanged(); -} - -void QQStyleKitFont::setTabBarFont(const QFont &font) -{ - if (!m_tabBarFont) { - m_tabBarFont.reset(new QFont(font)); - } else { - *m_tabBarFont = font; - } - emit tabBarFontChanged(); -} - -void QQStyleKitFont::setTextAreaFont(const QFont &font) -{ - if (!m_textAreaFont) { - m_textAreaFont.reset(new QFont(font)); - } else { - *m_textAreaFont = font; - } - emit textAreaFontChanged(); -} - -void QQStyleKitFont::setTextFieldFont(const QFont &font) -{ - if (!m_textFieldFont) { - m_textFieldFont.reset(new QFont(font)); - } else { - *m_textFieldFont = font; - } - emit textFieldFontChanged(); -} - -void QQStyleKitFont::setToolBarFont(const QFont &font) -{ - if (!m_toolBarFont) { - m_toolBarFont.reset(new QFont(font)); - } else { - *m_toolBarFont = font; - } - emit toolBarFontChanged(); -} - -void QQStyleKitFont::setToolTipFont(const QFont &font) -{ - if (!m_toolTipFont) { - m_toolTipFont.reset(new QFont(font)); - } else { - *m_toolTipFont = font; - } - emit toolTipFontChanged(); -} - -void QQStyleKitFont::setTumblerFont(const QFont &font) -{ - if (!m_tumblerFont) { - m_tumblerFont.reset(new QFont(font)); - } else { - *m_tumblerFont = font; - } - emit tumblerFontChanged(); + // Scopes: localScope > localSystem > fallbackScope > fallbackSystem + for (int i = 0; i < NScopes; ++i) { + if (i == sysIdx) + continue; + + const QQuickTheme::Scope scope = static_cast<QQuickTheme::Scope>(i); + const QFont localRole = isSet(scope) ? m_local[i] : QFont(); + const QFont fallbackRole = m_fallback ? m_fallback->fontForScope(scope) : QFont(); + + QFont fallbackLayer = fallbackRole.resolve(fallbackSystem); + QFont base = systemEff.resolve(fallbackLayer); + // TODO: Resolve mask? + m_effective[i] = localRole.resolve(base); + } + + m_effectiveDirty = false; } QT_END_NAMESPACE diff --git a/src/labs/stylekit/qqstylekitfont_p.h b/src/labs/stylekit/qqstylekitfont_p.h index eeadcdc0f4..41854222d8 100644 --- a/src/labs/stylekit/qqstylekitfont_p.h +++ b/src/labs/stylekit/qqstylekitfont_p.h @@ -17,6 +17,7 @@ // #include <QtQml/QtQml> +#include <QtQuickTemplates2/private/qquicktheme_p.h> #include <QtGui/qfont.h> QT_BEGIN_NAMESPACE @@ -26,7 +27,7 @@ class QQStyleKitFont : public QObject Q_OBJECT Q_PROPERTY(QFont systemFont READ systemFont WRITE setSystemFont NOTIFY systemFontChanged FINAL) Q_PROPERTY(QFont buttonFont READ buttonFont WRITE setButtonFont NOTIFY buttonFontChanged FINAL) - Q_PROPERTY(QFont checkboxFont READ checkboxFont WRITE setCheckboxFont NOTIFY checkboxFontChanged FINAL) + Q_PROPERTY(QFont checkBoxFont READ checkBoxFont WRITE setCheckBoxFont NOTIFY checkBoxFontChanged FINAL) Q_PROPERTY(QFont comboBoxFont READ comboBoxFont WRITE setComboBoxFont NOTIFY comboBoxFontChanged FINAL) Q_PROPERTY(QFont groupBoxFont READ groupBoxFont WRITE setGroupBoxFont NOTIFY groupBoxFontChanged FINAL) Q_PROPERTY(QFont itemViewFont READ itemViewFont WRITE setItemViewFont NOTIFY itemViewFontChanged FINAL) @@ -54,8 +55,8 @@ public: QFont buttonFont() const; void setButtonFont(const QFont &font); - QFont checkboxFont() const; - void setCheckboxFont(const QFont &font); + QFont checkBoxFont() const; + void setCheckBoxFont(const QFont &font); QFont comboBoxFont() const; void setComboBoxFont(const QFont &font); @@ -105,10 +106,15 @@ public: QFont tumblerFont() const; void setTumblerFont(const QFont &font); + QQStyleKitFont *fallbackFont() const; + void setFallbackFont(QQStyleKitFont *fallback); + + QFont fontForScope(QQuickTheme::Scope scope) const; + signals: void systemFontChanged(); void buttonFontChanged(); - void checkboxFontChanged(); + void checkBoxFontChanged(); void comboBoxFontChanged(); void groupBoxFontChanged(); void itemViewFontChanged(); @@ -125,29 +131,26 @@ signals: void toolBarFontChanged(); void toolTipFontChanged(); void tumblerFontChanged(); + void fallbackFontChanged(); private: Q_DISABLE_COPY(QQStyleKitFont) - mutable std::unique_ptr<QFont> m_systemFont; - mutable std::unique_ptr<QFont> m_buttonFont; - mutable std::unique_ptr<QFont> m_checkboxFont; - mutable std::unique_ptr<QFont> m_comboBoxFont; - mutable std::unique_ptr<QFont> m_groupBoxFont; - mutable std::unique_ptr<QFont> m_itemViewFont; - mutable std::unique_ptr<QFont> m_labelFont; - mutable std::unique_ptr<QFont> m_listViewFont; - mutable std::unique_ptr<QFont> m_menuFont; - mutable std::unique_ptr<QFont> m_menuBarFont; - mutable std::unique_ptr<QFont> m_radioButtonFont; - mutable std::unique_ptr<QFont> m_spinBoxFont; - mutable std::unique_ptr<QFont> m_switchControlFont; - mutable std::unique_ptr<QFont> m_tabBarFont; - mutable std::unique_ptr<QFont> m_textAreaFont; - mutable std::unique_ptr<QFont> m_textFieldFont; - mutable std::unique_ptr<QFont> m_toolBarFont; - mutable std::unique_ptr<QFont> m_toolTipFont; - mutable std::unique_ptr<QFont> m_tumblerFont; + void ensureEffectiveUpToDate() const; + void markEffectiveDirty() { m_effectiveDirty = true; } + + void setFontForScope(QQuickTheme::Scope scope, const QFont &font, void (QQStyleKitFont::*signal)()); + + bool isSet(QQuickTheme::Scope scope) const { return (m_setMask & (1u << int(scope))) != 0; } + void markSet(QQuickTheme::Scope scope) { m_setMask |= (1u << int(scope)); } + + static const int NScopes = QQuickTheme::Tumbler + 1; + QFont m_local[NScopes]; + mutable QFont m_effective[NScopes]; + quint32 m_setMask = 0; + mutable bool m_effectiveDirty = true; + + QQStyleKitFont *m_fallback = nullptr; }; QT_END_NAMESPACE diff --git a/src/labs/stylekit/qqstylekitreader.cpp b/src/labs/stylekit/qqstylekitreader.cpp index f6a2b974fd..71ea689377 100644 --- a/src/labs/stylekit/qqstylekitreader.cpp +++ b/src/labs/stylekit/qqstylekitreader.cpp @@ -243,6 +243,8 @@ void QQStyleKitReader::updateControl() default: Q_UNREACHABLE(); } + + setFont(style->fontForReader(this)); } void QQStyleKitReader::resetAll() @@ -251,9 +253,19 @@ void QQStyleKitReader::resetAll() reader->m_effectiveVariationsDirty = true; reader->clearLocalStorage(); reader->emitChangedForAllStyleProperties(); + reader->updateFontFromTheme(); } } +void QQStyleKitReader::updateFontFromTheme() +{ + const QQStyleKitStyle *style = QQStyleKitStyle::current(); + if (!style || !style->loaded()) + return; + + setFont(style->fontForReader(this)); +} + void QQStyleKitReader::populateLocalStorage() { if (!m_storage.isEmpty()) @@ -485,6 +497,20 @@ void QQStyleKitReader::setPalette(QQuickPalette *palette) emitChangedForAllStyleProperties(); } +QFont QQStyleKitReader::font() const +{ + return m_font; +} + +void QQStyleKitReader::setFont(const QFont &font) +{ + if (m_font == font) + return; + + m_font = font; + emit fontChanged(); +} + QQStyleKitControlProperties *QQStyleKitReader::global() const { return &const_cast<QQStyleKitReader *>(this)->m_global; diff --git a/src/labs/stylekit/qqstylekitreader_p.h b/src/labs/stylekit/qqstylekitreader_p.h index 860c12855f..33257141a9 100644 --- a/src/labs/stylekit/qqstylekitreader_p.h +++ b/src/labs/stylekit/qqstylekitreader_p.h @@ -21,6 +21,7 @@ #include "qqstylekitglobal_p.h" #include "qqstylekitcontrolproperties_p.h" +#include "qqstylekitfont_p.h" #include "qqstylekitstorage_p.h" QT_BEGIN_NAMESPACE @@ -40,6 +41,7 @@ class QQStyleKitReader : public QQStyleKitControlProperties Q_PROPERTY(bool pressed READ pressed WRITE setPressed NOTIFY pressedChanged FINAL) Q_PROPERTY(bool vertical READ vertical WRITE setVertical NOTIFY verticalChanged FINAL) Q_PROPERTY(bool highlighted READ highlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL) + Q_PROPERTY(QFont font READ font NOTIFY fontChanged FINAL) Q_PROPERTY(QQuickPalette *palette READ palette WRITE setPalette NOTIFY paletteChanged FINAL) Q_PROPERTY(QQStyleKitControlProperties *global READ global CONSTANT FINAL) @@ -111,6 +113,9 @@ public: bool highlighted() const; void setHighlighted(bool highlighted); + QFont font() const; + void setFont(const QFont &font); + QQStyleKitControlProperties *global() const; QVariant readStyleProperty(PropertyStorageId key) const; @@ -123,6 +128,8 @@ public: static bool transitionEnabled(); static void resetAll(); + void updateFontFromTheme(); + static QList<QQStyleKitReader *> s_allReaders; signals: @@ -137,6 +144,7 @@ signals: void paletteChanged(); void verticalChanged(); void highlightedChanged(); + void fontChanged(); private: void updateControl(); @@ -163,6 +171,7 @@ private: bool m_effectiveVariationsDirty: 1; QQuickPalette m_palette; + QFont m_font; mutable QQStyleKitPropertyStorage m_storage; AlternateState m_alternateState = AlternateState::Alternate1; QQSK::State m_state = QQSK::StateFlag::NoState; diff --git a/src/labs/stylekit/qqstylekitstyle.cpp b/src/labs/stylekit/qqstylekitstyle.cpp index 624294fc03..e15fa8674d 100644 --- a/src/labs/stylekit/qqstylekitstyle.cpp +++ b/src/labs/stylekit/qqstylekitstyle.cpp @@ -21,7 +21,7 @@ static const QString kLight = "Light"_L1; static const QString kDark = "Dark"_L1; QQStyleKitStyle::QQStyleKitStyle(QObject *parent) - : QQStyleKitControls(parent) + : QQStyleKitStyleAndThemeBase(parent) , m_themeName(kSystem) { } @@ -62,6 +62,12 @@ void QQStyleKitStyle::setFallbackStyle(QQStyleKitStyle *fallbackStyle) m_fallbackStyle = fallbackStyle; emit fallbackStyleChanged(); + + if (fonts()) + fonts()->setFallbackFont(m_fallbackStyle ? m_fallbackStyle->fonts() : nullptr); + + if (m_theme && m_theme->fonts()) + m_theme->fonts()->setFallbackFont(fonts()); } void QQStyleKitStyle::setLight(QQmlComponent *lightTheme) @@ -234,8 +240,14 @@ void QQStyleKitStyle::recreateTheme() m_theme->setParent(this); } + if (m_theme && m_theme->fonts()) + m_theme->fonts()->setFallbackFont(fonts()); if (this == current()) { m_theme->updateQuickTheme(); + if (m_theme->fonts()) + m_theme->fonts()->setFallbackFont(fonts()); + if (fonts()) + fonts()->setFallbackFont(m_fallbackStyle ? m_fallbackStyle->fonts() : nullptr); QQStyleKitReader::resetAll(); } @@ -247,6 +259,38 @@ QQStyleKitStyle* QQStyleKitStyle::current() return QQStyleKit::qmlAttachedProperties()->style(); } +QFont QQStyleKitStyle::fontForReader(QQStyleKitReader *reader) const +{ + switch (reader->type()) { + case QQStyleKitReader::ControlType::Control: + return m_theme->fonts()->systemFont(); + case QQStyleKitReader::ControlType::AbstractButton: + case QQStyleKitReader::ControlType::Button: + case QQStyleKitReader::ControlType::FlatButton: { + return m_theme->fonts()->buttonFont(); + } + case QQStyleKitReader::ControlType::CheckBox: + return m_theme->fonts()->checkBoxFont(); + case QQStyleKitReader::ControlType::ComboBox: + return m_theme->fonts()->comboBoxFont(); + case QQStyleKitReader::ControlType::RadioButton: + return m_theme->fonts()->radioButtonFont(); + case QQStyleKitReader::ControlType::SpinBox: + return m_theme->fonts()->spinBoxFont(); + case QQStyleKitReader::ControlType::SwitchControl: + return m_theme->fonts()->switchControlFont(); + case QQStyleKitReader::ControlType::TextInput: + case QQStyleKitReader::ControlType::TextField: + return m_theme->fonts()->textFieldFont(); + case QQStyleKitReader::ControlType::TextArea: + return m_theme->fonts()->textAreaFont(); + case QQStyleKitReader::ControlType::ItemDelegate: + return m_theme->fonts()->itemViewFont(); + default: + return m_theme->fonts()->systemFont(); + } +} + bool QQStyleKitStyle::loaded() const { /* Before both the style and theme has completed loading diff --git a/src/labs/stylekit/qqstylekitstyle_p.h b/src/labs/stylekit/qqstylekitstyle_p.h index ac560f7b88..2aba7ddbf5 100644 --- a/src/labs/stylekit/qqstylekitstyle_p.h +++ b/src/labs/stylekit/qqstylekitstyle_p.h @@ -20,6 +20,7 @@ #include "qqstylekitcontrols_p.h" #include "qqstylekitcustomtheme_p.h" #include "qqstylekitdebug_p.h" +#include "qqstylekitstyleandthemebase_p.h" #include <QtQml/QtQml> #include <QtQuickTemplates2/private/qquickdeferredpointer_p_p.h> @@ -29,7 +30,7 @@ QT_BEGIN_NAMESPACE class QQStyleKitTheme; class QQStyleKitPropertyResolver; -class QQStyleKitStyle : public QQStyleKitControls +class QQStyleKitStyle : public QQStyleKitStyleAndThemeBase { Q_OBJECT Q_PROPERTY(QQuickPalette *palette READ palette NOTIFY paletteChanged FINAL) @@ -76,6 +77,8 @@ public: static QQStyleKitStyle *current(); + QFont fontForReader(QQStyleKitReader *reader) const; + // For now, used by qqcontrolstowidgetstyle Q_INVOKABLE QList<QObject *> customThemesAsList(); diff --git a/src/labs/stylekit/qqstylekitthemeproperties.cpp b/src/labs/stylekit/qqstylekitstyleandthemebase.cpp index 7d342c563b..6ff18b26cc 100644 --- a/src/labs/stylekit/qqstylekitthemeproperties.cpp +++ b/src/labs/stylekit/qqstylekitstyleandthemebase.cpp @@ -1,22 +1,22 @@ // Copyright (C) 2025 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#include "qqstylekitthemeproperties_p.h" +#include "qqstylekitstyleandthemebase_p.h" QT_BEGIN_NAMESPACE -QQStyleKitThemeProperties::QQStyleKitThemeProperties(QObject *parent) +QQStyleKitStyleAndThemeBase::QQStyleKitStyleAndThemeBase(QObject *parent) : QQStyleKitControls(parent) { } -QQStyleKitFont *QQStyleKitThemeProperties::fonts() +QQStyleKitFont *QQStyleKitStyleAndThemeBase::fonts() { return &m_fonts; } QT_END_NAMESPACE -#include "moc_qqstylekitthemeproperties_p.cpp" +#include "moc_qqstylekitstyleandthemebase_p.cpp" diff --git a/src/labs/stylekit/qqstylekitthemeproperties_p.h b/src/labs/stylekit/qqstylekitstyleandthemebase_p.h index 5d8985441b..c59fde77c8 100644 --- a/src/labs/stylekit/qqstylekitthemeproperties_p.h +++ b/src/labs/stylekit/qqstylekitstyleandthemebase_p.h @@ -2,8 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Qt-Security score:significant reason:default -#ifndef QQSTYLEKITTHEMEPROPERTIES_P_H -#define QQSTYLEKITTHEMEPROPERTIES_P_H +#ifndef QQSTYLEKITTSTYLEANDTHEMEBASE_P_H +#define QQSTYLEKITTSTYLEANDTHEMEBASE_P_H // // W A R N I N G @@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE -class QQStyleKitThemeProperties : public QQStyleKitControls +class QQStyleKitStyleAndThemeBase : public QQStyleKitControls { Q_OBJECT Q_PROPERTY(QQStyleKitFont *fonts READ fonts NOTIFY fontsChanged FINAL) @@ -31,7 +31,7 @@ class QQStyleKitThemeProperties : public QQStyleKitControls QML_NAMED_ELEMENT(ThemeProperties) public: - QQStyleKitThemeProperties(QObject *parent = nullptr); + QQStyleKitStyleAndThemeBase(QObject *parent = nullptr); QQStyleKitFont *fonts(); @@ -39,11 +39,11 @@ signals: void fontsChanged(); private: - Q_DISABLE_COPY(QQStyleKitThemeProperties) + Q_DISABLE_COPY(QQStyleKitStyleAndThemeBase) QQStyleKitFont m_fonts; }; QT_END_NAMESPACE -#endif // QQSTYLEKITTHEMEPROPERTIES_P_H +#endif // QQSTYLEKITTSTYLEANDTHEMEBASE_P_H diff --git a/src/labs/stylekit/qqstylekittheme.cpp b/src/labs/stylekit/qqstylekittheme.cpp index 08cec449c9..6996f299cf 100644 --- a/src/labs/stylekit/qqstylekittheme.cpp +++ b/src/labs/stylekit/qqstylekittheme.cpp @@ -12,7 +12,7 @@ QT_BEGIN_NAMESPACE QQStyleKitTheme::QQStyleKitTheme(QObject *parent) - : QQStyleKitControls(parent) + : QQStyleKitStyleAndThemeBase(parent) { } diff --git a/src/labs/stylekit/qqstylekittheme_p.h b/src/labs/stylekit/qqstylekittheme_p.h index 8096e9935b..6b09eea3f3 100644 --- a/src/labs/stylekit/qqstylekittheme_p.h +++ b/src/labs/stylekit/qqstylekittheme_p.h @@ -18,7 +18,7 @@ #include <QtQml/QtQml> -#include "qqstylekitcontrols_p.h" +#include "qqstylekitstyleandthemebase_p.h" #include "qqstylekitpalette_p.h" QT_BEGIN_NAMESPACE @@ -26,10 +26,11 @@ QT_BEGIN_NAMESPACE class QQStyleKitControls; class QQStyleKitPropertyResolver; -class QQStyleKitTheme : public QQStyleKitControls +class QQStyleKitTheme : public QQStyleKitStyleAndThemeBase { Q_OBJECT + // TODO: Move to QQStyleKitStyleAndThemeBase Q_PROPERTY(QQStyleKitPalette *palettes READ palettes NOTIFY palettesChanged FINAL) QML_NAMED_ELEMENT(Theme) diff --git a/src/qmlls/qqmlcodemodel_p.h b/src/qmlls/qqmlcodemodel_p.h index dba6060dff..d2851a885f 100644 --- a/src/qmlls/qqmlcodemodel_p.h +++ b/src/qmlls/qqmlcodemodel_p.h @@ -20,6 +20,7 @@ #include "qtextdocument_p.h" #include "qprocessscheduler_p.h" #include "qqmllshelputils_p.h" +#include "qqmlsemantictokens_p.h" #include <QObject> #include <QHash> @@ -70,7 +71,7 @@ public: struct RegisteredSemanticTokens { QByteArray resultId = "0"; - QList<int> lastTokens; + QmlHighlighting::HighlightsContainer highlights; }; struct ModuleSetting diff --git a/src/qmlls/qqmlhighlightsupport.cpp b/src/qmlls/qqmlhighlightsupport.cpp index 70f484d245..50617cceb9 100644 --- a/src/qmlls/qqmlhighlightsupport.cpp +++ b/src/qmlls/qqmlhighlightsupport.cpp @@ -3,12 +3,14 @@ // Qt-Security score:significant reason:default #include <qqmlhighlightsupport_p.h> +#include <qqmldiffer_p.h> QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; using namespace QLspSpecification; using namespace QQmlJS::Dom; +using namespace QmlHighlighting; /*! \internal @@ -37,7 +39,36 @@ QList<QByteArray> defaultTokenModifiersList() QList<QByteArray> extendedTokenTypesList() { - return enumToByteArray<QmlHighlighting::SemanticTokenProtocolTypes>(); + return enumToByteArray<SemanticTokenProtocolTypes>(); +} + +static QList<int> generateHighlights(QmlLsp::RegisteredSemanticTokens &cached, + const QmlLsp::OpenDocument &doc, + const std::optional<HighlightsRange> &range, + HighlightingMode mode) +{ + DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely); + const auto fileObject = file.ownerAs<QmlFile>(); + QmlHighlighting::Utils::updateResultID(cached.resultId); + if (!fileObject || !(fileObject && fileObject->isValid())) { + if (const auto lastValidItem = doc.snapshot.validDoc.ownerAs<QmlFile>()) { + const auto shiftedHighlights = QmlHighlighting::Utils::shiftHighlights( + cached.highlights, lastValidItem->code(), doc.textDocument->toPlainText()); + return QmlHighlighting::Utils::encodeSemanticTokens(shiftedHighlights, mode); + } else { + // TODO: Implement regexp based fallback highlighting + return {}; + } + } else { + HighlightsContainer highlights = QmlHighlighting::Utils::visitTokens(file, range); + if (highlights.isEmpty()) + return {}; + // Record the highlights for future diffs, only record full highlights + if (!range.has_value() ) + cached.highlights = highlights; + + return QmlHighlighting::Utils::encodeSemanticTokens(highlights, mode); + } } /*! @@ -47,7 +78,7 @@ https://microsoft.github.io/language-server-protocol/specifications/specificatio Sends a QLspSpecification::SemanticTokens data as response that is generated for the entire file. */ SemanticTokenFullHandler::SemanticTokenFullHandler(QmlLsp::QQmlCodeModelManager *codeModelManager) - : QQmlBaseModule(codeModelManager), m_mode(QmlHighlighting::HighlightingMode::Default) + : QQmlBaseModule(codeModelManager), m_mode(HighlightingMode::Default) { } @@ -63,23 +94,14 @@ void SemanticTokenFullHandler::process( ResponseScopeGuard guard(result, request->m_response); const QByteArray uri = QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri); const auto doc = m_codeModelManager->openDocumentByUrl(uri); - DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely); - const auto fileObject = file.ownerAs<QmlFile>(); - if (!fileObject || !(fileObject && fileObject->isValid())) { - guard.setError({ - int(QLspSpecification::ErrorCodes::RequestCancelled), - "Cannot proceed: current QML document is invalid!"_L1, - }); + auto &cached = m_codeModelManager->registeredTokens(uri); + const auto encoded = generateHighlights(cached, doc, std::nullopt, m_mode); + + if (encoded.isEmpty()) { + result = nullptr; return; - } - auto &&encoded = QmlHighlighting::Utils::collectTokens(file, std::nullopt, m_mode); - auto ®isteredTokens = m_codeModelManager->registeredTokens(uri); - if (!encoded.isEmpty()) { - QmlHighlighting::Utils::updateResultID(registeredTokens.resultId); - result = SemanticTokens{ registeredTokens.resultId, encoded }; - registeredTokens.lastTokens = std::move(encoded); } else { - result = nullptr; + result = SemanticTokens{cached.resultId, std::move(encoded)}; } } @@ -96,7 +118,7 @@ Sends either SemanticTokens or SemanticTokensDelta data as response. This is generally requested when the text document is edited after receiving full highlighting data. */ SemanticTokenDeltaHandler::SemanticTokenDeltaHandler(QmlLsp::QQmlCodeModelManager *codeModelManager) - : QQmlBaseModule(codeModelManager), m_mode(QmlHighlighting::HighlightingMode::Default) + : QQmlBaseModule(codeModelManager), m_mode(HighlightingMode::Default) { } @@ -112,33 +134,20 @@ void SemanticTokenDeltaHandler::process( ResponseScopeGuard guard(result, request->m_response); const QByteArray uri = QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri); const auto doc = m_codeModelManager->openDocumentByUrl(uri); - DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely); - const auto fileObject = file.ownerAs<QmlFile>(); - if (!fileObject || !(fileObject && fileObject->isValid())) { - guard.setError({ - int(QLspSpecification::ErrorCodes::RequestCancelled), - "Cannot proceed: current QML document is invalid!"_L1, - }); - return; - } - auto newEncoded = QmlHighlighting::Utils::collectTokens(file, std::nullopt, m_mode); - auto ®isteredTokens = m_codeModelManager->registeredTokens(uri); - const auto lastResultId = registeredTokens.resultId; - QmlHighlighting::Utils::updateResultID(registeredTokens.resultId); - - // Return full token list if result ids not align - // otherwise compute the delta. - if (lastResultId == request->m_parameters.previousResultId) { - result = QLspSpecification::SemanticTokensDelta{ - registeredTokens.resultId, - QmlHighlighting::Utils::computeDiff(registeredTokens.lastTokens, newEncoded) - }; - } else if (!newEncoded.isEmpty()) { - result = QLspSpecification::SemanticTokens{ registeredTokens.resultId, newEncoded }; + auto &cached = m_codeModelManager->registeredTokens(uri); + if (cached.resultId != request->m_parameters.previousResultId) { + // The client is out of sync, send full tokens + cached.resultId = request->m_parameters.previousResultId; + const auto encoded = generateHighlights(cached, doc, std::nullopt, m_mode); + result = QLspSpecification::SemanticTokens{ cached.resultId, encoded }; } else { - result = nullptr; + const auto cachedHighlights = QmlHighlighting::Utils::encodeSemanticTokens(cached.highlights); + const auto encoded = generateHighlights(cached, doc, std::nullopt, m_mode); + result = QLspSpecification::SemanticTokensDelta{ + cached.resultId, + QmlHighlighting::Utils::computeDiff(cachedHighlights, encoded) + }; } - registeredTokens.lastTokens = std::move(newEncoded); } void SemanticTokenDeltaHandler::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol) @@ -153,7 +162,7 @@ https://microsoft.github.io/language-server-protocol/specifications/specificatio Sends a QLspSpecification::SemanticTokens data as response that is generated for a range of file. */ SemanticTokenRangeHandler::SemanticTokenRangeHandler(QmlLsp::QQmlCodeModelManager *codeModelManager) - : QQmlBaseModule(codeModelManager), m_mode(QmlHighlighting::HighlightingMode::Default) + : QQmlBaseModule(codeModelManager), m_mode(HighlightingMode::Default) { } @@ -169,28 +178,21 @@ void SemanticTokenRangeHandler::process( ResponseScopeGuard guard(result, request->m_response); const QByteArray uri = QQmlLSUtils::lspUriToQmlUrl(request->m_parameters.textDocument.uri); const auto doc = m_codeModelManager->openDocumentByUrl(uri); - DomItem file = doc.snapshot.doc.fileObject(GoTo::MostLikely); - const auto qmlFile = file.as<QmlFile>(); - if (!qmlFile || !(qmlFile && qmlFile->isValid())) { - guard.setError({ - int(QLspSpecification::ErrorCodes::RequestCancelled), - "Cannot proceed: current QML document is invalid!"_L1, - }); - return; - } - const QString &code = qmlFile->code(); + const QString code = doc.textDocument->toPlainText(); const auto range = request->m_parameters.range; int startOffset = int(QQmlLSUtils::textOffsetFrom(code, range.start.line, range.end.character)); int endOffset = int(QQmlLSUtils::textOffsetFrom(code, range.end.line, range.end.character)); - auto &&encoded = QmlHighlighting::Utils::collectTokens( - file, QmlHighlighting::HighlightsRange{ startOffset, endOffset }, m_mode); - auto ®isteredTokens = m_codeModelManager->registeredTokens(uri); - if (!encoded.isEmpty()) { - QmlHighlighting::Utils::updateResultID(registeredTokens.resultId); - result = SemanticTokens{ registeredTokens.resultId, std::move(encoded) }; - } else { + auto &cached = m_codeModelManager->registeredTokens(uri); + const auto encodedTokens = generateHighlights( + cached, + doc, + QmlHighlighting::HighlightsRange{ startOffset, endOffset }, + m_mode); + if (encodedTokens.isEmpty()) { result = nullptr; + } else { + result = SemanticTokens{ cached.resultId, std::move(encodedTokens) }; } } @@ -226,7 +228,7 @@ void QQmlHighlightSupport::setupCapabilities( if (auto clientInitOptions = clientCapabilities.initializationOptions) { if ((*clientInitOptions)[u"qtCreatorHighlighting"_s].toBool(false)) { - const auto mode = QmlHighlighting::HighlightingMode::QtCHighlighting; + const auto mode = HighlightingMode::QtCHighlighting; m_delta.setHighlightingMode(mode); m_full.setHighlightingMode(mode); m_range.setHighlightingMode(mode); diff --git a/src/qmlls/qqmlsemantictokens.cpp b/src/qmlls/qqmlsemantictokens.cpp index b5b38f827f..3a0157f447 100644 --- a/src/qmlls/qqmlsemantictokens.cpp +++ b/src/qmlls/qqmlsemantictokens.cpp @@ -1057,11 +1057,15 @@ HighlightsContainer Utils::visitTokens(const QQmlJS::Dom::DomItem &item, return highlightDomElements.highlights(); } -QList<int> Utils::collectTokens(const QQmlJS::Dom::DomItem &item, - const std::optional<HighlightsRange> &range, - HighlightingMode mode) +HighlightsContainer Utils::shiftHighlights(const HighlightsContainer &cachedHighlights, + const QString &lastValidCode, const QString ¤tCode) { - return Utils::encodeSemanticTokens(visitTokens(item, range), mode); + using namespace QQmlLSUtils; + Differ differ; + const QList<Diff> diffs = differ.diff(lastValidCode, currentCode); + HighlightsContainer shifts = cachedHighlights; + applyDiffs(shifts, diffs); + return shifts; } static std::pair<quint32, quint32> newlineCountAndLastLineLength(const QString &text) @@ -1069,6 +1073,7 @@ static std::pair<quint32, quint32> newlineCountAndLastLineLength(const QString & auto [row, col] = QQmlJS::SourceLocation::rowAndColumnFrom(text, text.size()); return { row - 1, col - 1 }; // rows are 1-based, so subtract 1 to get the number of newlines } + static void updateCursorPositionByDiff(const QString &text, QQmlJS::SourceLocation &cursor) { auto [newLines, lastLineLength] = newlineCountAndLastLineLength(text); diff --git a/src/qmlls/qqmlsemantictokens_p.h b/src/qmlls/qqmlsemantictokens_p.h index 1641c9f303..0674a0a3cc 100644 --- a/src/qmlls/qqmlsemantictokens_p.h +++ b/src/qmlls/qqmlsemantictokens_p.h @@ -160,14 +160,13 @@ void addModifier(QLspSpecification::SemanticTokenModifiers modifier, int *baseMo bool rangeOverlapsWithSourceLocation(const QQmlJS::SourceLocation &loc, const HighlightsRange &r); QList<QLspSpecification::SemanticTokensEdit> computeDiff(const QList<int> &, const QList<int> &); void updateResultID(QByteArray &resultID); -QList<int> collectTokens(const QQmlJS::Dom::DomItem &item, - const std::optional<HighlightsRange> &range, - HighlightingMode mode = HighlightingMode::Default); HighlightsContainer visitTokens(const QQmlJS::Dom::DomItem &item, const std::optional<HighlightsRange> &range); void addHighlight(HighlightsContainer &out, const QQmlJS::SourceLocation &loc, QmlHighlightKind, QmlHighlightModifiers = QmlHighlightModifier::None); void applyDiffs(HighlightsContainer &highlights, const QList<QQmlLSUtils::Diff> &diffs); +HighlightsContainer shiftHighlights(const HighlightsContainer &cachedHighlights, + const QString &lastValidCode, const QString ¤tCode); } // namespace Utils class HighlightingVisitor diff --git a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp index 7ad4b4447e..533fccfe62 100644 --- a/tests/auto/qmlls/modules/tst_qmlls_modules.cpp +++ b/tests/auto/qmlls/modules/tst_qmlls_modules.cpp @@ -1945,8 +1945,8 @@ void tst_qmlls_modules::semanticHighlightingFull() { QFETCH(QString, filePath); const auto item = fileObject(testFile(filePath)); - QmlHighlighting::HighlightsContainer highlights; - const auto expectedData = QmlHighlighting::Utils::collectTokens(item, std::nullopt); + QmlHighlighting::HighlightsContainer highlights = QmlHighlighting::Utils::visitTokens(item, std::nullopt); + const auto expectedData = QmlHighlighting::Utils::encodeSemanticTokens(highlights); const auto uri = openFile(filePath); QVERIFY(uri); @@ -1988,13 +1988,13 @@ void tst_qmlls_modules::semanticHighlightingRange() QFETCH(QLspSpecification::Range, range); const auto item = fileObject(testFile(filePath)); - QmlHighlighting::HighlightsContainer highlights; const auto qmlFile = item.as<QQmlJS::Dom::QmlFile>(); const auto code = qmlFile->code(); const int startOffset = int(QQmlLSUtils::textOffsetFrom(code, range.start.line, range.end.character)); const int endOffset = int(QQmlLSUtils::textOffsetFrom(code, range.end.line, range.end.character)); - const auto expectedData = QmlHighlighting::Utils::collectTokens( + QmlHighlighting::HighlightsContainer highlights = QmlHighlighting::Utils::visitTokens( item, QmlHighlighting::HighlightsRange{ startOffset, endOffset }); + const auto expectedData = QmlHighlighting::Utils::encodeSemanticTokens(highlights); const auto uri = openFile(filePath); QVERIFY(uri); @@ -2039,8 +2039,12 @@ void tst_qmlls_modules::semanticHighlightingDelta() const auto fileItem = fileObject(testFile(filePath)); const auto deltaFileItem = fileObject(testFile(deltaFilePath)); - auto fullDocumentSemanticTokensData = QmlHighlighting::Utils::collectTokens(fileItem, std::nullopt); - auto editedDocumentSemanticTokensData = QmlHighlighting::Utils::collectTokens(deltaFileItem, std::nullopt); + const auto fullDocumentSemanticTokens = QmlHighlighting::Utils::visitTokens(fileItem, std::nullopt); + const auto editedDocumentSemanticTokens = QmlHighlighting::Utils::visitTokens(deltaFileItem, std::nullopt); + auto fullDocumentSemanticTokensData = + QmlHighlighting::Utils::encodeSemanticTokens(fullDocumentSemanticTokens); + auto editedDocumentSemanticTokensData = + QmlHighlighting::Utils::encodeSemanticTokens(editedDocumentSemanticTokens); const auto expectedEdits = QmlHighlighting::Utils::computeDiff( fullDocumentSemanticTokensData, editedDocumentSemanticTokensData); diff --git a/tests/auto/qmlls/utils/data/highlights/highlightsShift.qml b/tests/auto/qmlls/utils/data/highlights/highlightsShift.qml new file mode 100644 index 0000000000..df4ffc69da --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/highlightsShift.qml @@ -0,0 +1,8 @@ +import QtQuick +Item { + width: 100 + Rectangle { + width: 100 + height: 100 + } +} diff --git a/tests/auto/qmlls/utils/data/highlights/highlightsShift_consoleLog.qml b/tests/auto/qmlls/utils/data/highlights/highlightsShift_consoleLog.qml new file mode 100644 index 0000000000..b717634689 --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/highlightsShift_consoleLog.qml @@ -0,0 +1,8 @@ +import QtQuick + +HasEnumAndAttachedType { + property var enumValue: 1 + Component.onCompleted: { + console.log(enumValue) + } +} diff --git a/tests/auto/qmlls/utils/data/highlights/highlightsShift_withComments.qml b/tests/auto/qmlls/utils/data/highlights/highlightsShift_withComments.qml new file mode 100644 index 0000000000..fddac941f6 --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/highlightsShift_withComments.qml @@ -0,0 +1,11 @@ +import QtQuick + +HasEnumAndAttachedType { + property var enumValue: 1 + Component.onCompleted: { + console.log(enumValue) + } +} + +// This is a comment line +// Another comment line
\ No newline at end of file diff --git a/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_consoleLog_del.qml b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_consoleLog_del.qml new file mode 100644 index 0000000000..63ace4b663 --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_consoleLog_del.qml @@ -0,0 +1,8 @@ +import QtQuick + +HasEnumAndAttachedType { + property var enumValue: 1 + Component.onCompleted: + console.log(enumValue) + } +} diff --git a/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del1.qml b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del1.qml new file mode 100644 index 0000000000..ee722b9ce7 --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del1.qml @@ -0,0 +1,8 @@ +imQuick +Item { + width: 100 + Rectangle { + width: 100 + height: 100 + } +} diff --git a/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del2.qml b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del2.qml new file mode 100644 index 0000000000..c81451e306 --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del2.qml @@ -0,0 +1,8 @@ +import QtQuick +Ite + width: 100 + Rectangle { + width: 100 + height: 100 + } +} diff --git a/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del3.qml b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del3.qml new file mode 100644 index 0000000000..7e93d458d3 --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del3.qml @@ -0,0 +1,8 @@ + QtQuick +Item { + width: 100 + Rectangle { + width: 100 + height: 100 + } +} diff --git a/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del4.qml b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del4.qml new file mode 100644 index 0000000000..8e8e7f0475 --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del4.qml @@ -0,0 +1,6 @@ +import QtQuick +Item { + width: 100 + Rectangle { + +} diff --git a/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del5.qml b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del5.qml new file mode 100644 index 0000000000..9cf4c3804a --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del5.qml @@ -0,0 +1,7 @@ +import QtQuick +Item { + width: 100 + Rectangle +height: 100 + } +} diff --git a/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del6.qml b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del6.qml new file mode 100644 index 0000000000..5437541542 --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_del6.qml @@ -0,0 +1,6 @@ +import QtQuick +Item { + width: 100th: 100 + height: 100 + } +} diff --git a/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_withComments_del.qml b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_withComments_del.qml new file mode 100644 index 0000000000..4bccec2ebe --- /dev/null +++ b/tests/auto/qmlls/utils/data/highlights/invalid/highlightsShift_withComments_del.qml @@ -0,0 +1,5 @@ +import QtQuick + + +// This is a comment line +// Another comment line
\ No newline at end of file diff --git a/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp b/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp index 6dae204bb0..49fe4e8c87 100644 --- a/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp +++ b/tests/auto/qmlls/utils/tst_qmlls_highlighting.cpp @@ -991,28 +991,28 @@ void tst_qmlls_highlighting::computeDiff() } } -void tst_qmlls_highlighting::enumCrash() +static QQmlJS::Dom::DomItem fileObject(const QString &filePath) { using namespace QQmlJS::Dom; - const auto fileObject = [](const QString &filePath) { - QFile f(filePath); - DomItem file; - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) - return file; - QString code = f.readAll(); - - QStringList dirs = { QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) }; - auto envPtr = DomEnvironment::create( - dirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, Extended); - envPtr->loadBuiltins(); - envPtr->loadFile(FileToLoad::fromMemory(envPtr, filePath, code), - [&file](Path, const DomItem &, const DomItem &newIt) { - file = newIt.fileObject(); - }); - envPtr->loadPendingDependencies(); + QFile f(filePath); + DomItem file; + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return file; - }; + QString code = f.readAll(); + + QStringList dirs = { QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) }; + auto envPtr = DomEnvironment::create(dirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, + Extended); + envPtr->loadBuiltins(); + envPtr->loadFile( + FileToLoad::fromMemory(envPtr, filePath, code), + [&file](Path, const DomItem &, const DomItem &newIt) { file = newIt.fileObject(); }); + envPtr->loadPendingDependencies(); + return file; +}; +void tst_qmlls_highlighting::enumCrash() +{ const auto filePath = m_highlightingDataDir + "/enums_qtbug.qml"; const auto fileItem = fileObject(filePath); @@ -1021,4 +1021,253 @@ void tst_qmlls_highlighting::enumCrash() QVERIFY(!highlights.isEmpty()); } +void tst_qmlls_highlighting::shiftHighlights_data() +{ + QTest::addColumn<QmlHighlighting::HighlightsContainer>("lastValidHighlights"); + QTest::addColumn<QString>("lastValidCode"); + QTest::addColumn<QString>("currentCode"); + QTest::addColumn<QmlHighlighting::HighlightsContainer>("expectedHighlights"); + + const auto filePath = m_highlightingDataDir + "/highlightsShift.qml"; + const auto fileItem = fileObject(filePath); + const auto originalHighlights = QmlHighlighting::Utils::visitTokens(fileItem, std::nullopt); + const QString lastValidCode = fileItem.ownerAs<QQmlJS::Dom::QmlFile>()->code(); + + const auto editedCode = [&](const QString &file) -> QString { + const auto editedPath = m_highlightingDataDir + file; + QFile editedFile(editedPath); + if (!editedFile.open(QIODevice::ReadOnly | QIODevice::Text)) + return QString(); + return editedFile.readAll(); + }; + + // delete port Qt from import QtQuick + // Expect highlights are the same as the originals except Item token. + { + const QString currentCode = editedCode("/invalid/highlightsShift_del1.qml"); + + QmlHighlighting::HighlightsContainer expectedHighlights = originalHighlights; + expectedHighlights.remove(0); + expectedHighlights.remove(7); + const auto im = QQmlJS::SourceLocation(0, 2, 1, 1); // 'im' location + expectedHighlights[im.offset] = + HighlightToken(im, QmlHighlightKind::QmlKeyword, QmlHighlightModifier::None); + const auto quick = QQmlJS::SourceLocation(3, 5, 1, 3); // 'Quick' location + expectedHighlights[quick.offset] = + HighlightToken(quick, QmlHighlightKind::QmlImportId, QmlHighlightModifier::None); + + QTest::addRow("modify-line-without-lineshift-from-lhs-rhs") + << originalHighlights << lastValidCode << currentCode << expectedHighlights; + } + // modify Item { into Ite + // Expect highlights are the same as the originals except Item token. + { + const QString currentCode = editedCode("/invalid/highlightsShift_del2.qml"); + + QmlHighlighting::HighlightsContainer expectedHighlights = originalHighlights; + const auto itemNewLoc = QQmlJS::SourceLocation(15, 3, 2, 1); // 'Ite' location + expectedHighlights[itemNewLoc.offset] = + HighlightToken(itemNewLoc, QmlHighlightKind::QmlType, QmlHighlightModifier::None); + + QTest::addRow("modify-line-without-lineshift-from-rhs") + << originalHighlights << lastValidCode << currentCode << expectedHighlights; + } + // delete "import" from import QtQuick + // Expect highlights are the same as the originals QtQuick token should shift. + // and import should be gone. + { + const QString currentCode = editedCode("/invalid/highlightsShift_del3.qml"); + + QmlHighlighting::HighlightsContainer expectedHighlights = originalHighlights; + // remove import QtQuick + expectedHighlights.remove(0); + expectedHighlights.remove(7); + // insert QtQuick + const auto qtquickNewLoc = QQmlJS::SourceLocation(1, 7, 1, 2); + expectedHighlights[qtquickNewLoc.offset] = HighlightToken( + qtquickNewLoc, QmlHighlightKind::QmlImportId, QmlHighlightModifier::None); + + QTest::addRow("modify-line-without-lineshift-from-lhs") + << originalHighlights << lastValidCode << currentCode << expectedHighlights; + } + + // delete inner content of Rectangle, leave it in invalid state + { + const QString currentCode = editedCode("/invalid/highlightsShift_del4.qml"); + + QmlHighlighting::HighlightsContainer expectedHighlights = originalHighlights; + // remove Rectangle content tokens + expectedHighlights.remove(61); // width + expectedHighlights.remove(68); // 100 + expectedHighlights.remove(80); // height + expectedHighlights.remove(88); // 100 + + QTest::addRow("delete-multiline-no-shift") + << originalHighlights << lastValidCode << currentCode << expectedHighlights; + } + // delete width content of Rectangle, height should line shift up + { + const QString currentCode = editedCode("/invalid/highlightsShift_del5.qml"); + + QmlHighlighting::HighlightsContainer expectedHighlights = originalHighlights; + // remove Rectangle content tokens + expectedHighlights.remove(61); // width + expectedHighlights.remove(68); // 100 + + // height should line shift up, and shifts column + auto heightToken = expectedHighlights.take(80); + expectedHighlights.remove(80); + heightToken.loc.startLine = 5; + heightToken.loc.startColumn = 1; + heightToken.loc.offset = 51; + expectedHighlights[heightToken.loc.offset] = heightToken; + + auto numberToken = expectedHighlights.take(88); + expectedHighlights.remove(88); + numberToken.loc.startLine = 5; + numberToken.loc.startColumn = 9; + numberToken.loc.offset = 59; + expectedHighlights[numberToken.loc.offset] = numberToken; + QTest::addRow("delete-multiline-line-column-shift") + << originalHighlights << lastValidCode << currentCode << expectedHighlights; + } + // delete width content of Rectangle, height should line shift up, Rectangle should be erased + // th: 100 part of width should line up and start at the end of upper line + { + const QString currentCode = editedCode("/invalid/highlightsShift_del6.qml"); + + QmlHighlighting::HighlightsContainer expectedHighlights = originalHighlights; + // remove Rectangle content tokens + expectedHighlights.remove(41); // Rectangle + + // width + auto widthToken = expectedHighlights.take(61); + expectedHighlights.remove(61); + widthToken.loc.startLine = 3; + widthToken.loc.startColumn = 15; + widthToken.loc.offset = 36; + widthToken.loc.length = 2; // th part remained + expectedHighlights[widthToken.loc.offset] = widthToken; + // 100 + auto numberToken = expectedHighlights.take(68); + expectedHighlights.remove(68); + numberToken.loc.startLine = 3; + numberToken.loc.startColumn = 19; + numberToken.loc.offset = 40; + expectedHighlights[numberToken.loc.offset] = numberToken; + // height should line shift up + auto heightToken = expectedHighlights.take(80); + expectedHighlights.remove(80); + heightToken.loc.startLine = 4; + heightToken.loc.startColumn = 9; + heightToken.loc.offset = 52; + expectedHighlights[heightToken.loc.offset] = heightToken; + + auto heightNumber = expectedHighlights.take(88); + expectedHighlights.remove(88); + heightNumber.loc.startLine = 4; + heightNumber.loc.startColumn = 17; + heightNumber.loc.offset = 60; + expectedHighlights[heightNumber.loc.offset] = heightNumber; + + QTest::addRow("delete-multiline-line-column-shift-2") + << originalHighlights << lastValidCode << currentCode << expectedHighlights; + } + { // manual testing find: console.log(element) + const auto filePath = m_highlightingDataDir + "/highlightsShift_consoleLog.qml"; + const auto fileItem = fileObject(filePath); + const auto highlights = QmlHighlighting::Utils::visitTokens(fileItem, std::nullopt); + const QString validCode = fileItem.ownerAs<QQmlJS::Dom::QmlFile>()->code(); + const QString currentCode = editedCode("/invalid/highlightsShift_consoleLog_del.qml"); + + QmlHighlighting::HighlightsContainer expectedHighlights = highlights; + // console + auto consoleToken = expectedHighlights.take(108); + expectedHighlights.remove(108); + consoleToken.loc.offset = 107; + expectedHighlights[consoleToken.loc.offset] = consoleToken; + // log + auto logToken = expectedHighlights.take(116); + expectedHighlights.remove(116); + logToken.loc.offset = 115; + expectedHighlights[logToken.loc.offset] = logToken; + // enumValue + auto enumValueToken = expectedHighlights.take(120); + expectedHighlights.remove(120); + enumValueToken.loc.offset = 119; + expectedHighlights[enumValueToken.loc.offset] = enumValueToken; + + QTest::addRow("delete-shift-console-log") + << highlights << validCode << currentCode << expectedHighlights; + } + { // manual testing find: comments after deletion + const auto filePath = m_highlightingDataDir + "/highlightsShift_withComments.qml"; + const auto fileItem = fileObject(filePath); + const auto highlights = QmlHighlighting::Utils::visitTokens(fileItem, std::nullopt); + const QString validCode = fileItem.ownerAs<QQmlJS::Dom::QmlFile>()->code(); + const QString currentCode = editedCode("/invalid/highlightsShift_withComments_del.qml"); + + QmlHighlighting::HighlightsContainer expectedHighlights; + // import + expectedHighlights[0] = highlights[0]; + // QtQuick + expectedHighlights[7] = highlights[7]; + // commentline 1 + auto commentLine1 = QmlHighlighting::HighlightToken( + QQmlJS::SourceLocation(17, 25, 4, 1), + QmlHighlightKind::Comment, QmlHighlightModifier::None); + expectedHighlights[commentLine1.loc.offset] = commentLine1; + // commentline 2 + auto commentLine2 = QmlHighlighting::HighlightToken( + QQmlJS::SourceLocation(43, 23, 5, 1), + QmlHighlightKind::Comment, QmlHighlightModifier::None); + expectedHighlights[commentLine2.loc.offset] = commentLine2; + + QTest::addRow("delete-shift-comments-after-deletion") + << highlights << validCode << currentCode << expectedHighlights; + } +} + +void tst_qmlls_highlighting::shiftHighlights() +{ + QFETCH(QmlHighlighting::HighlightsContainer, lastValidHighlights); + QFETCH(QString, lastValidCode); + QFETCH(QString, currentCode); + QFETCH(QmlHighlighting::HighlightsContainer, expectedHighlights); + + HighlightsContainer actualHighlights = + QmlHighlighting::Utils::shiftHighlights(lastValidHighlights, lastValidCode, currentCode); + [&] { + const auto actualEncoded = QmlHighlighting::Utils::encodeSemanticTokens( + actualHighlights, QmlHighlighting::HighlightingMode::Default); + const auto expectedEncoded = QmlHighlighting::Utils::encodeSemanticTokens( + expectedHighlights, QmlHighlighting::HighlightingMode::Default); + QCOMPARE(actualEncoded, expectedEncoded); + }(); + + if (QTest::currentTestFailed()) { + auto [actual, expected] = + std::mismatch(actualHighlights.begin(), actualHighlights.end(), + expectedHighlights.begin(), expectedHighlights.end()); + + if (actual != actualHighlights.end() && expected != expectedHighlights.end()) { + + const auto msg = [](const QString &title, int actualValue, int expectedValue) { + return QString("%1 : [Actual %2, Expected %3]") + .arg(title) + .arg(actualValue) + .arg(expectedValue); + }; + qDebug() << msg("Offset", actual->loc.offset, expected->loc.offset); + qDebug() << msg("Length", actual->loc.length, expected->loc.length); + qDebug() << msg("StartLine", actual->loc.startLine, expected->loc.startLine); + qDebug() << msg("StartColumn", actual->loc.startColumn, expected->loc.startColumn); + qDebug() << msg("Kind", static_cast<int>(actual->kind), + static_cast<int>(expected->kind)); + qDebug() << msg("Modifiers", static_cast<int>(actual->modifiers), + static_cast<int>(expected->modifiers)); + } + } +} + QTEST_MAIN(tst_qmlls_highlighting) diff --git a/tests/auto/qmlls/utils/tst_qmlls_highlighting.h b/tests/auto/qmlls/utils/tst_qmlls_highlighting.h index 42a0344a65..f7dc03ac00 100644 --- a/tests/auto/qmlls/utils/tst_qmlls_highlighting.h +++ b/tests/auto/qmlls/utils/tst_qmlls_highlighting.h @@ -32,6 +32,8 @@ private slots: void computeDiff(); void enumCrash(); + void shiftHighlights_data(); + void shiftHighlights(); private: QString m_highlightingDataDir; |
