diff --git a/Desktop/components/JASP/Widgets/MainPage.qml b/Desktop/components/JASP/Widgets/MainPage.qml index 18ec3b81b7..7b10daa49b 100644 --- a/Desktop/components/JASP/Widgets/MainPage.qml +++ b/Desktop/components/JASP/Widgets/MainPage.qml @@ -535,7 +535,7 @@ Item "functionCall" : functionCall }; - customMenu.toggle(resultsView, props, (optionsJSON['rXright'] + 10) * preferencesModel.uiScale, optionsJSON['rY'] * preferencesModel.uiScale); + customMenu.toggle(resultsView, props, (optionsJSON['rXright'] + 10 ) * preferencesModel.uiScale, optionsJSON['rY'] ); customMenu.scrollOri = resultsView.scrollPosition; customMenu.menuScroll.x = Qt.binding(function() { return -1 * (resultsView.scrollPosition.x - customMenu.scrollOri.x) / resultsView.zoomFactor; }); diff --git a/Desktop/components/JASP/Widgets/PlotEditor/PlotEditTabHead.qml b/Desktop/components/JASP/Widgets/PlotEditor/PlotEditTabHead.qml new file mode 100644 index 0000000000..afc2b468b0 --- /dev/null +++ b/Desktop/components/JASP/Widgets/PlotEditor/PlotEditTabHead.qml @@ -0,0 +1,60 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import JASP.Widgets as JASPW +import JASP.Theme +import JASP.Controls as JASPC +import JASP.PlotEditor + +TabButton +{ + id: refTab + height: axes.tabBarHeight + 2 + clip: true + + property string buttonText: "Fill me" + + background: Rectangle + { + color: refTab.checked ? jaspTheme.uiBackground : jaspTheme.grayLighter + radius: axes.tabButtonRadius + border.width: 1 + border.color: refTab.checked ? jaspTheme.uiBorder : jaspTheme.borderColor + height: axes.tabBarHeight + axes.tabButtonRadius + + + Rectangle + { + color: jaspTheme.uiBorder + height: 1 + visible: !refTab.checked + anchors + { + left: parent.left + right: parent.right + top: parent.top + topMargin: axes.tabBarHeight + 1 + } + } + } + + contentItem: Text + { + // The bottom of buttons are hidden to remove their bottom line with the radius + // So the text has to be moved higher from the horizontal middle line. + topPadding: -axes.tabButtonRadius * 3/4 + font: jaspTheme.font + color: jaspTheme.black + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + opacity: refTab.checked ? 1 : .6 + text: buttonText + } + + MouseArea + { + anchors.fill : parent + cursorShape : refTab.checked ? Qt.ArrowCursor : Qt.PointingHandCursor + acceptedButtons : Qt.NoButton + } +} diff --git a/Desktop/components/JASP/Widgets/PlotEditor/PlotEditingAxis.qml b/Desktop/components/JASP/Widgets/PlotEditor/PlotEditingAxis.qml index 98f5e24cc4..049119db84 100644 --- a/Desktop/components/JASP/Widgets/PlotEditor/PlotEditingAxis.qml +++ b/Desktop/components/JASP/Widgets/PlotEditor/PlotEditingAxis.qml @@ -25,7 +25,7 @@ import JASP */ -Column +ColumnLayout { id: axis spacing: jaspTheme.columnGroupSpacing @@ -37,6 +37,8 @@ Column label : qsTr("Show title") checked : axisModel.titleType !== AxisModel.TitleNull onClicked : axisModel.titleType = (checked ? AxisModel.TitleCharacter : AxisModel.TitleNull) + + Layout.fillWidth: true JASPC.TextField { @@ -56,6 +58,8 @@ Column checked : axisModel.breaksType !== AxisModel.BreaksNull onClicked : axisModel.breaksType = (checked ? lastBreakType : AxisModel.BreaksNull) columns : 1 + + Layout.fillWidth: true property int lastBreakType: AxisModel.BreaksRange @@ -151,6 +155,7 @@ Column { title : qsTr("Advanced") columns : 1 + Layout.fillWidth: true JASPC.CheckBox { diff --git a/Desktop/components/JASP/Widgets/PlotEditor/PlotEditingReferenceLines.qml b/Desktop/components/JASP/Widgets/PlotEditor/PlotEditingReferenceLines.qml new file mode 100644 index 0000000000..846692de83 --- /dev/null +++ b/Desktop/components/JASP/Widgets/PlotEditor/PlotEditingReferenceLines.qml @@ -0,0 +1,144 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import JASP.Widgets as JASPW +import JASP.Theme +import JASP.Controls as JASPC +import JASP.PlotEditor +import JASP + + +JASPW.JASPDataView +{ + id: jaspDataView + model: plotEditorModel.references + + onWidthChanged: plotEditorModel.references.viewWidth = width + + rowNumberDelegate: null + + itemDelegate: Component { Loader + { + + property int rowIdx: rowIndex + property int columnIdx: columnIndex + property string modelText: itemText + property var modelData: itemData + property bool modelEnabled: itemEnabled + + sourceComponent: columnIndex === 0 ? typeSelector : columnIndex == 4 ? eraseButton : textView; + }} + + + editDelegate: Component { Loader + { + + property int rowIdx: rowIndex + property int columnIdx: columnIndex + property string modelText: itemText + property var modelData: itemData + //property bool modelEnabled: itemEnabled + + sourceComponent: textEdit + }} + + Component + { + id: textView + + Text + { + text: modelText == "" ? "..." : modelText; + font: jaspTheme.font; + color: enabled ? jaspTheme.textEnabled : jaspTheme.textDisabled; + enabled: modelEnabled + visible: enabled + MouseArea + { + anchors.fill: parent; + onClicked: jaspDataView.view.edit(rowIdx, columnIdx) + } + + // opacity: rowIdx < jaspDataView.view.rowCount - 1 ? 1.0 : 0.5 + + } + } + + Component + { + id: textEdit + + TextInput + { + id: editItem + text: modelText + color: enabled ? jaspTheme.textEnabled : jaspTheme.textDisabled; + font: jaspTheme.font + focus: true + clip: true + //onTextEdited: jaspDataView.view.model.setData(jaspDataView.view.model.index(rowIdx, columnIdx), text) + onEditingFinished: {saveEdit(); jaspDataView.view.forceActiveFocus(); } + Keys.onReturnPressed: {saveEdit(); jaspDataView.view.forceActiveFocus(); } + Component.onCompleted: forceActiveFocus() + //enabled: modelEnabled + onActiveFocusChanged: + { + if(!activeFocus) + { + + text = Qt.binding(function() {return modelText;}); + } + } + + function saveEdit() + { + jaspDataView.view.commitEdit(rowIdx, columnIdx, text); + jaspDataView.view.clearEdit(); + } + + //opacity: rowIdx < jaspDataView.view.rowCount - 1 ? 1.0 : 0.5 + } + } + + ListModel + { + id: typeModel + + ListElement { value: 0; name: qsTr("Point") } + ListElement { value: 1; name: qsTr("Horizontal Line") } + ListElement { value: 2; name: qsTr("Vertical Line") } + } + + Component + { + id: typeSelector + + JASPC.DropDown + { + fieldWidth: width + source: typeModel + currentIndex: modelData + onValueChanged: + { + if(modelData != currentIndex) + jaspDataView.view.model.setData(jaspDataView.view.model.index(rowIdx, columnIdx), currentIndex); + } + + //opacity: rowIdx < jaspDataView.view.rowCount - 1 ? 1.0 : 0.5 + } + } + + Component + { + id: eraseButton + + + JASPC.RectangularButton + { + text: "X" + onClicked: model.setData(model.index(rowIdx, columnIdx), true); + //visible: rowIdx < jaspDataView.view.rowCount - 1 + } + } + +} diff --git a/Desktop/components/JASP/Widgets/PlotEditor/PlotEditor.qml b/Desktop/components/JASP/Widgets/PlotEditor/PlotEditor.qml index a505a7ef41..b65aeb6c0a 100644 --- a/Desktop/components/JASP/Widgets/PlotEditor/PlotEditor.qml +++ b/Desktop/components/JASP/Widgets/PlotEditor/PlotEditor.qml @@ -163,10 +163,10 @@ Popup Item { - id: axes + id: axes SplitView.preferredWidth: parent.width * .3 - SplitView.minimumWidth: parent.width * .3 - SplitView.maximumWidth: parent.width * .9 + SplitView.minimumWidth: parent.width * .3 + SplitView.maximumWidth: parent.width * .9 property real tabBarHeight: 28 * preferencesModel.uiScale property real tabButtonRadius: 5 * preferencesModel.uiScale @@ -185,165 +185,100 @@ Popup border.width: 1 border.color: jaspTheme.uiBorder color: "transparent" + z: -1 + } + + + TabBar + { + id: tabbar + contentHeight: axes.tabBarHeight + width: axes.axeTitles.length * axes.tabButtonWidth + background: Rectangle { color: jaspTheme.uiBackground } // Per default the background is white + + Repeater + { + model: axes.axeTitles + PlotEditTabHead + { + buttonText: modelData + } + } + + PlotEditTabHead + { + buttonText: qsTr("References") + } + + Component.onCompleted: setCurrentIndex(0); + } + + JASPC.MenuButton + { + id: helpButton + iconSource: jaspTheme.iconPath + "info-button.png" + width: height + radius: height + onClicked: helpModel.showOrTogglePage("other/plotediting"); + toolTip: qsTr("Open Documentation") + anchors + { + top: tabbar.top + left: tabbar.right + bottom: tabbar.bottom + leftMargin: 4 * preferencesModel.uiScale + } + } + + MouseArea + { + id: focusCatcher //Because then people can click away from the axistableview or something + onPressed: { forceActiveFocus(); mouse.accepted = false; } + anchors.fill: axesFlickable + z: -100 + } + + JASPC.JASPScrollBar + { + id: axesScrollbar + flickable: axesFlickable + vertical: true } Flickable { id: axesFlickable - anchors.fill: parent + anchors + { + top: tabbar.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } clip: true - contentHeight: flickChild.height - contentWidth: flickChild.width flickableDirection: Flickable.VerticalFlick onFlickStarted: forceActiveFocus(); - Item + StackLayout { - id: flickChild - width: axesFlickable.width - height: stack.y + stack.height + jaspTheme.generalAnchorMargin - - MouseArea - { - id: focusCatcher //Because then people can click away from the axistableview or something - onPressed: { forceActiveFocus(); mouse.accepted = false; } - anchors - { - top: parent.top - left: parent.left - right: parent.right - } - height: Math.max(flickChild.height, axesFlickable.height) - z: -100 - } - - TabBar - { - id: tabbar - contentHeight: axes.tabBarHeight + axes.tabButtonRadius - width: axes.axeTitles.length * axes.tabButtonWidth - - background: Rectangle { color: jaspTheme.uiBackground } // Per default the background is white - - Repeater - { - model: axes.axeTitles - TabButton - { - height: tabbar.height - background: Rectangle - { - color: checked ? jaspTheme.uiBackground : jaspTheme.grayLighter - radius: axes.tabButtonRadius - border.width: 1 - border.color: checked ? jaspTheme.uiBorder : jaspTheme.borderColor - } - - contentItem: Text - { - // The bottom of buttons are hidden to remove their bottom line with the radius - // So the text has to be moved higher from the horizontal middle line. - topPadding: -axes.tabButtonRadius * 3/4 - text: modelData - font: jaspTheme.font - color: jaspTheme.black - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - opacity: checked ? 1 : .6 - } - - MouseArea - { - anchors.fill : parent - cursorShape : checked ? Qt.ArrowCursor : Qt.PointingHandCursor - acceptedButtons : Qt.NoButton - } - } - } - } - - Rectangle + id: stack + currentIndex: tabbar.currentIndex + width: axesFlickable.width - (2*jaspTheme.generalAnchorMargin + axesScrollbar.width) + y: jaspTheme.generalAnchorMargin + x: jaspTheme.generalAnchorMargin + + Repeater { - // This hides the bottom border of the buttons (with their radius) - id : roundingHider - width : parent.width - height : axes.tabButtonRadius + 1 - anchors + model: axes.axeModels + PlotEditingAxis { - left: parent.left - right: tabbar.right - top: parent.top - topMargin: axes.tabBarHeight - } - color: jaspTheme.uiBackground - - Rectangle - { - // The Tabbar removes the left border. Redraw it. - anchors.left: parent.left - anchors.top: parent.top - anchors.bottom: parent.bottom - width: 1 - color: jaspTheme.uiBorder - } - } - - Rectangle - { - // Redraw a line below the unchecked tab - anchors - { - top: roundingHider.top - left: parent.left - leftMargin: tabbar.currentIndex === 0 ? axes.tabButtonWidth - 1 : 0 - right: tabbar.right - rightMargin: tabbar.currentIndex === 0 ? 0 : axes.tabButtonWidth - 1 - } - height: 1 - color: jaspTheme.uiBorder - } - - StackLayout - { - id: stack - anchors - { - top : tabbar.bottom - left : parent.left - right : parent.right - margins : jaspTheme.generalAnchorMargin - } - currentIndex: tabbar.currentIndex - - Repeater - { - model: axes.axeModels - PlotEditingAxis - { - axisModel: modelData - width: flickChild.width - } - } - } - - JASPC.MenuButton - { - id: helpButton - iconSource: jaspTheme.iconPath + "info-button.png" - width: height - radius: height - onClicked: helpModel.showOrTogglePage("other/plotediting"); - toolTip: qsTr("Open Documentation") - anchors - { - top: tabbar.top - left: tabbar.right - bottom: roundingHider.top - leftMargin: 4 * preferencesModel.uiScale + axisModel: modelData } } + + PlotEditingReferenceLines {} } } @@ -388,12 +323,7 @@ Popup onClicked: plotEditorModel.undoSomething() } */ - JASPC.JASPScrollBar - { - id: axesScrollbar - flickable: axesFlickable - vertical: true - } + } Item diff --git a/Desktop/data/expanddataproxymodel.cpp b/Desktop/data/expanddataproxymodel.cpp index 32610d1183..776f3b086d 100644 --- a/Desktop/data/expanddataproxymodel.cpp +++ b/Desktop/data/expanddataproxymodel.cpp @@ -277,10 +277,20 @@ void ExpandDataProxyModel::resize(int row, int col, bool onlyExpand, const QStri _undoStack->endMacro(); } +bool ExpandDataProxyModel::useUndoStack() const +{ + return dynamic_cast(_sourceModel); +} + bool ExpandDataProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!_sourceModel || index.row() < 0 || index.column() < 0) return false; + + if(!useUndoStack()) + { + return _sourceModel->setData(_sourceModel->index(index.row(), index.column()), value, role); + } resize(index.row(), index.column()); _undoStack->endMacro(new SetDataCommand(_sourceModel, index.row(), index.column(), value, role)); diff --git a/Desktop/data/expanddataproxymodel.h b/Desktop/data/expanddataproxymodel.h index 660ca89416..8d269c21b4 100644 --- a/Desktop/data/expanddataproxymodel.h +++ b/Desktop/data/expanddataproxymodel.h @@ -48,6 +48,7 @@ class ExpandDataProxyModel : public QAbstractItemModel QString undoText() { return _undoStack->undoText(); } QString redoText() { return _undoStack->redoText(); } void resize(int row, int col, bool onlyExpand = true, const QString& undoText = QString()); + bool useUndoStack() const; signals: void undoChanged(); diff --git a/Desktop/mainwindow.cpp b/Desktop/mainwindow.cpp index a0789f560d..77d122b95e 100644 --- a/Desktop/mainwindow.cpp +++ b/Desktop/mainwindow.cpp @@ -426,6 +426,7 @@ void MainWindow::makeConnections() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); connect(_computedColumnsModel, &ComputedColumnModel::sendComputeCode, _engineSync, &EngineSync::computeColumn, Qt::QueuedConnection); connect(_computedColumnsModel, &ComputedColumnModel::dataColumnAdded, _fileMenu, &FileMenu::dataColumnAdded ); diff --git a/Desktop/results/ploteditormodel.cpp b/Desktop/results/ploteditormodel.cpp index c6384bc4d8..b2ffabf53b 100644 --- a/Desktop/results/ploteditormodel.cpp +++ b/Desktop/results/ploteditormodel.cpp @@ -15,15 +15,18 @@ int PlotEditorModel::_editRequest = 0; PlotEditorModel::PlotEditorModel() : QObject(Analyses::analyses()) { - _xAxis = new AxisModel(this, true); - _yAxis = new AxisModel(this, true); - _currentAxis = _xAxis; - _ppi = PreferencesModel::prefs()->plotPPI(); - - connect(_xAxis, &AxisModel::somethingChanged, this, &PlotEditorModel::somethingChanged); - connect(_yAxis, &AxisModel::somethingChanged, this, &PlotEditorModel::somethingChanged); - connect(_xAxis, &AxisModel::addToUndoStack, this, &PlotEditorModel::addToUndoStack); - connect(_yAxis, &AxisModel::addToUndoStack, this, &PlotEditorModel::addToUndoStack); + _xAxis = new AxisModel(this, true); + _yAxis = new AxisModel(this, true); + _currentAxis = _xAxis; + _ppi = PreferencesModel::prefs()->plotPPI(); + _references = new References(this); + + connect(_xAxis, &AxisModel::somethingChanged, this, &PlotEditorModel::somethingChanged); + connect(_yAxis, &AxisModel::somethingChanged, this, &PlotEditorModel::somethingChanged); + connect(_references, &References::somethingChanged, this, &PlotEditorModel::somethingChanged); + connect(_references, &References::addToUndoStack, this, &PlotEditorModel::addToUndoStack); + connect(_xAxis, &AxisModel::addToUndoStack, this, &PlotEditorModel::addToUndoStack); + connect(_yAxis, &AxisModel::addToUndoStack, this, &PlotEditorModel::addToUndoStack); } @@ -79,13 +82,9 @@ void PlotEditorModel::setup() return; } - Json::Value xAxis = editOptions.get( "xAxis", Json::objectValue), - yAxis = editOptions.get( "yAxis", Json::objectValue); - - _xAxis->setAxisData(xAxis); - _yAxis->setAxisData(yAxis); - - //_coordinates.loadCoordinates(editOptions.get("coordinates", Json::objectValue)); // To Do Vincent Pedata: is this the right json object? + _xAxis ->setAxisData( editOptions.get("xAxis", Json::objectValue)); + _yAxis ->setAxisData( editOptions.get("yAxis", Json::objectValue)); + _references ->fromJson( editOptions.get("references", Json::arrayValue)); _originalImgOps = _imgOptions = generateImgOptions(); } @@ -128,8 +127,9 @@ void PlotEditorModel::reset() Json::Value PlotEditorModel::generateImgOptions() const { Json::Value newOptions = _imgOptions; - newOptions["editOptions"]["xAxis"] = _xAxis->getAxisData(); - newOptions["editOptions"]["yAxis"] = _yAxis->getAxisData(); + newOptions["editOptions"]["xAxis"] = _xAxis->getAxisData(); + newOptions["editOptions"]["yAxis"] = _yAxis->getAxisData(); + newOptions["editOptions"]["references"] = _references->toJson(); newOptions["name"] = name().toStdString(); newOptions["data"] = data().toStdString(); @@ -370,4 +370,9 @@ void PlotEditorModel::setLoading(bool loading) emit loadingChanged(_loading); } +References *PlotEditorModel::references() const +{ + return _references; +} + } diff --git a/Desktop/results/ploteditormodel.h b/Desktop/results/ploteditormodel.h index 97e7efecdc..f8641a0a00 100644 --- a/Desktop/results/ploteditormodel.h +++ b/Desktop/results/ploteditormodel.h @@ -2,11 +2,12 @@ #define PLOTEDITORMODEL_H #include +#include #include #include #include "ploteditoraxismodel.h" #include "ploteditorcoordinates.h" -#include +#include "ploteditorreferencelines.h" class Analyses; class Analysis; @@ -38,6 +39,7 @@ class PlotEditorModel : public QObject Q_PROPERTY(bool redoEnabled READ redoEnabled NOTIFY unOrRedoEnabledChanged ) Q_PROPERTY(AxisModel * currentAxis READ currentAxis NOTIFY currentAxisChanged ) Q_PROPERTY(AxisType axisType READ axisType WRITE setAxisType NOTIFY axisTypeChanged ) + Q_PROPERTY(References * references READ references NOTIFY referencesChanged ) public: explicit PlotEditorModel(); @@ -72,6 +74,8 @@ class PlotEditorModel : public QObject void setBlockChanges(bool change) { _blockChanges = change; } bool blockChanges() const { return _blockChanges; } + References *references() const; + signals: void visibleChanged( bool visible ); void nameChanged( QString name ); @@ -91,6 +95,8 @@ class PlotEditorModel : public QObject void currentAxisChanged( AxisModel * currentAxis); void axisTypeChanged( AxisType axisType); + void referencesChanged(); + public slots: void showPlotEditor(int id, QString options); void updateOptions(Analysis* analysis); @@ -103,9 +109,9 @@ public slots: void setHeight( int height ); void setLoading( bool loading ); - void resetDefaults(); - void cancelPlot(); - void savePlot() const; + void resetDefaults(); + void cancelPlot(); + void savePlot() const; void setAxisType( const AxisType axisType ); void somethingChanged(); @@ -126,34 +132,36 @@ public slots: void updatePlot(Json::Value& imageOptions); private: - Analysis * _analysis = nullptr; - AxisModel * _xAxis = nullptr, - * _yAxis = nullptr, - * _currentAxis = nullptr; + Analysis * _analysis = nullptr; + AxisModel * _xAxis = nullptr, + * _yAxis = nullptr, + * _currentAxis = nullptr; + References * _references = nullptr; //Coordinates _coordinates; - Json::Value _imgOptions = Json::nullValue, - _originalImgOps = Json::nullValue; - std::map _editedImgsMap; - QString _name, - _data, - _title; - bool _visible = false, - _goBlank = false, - _loading = false, - _validOptions = false, - _blockChanges = false; - int _width, - _height; - double _ppi; - - static int _editRequest; + Json::Value _imgOptions = Json::nullValue, + _originalImgOps = Json::nullValue; + std::map _editedImgsMap; + QString _name, + _data, + _title; + bool _visible = false, + _goBlank = false, + _loading = false, + _validOptions = false, + _blockChanges = false; + int _width, + _height; + double _ppi; + + static int _editRequest; std::stack _undo, _redo; - AxisType _axisType = AxisType::Xaxis; + AxisType _axisType = AxisType::Xaxis; + }; } diff --git a/Desktop/results/ploteditorreferencelines.cpp b/Desktop/results/ploteditorreferencelines.cpp new file mode 100644 index 0000000000..2c22c5d52c --- /dev/null +++ b/Desktop/results/ploteditorreferencelines.cpp @@ -0,0 +1,272 @@ +#include "ploteditorreferencelines.h" +#include "data/datasetpackageenums.h" +#include "utilities/qutils.h" +#include "ploteditormodel.h" + +namespace PlotEditor +{ + +References::References(PlotEditorModel * model) + : QAbstractTableModel{model}, _model(model) +{ + +} + +int PlotEditor::References::rowCount(const QModelIndex &parent) const +{ + return _refs.size() + 1; +} + +int PlotEditor::References::columnCount(const QModelIndex &parent) const +{ + return 5; +} + +// I hate the following and fixes for this kind of shenanigans are in a different branch but for now just do this: +QHash PlotEditor::References::roleNames() const +{ + static bool set = false; + static QHash roles = QAbstractItemModel::roleNames (); + + if(!set) + { + for(const auto & enumString : dataPkgRolesToStringMap()) + roles[int(enumString.first)] = tq(enumString.second).toUtf8(); + + set = true; + } + + return roles; +} + +bool References::indexDisabled(const QModelIndex &index) const +{ + if(index.row() >= rowCount()) + return false; + + if(index.row() >= _refs.size() || _refs[index.row()].point) + return false; + + if(_refs[index.row()].horizontal && index.column() == 2) + return true; + + if(!_refs[index.row()].horizontal && index.column() == 3) + return true; + + return false; +} + +Qt::ItemFlags References::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index) | Qt::ItemIsEditable; + if(indexDisabled(index)) + flags &= ~Qt::ItemIsEnabled; + return flags; +} + +QVariant PlotEditor::References::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Vertical) + return QVariant(); + + switch(role) + { + default: + switch(section) + { + case 0: return tr("Type"); + case 1: return tr("Text"); + case 2: return tr("Horizontal"); + case 3: return tr("Vertical"); + case 4: return tr("Remove"); + }; + break; + + case int(dataPkgRoles::maxColString): + case int(dataPkgRoles::maxRowHeaderString): + case int(dataPkgRoles::maxColumnHeaderString): + return QVariant(); + + case int(dataPkgRoles::columnWidthFallback): + return _widths.size() > section ? _widths[section] : 200; + } + + return QVariant(); +} + +bool References::insertRows(int rows, int count, const QModelIndex &parent) +{ + emit addToUndoStack(); + rows = std::min(rows, int(_refs.size())); + beginInsertRows(QModelIndex(), rows, rows+count-1); + for(int c=0; c _refs.size() || index.column() < 0 || index.column() >= columnCount() ) + return QVariant(); + + if(index.row() == _refs.size()) //Special "add a row"-row + switch(index.column()) + { + case 0: return ReferenceType::Point; + default: return ""; + case 2: return 0; + case 3: return 0; + } + + const Reference & ref = _refs[index.row()]; + + switch(index.column()) + { + case 0: return ref.point ? ReferenceType::Point : ref.horizontal ? ReferenceType::LineHorizontal : ReferenceType::LineVertical; + case 1: return ref.text; + case 2: return ref.x; + case 3: return ref.y; + } + + return QVariant(); +} + +bool References::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if(index.row() < 0 || index.row() > _refs.size() || index.column() < 0 || index.column() >= columnCount() ) + return false; + + if(index.row() == _refs.size()) + { + if(index.column() == 0 && ReferenceType(value.toInt()) == ReferenceType::Point) + return false; + else + insertRows(index.row(), 1); + } + + emit addToUndoStack(); + + beginResetModel(); + Reference & ref = _refs[index.row()]; + + switch(index.column()) + { + default: + return false; + + case 0: + { + ref.point = ReferenceType(value.toInt()) == ReferenceType::Point; + ref.horizontal = ReferenceType(value.toInt()) == ReferenceType::LineHorizontal; + break; + } + case 1: + { + ref.text = value.toString(); + break; + } + case 2: + { + ref.x = value.toDouble(); + break; + } + case 3: + { + ref.y = value.toDouble(); + break; + } + case 4: + removeRows(index.row(), 1); + break; + } + endResetModel(); + //emit dataChanged(References::index(index.row(), 0), References::index(index.row(), columnCount())); + emit somethingChanged(); + return true; + +} + +Json::Value PlotEditor::References::toJson() const +{ + Json::Value val = Json::arrayValue; + + for(const Reference & l : _refs) + { + Json::Value obj; + obj["text"] = fq(l.text); + obj["horizontal"] = l.horizontal; + obj["point"] = l.point; + obj["x"] = l.x; + obj["y"] = l.y; + val.append(obj); + } + + return val; +} + +void PlotEditor::References::fromJson(const Json::Value &json) +{ + emit addToUndoStack(); + beginResetModel(); + _refs.clear(); + if(json.type() == Json::arrayValue) + for(const Json::Value & r : json) + _refs.push_back(Reference{ + tq(r.get("text", "???") .asString()), + r.get("horizontal", true) .asBool(), + r.get("point", false) .asBool(), + r.get("x", 0.) .asDouble(), + r.get("y", 0.) .asDouble() + }); + endResetModel(); + emit somethingChanged(); +} + +void References::setColWidth(int index, int width) +{ + if(_widths.size() > index && _widths[index] == width) + return; + + if(_widths.size() <= index) + _widths.resize(index+1); + _widths[index] = width; +} + +int References::viewWidth() const +{ + return _viewWidth; +} + +void References::setViewWidth(int newViewWidth) +{ + if (_viewWidth == newViewWidth) + return; + _viewWidth = newViewWidth; + emit viewWidthChanged(); + + int fiver = newViewWidth / 5; + + beginResetModel(); + for(int i=0; i<5; i++) + setColWidth(i, fiver); + endResetModel(); +} + +} diff --git a/Desktop/results/ploteditorreferencelines.h b/Desktop/results/ploteditorreferencelines.h new file mode 100644 index 0000000000..66beef2f58 --- /dev/null +++ b/Desktop/results/ploteditorreferencelines.h @@ -0,0 +1,78 @@ +#ifndef PLOTEDITORREFERENCELINES_H +#define PLOTEDITORREFERENCELINES_H + +#include +#include +#include +#include "utils.h" + +namespace PlotEditor +{ + +class PlotEditorModel; + + +class References : public QAbstractTableModel +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int viewWidth READ viewWidth WRITE setViewWidth NOTIFY viewWidthChanged) + +public: + explicit References(PlotEditorModel * model); + + enum ReferenceType { Point, LineHorizontal, LineVertical}; + Q_ENUM(ReferenceType) + + + struct Reference + { + QString text = ""; + bool horizontal = false, + point = true; + double x = 0, + y; + }; + + int rowCount( const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData( const QModelIndex &index, const QVariant &value, int role) override; + QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; + bool insertRows(int rows, int count, const QModelIndex &parent = QModelIndex()) override; + bool removeRows(int rows, int count, const QModelIndex &parent = QModelIndex()) override; + QHash roleNames() const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + + + Json::Value toJson() const; + void fromJson(const Json::Value & json); + + int viewWidth() const; + void setViewWidth(int newViewWidth); + + +public slots: + void setColWidth(int index, int width); + +protected: + bool indexDisabled(const QModelIndex &index) const; + +signals: + void somethingChanged(); + void addToUndoStack(); + + + void viewWidthChanged(); + +protected: + PlotEditorModel * _model; + std::vector _refs; + intvec _widths; + int _viewWidth; + +}; + +} + +#endif // PLOTEDITORREFERENCELINES_H diff --git a/QMLComponents/components/JASP/Controls/DropDown.qml b/QMLComponents/components/JASP/Controls/DropDown.qml index de1804a11d..78a63b8652 100644 --- a/QMLComponents/components/JASP/Controls/DropDown.qml +++ b/QMLComponents/components/JASP/Controls/DropDown.qml @@ -6,6 +6,8 @@ import JASP.Controls ComboBoxBase { id: comboBox + height: implicitHeight + width: implicitWidth implicitHeight: control.height + ((controlLabel.visible && setLabelAbove) ? rectangleLabel.height : 0) implicitWidth: controlLabel.visible ? (setLabelAbove ? Math.max(control.width, rectangleLabel.width) : (rectangleLabel.width + jaspTheme.labelSpacing + control.width)) diff --git a/QMLComponents/datasetviewbase.cpp b/QMLComponents/datasetviewbase.cpp index ff8aee27f5..86dce277c1 100644 --- a/QMLComponents/datasetviewbase.cpp +++ b/QMLComponents/datasetviewbase.cpp @@ -201,6 +201,7 @@ void DataSetViewBase::modelDataChanged(const QModelIndex &topLeft, const QModelI { //Changes here should be considered also for DataSetViewBase::setStyleDataItem: context->setContextProperty("itemText", _model->data(modelIndex)); + context->setContextProperty("itemEnabled", bool(_model->flags(modelIndex) & Qt::ItemIsEnabled)); context->setContextProperty("itemTextEdit", _model->data(modelIndex, getRole("noSepaDisplay"))); context->setContextProperty("itemShadowText", _model->data(modelIndex, getRole("shadowDisplay"))); context->setContextProperty("itemLabel", _model->data(modelIndex, getRole("label"))); @@ -1475,7 +1476,7 @@ void DataSetViewBase::onDataModeChanged(bool dataMode) void DataSetViewBase::commitLastEdit() { Log::log() << "Commit last edit" << std::endl; - if(_prevEditRow != -1 && _prevEditCol != -1 && _editItemContextual && _editItemContextual->item) + if(_prevEditRow != -1 && _prevEditCol != -1 && _editItemContextual && _editItemContextual->item && _editItemContextual->item->property("text").isValid()) commitEdit(_prevEditRow, _prevEditCol, _editItemContextual->item->property("text")); } @@ -1485,7 +1486,9 @@ QQmlContext * DataSetViewBase::setStyleDataItem(QQmlContext * previousContext, b QModelIndex modelIndex = _model->index(row, col); - bool isEditable(_model->flags(modelIndex) & Qt::ItemIsEditable); + auto flagsModel = _model->flags(modelIndex); + bool isEditable ( flagsModel & Qt::ItemIsEditable), + isEnabled ( flagsModel & Qt::ItemIsEnabled); if(isEditable || _storedDisplayText.count(row) == 0 || _storedDisplayText[row].count(col) == 0) _storedDisplayText[row][col] = _model->data(modelIndex, Qt::DisplayRole).toString(); @@ -1510,6 +1513,8 @@ QQmlContext * DataSetViewBase::setStyleDataItem(QQmlContext * previousContext, b //The first four also get updated in DataSetViewBase::modelDataChanged! //If adding or changes behaviour also do that there previousContext->setContextProperty("itemText", text); + previousContext->setContextProperty("itemData", _model->data(modelIndex)); + previousContext->setContextProperty("itemEnabled", isEnabled); previousContext->setContextProperty("itemTextEdit", textEdit); previousContext->setContextProperty("itemShadowText", _model->data(modelIndex, getRole("shadowDisplay"))); previousContext->setContextProperty("itemLabel", _model->data(modelIndex, getRole("label")));