Commit f50b2575 authored by Leander Schulten's avatar Leander Schulten

UI/UX: ProgramPrototypeView/ChannelProgramEditor: Greatly improve UI and UX! Fix crashes.

parent 4fec9750
Pipeline #193496 passed with stage
in 5 minutes and 34 seconds
This diff is collapsed.
#ifndef CHANNELPROGRAMMEDITOR_H
#define CHANNELPROGRAMMEDITOR_H
#include <QQuickItem>
#include <QFlags>
#include <QQuickItem>
#include <dmx/programmprototype.h>
namespace GUI{
namespace GUI {
class ChannelProgrammEditor;
class CurrentTimePointWrapper : public QObject{
class CurrentTimePointWrapper : public QObject {
Q_OBJECT
Q_PROPERTY(unsigned char value READ getValue WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(bool hasCurrent READ hasCurrent NOTIFY hasCurrentChanged)
Q_PROPERTY(double time READ getTime WRITE setTime NOTIFY timeChanged)
Q_PROPERTY(QEasingCurve::Type curveToNext READ getCurve WRITE setCurve NOTIFY curveChanged)
ChannelProgrammEditor * editor;
ChannelProgrammEditor *editor;
public:
CurrentTimePointWrapper(ChannelProgrammEditor * editor);
CurrentTimePointWrapper(ChannelProgrammEditor *editor);
void setValue(unsigned char value);
unsigned char getValue()const;
unsigned char getValue() const;
bool hasCurrent();
void setTime(double time);
double getTime()const;
void setCurve(const QEasingCurve::Type &c);
QEasingCurve::Type getCurve()const;
double getTime() const;
signals:
void valueChanged();
void hasCurrentChanged();
void timeChanged();
void curveChanged();
};
class ChannelProgrammEditor : public QQuickItem
{
class ChannelProgrammEditor : public QQuickItem {
Q_OBJECT
Q_PROPERTY(CurrentTimePointWrapper * currentTimePoint READ getCurrentTimePointWrapper CONSTANT)
Q_PROPERTY(QQuickItem* background READ getBackgroundItem WRITE setBackgroundItem NOTIFY backgroundItemChanged)
Q_PROPERTY(QQuickItem* informationDisplayTextItem READ getValueChangeItem WRITE setValueChangeItem NOTIFY valueChangeItemChanged)
Q_PROPERTY(int currentChangingValue READ getCurrentChangingValue NOTIFY currentChangingValueChanged)
Q_PROPERTY(CurrentTimePointWrapper *currentTimePoint READ getCurrentTimePointWrapper CONSTANT)
Q_PROPERTY(int clickRadius MEMBER clickRadius NOTIFY clickRadiusChanged)
Q_PROPERTY(QColor graphColor MEMBER graphColor NOTIFY graphColorChanged)
Q_PROPERTY(QString tooltipText READ getTooltipText CONSTANT)
Q_PROPERTY(DMX::ChannelProgramm* channelProgramm READ getChannelProgramm WRITE setChannelProgramm NOTIFY channelProgrammChanged)
Q_PROPERTY(DMX::ChannelProgramm *channelProgramm READ getChannelProgramm WRITE setChannelProgramm NOTIFY channelProgrammChanged)
Q_PROPERTY(double hoverSegmentX READ getHoverSegmentX NOTIFY hoverSegmentXChanged)
Q_PROPERTY(double hoverSegmentLength READ getHoverSegmentLength NOTIFY hoverSegmentLengthChanged)
Q_PROPERTY(double selectedSegmentX READ getSelectedSegmentX NOTIFY selectedSegmentXChanged)
Q_PROPERTY(double selectedSegmentLength READ getSelectedSegmentLength NOTIFY selectedSegmentLengthChanged)
Q_PROPERTY(QEasingCurve::Type selectedEasingCurve READ getSelectedEasingCurve WRITE setSelectedEasingCurve NOTIFY selectedEasingCurveChanged)
Q_PROPERTY(double mouseX READ getMouseX NOTIFY mouseXChanged)
private:
friend class CurrentTimePointWrapper;
CurrentTimePointWrapper currentTimePointWrapper;
DMX::ChannelProgramm * channelProgramm = nullptr;
QQuickItem * backgroundItem = nullptr;
QQuickItem * valueChangeItem = nullptr;
DMX::ChannelProgramm *channelProgramm = nullptr;
decltype(DMX::ChannelProgramm::timeline)::iterator currentTimePoint;
int clickRadius=4;
double xScale = 4.;
decltype(DMX::ChannelProgramm::timeline)::iterator currentSegment;
int clickRadius = 4;
double xScale = 50.;
double totalXOffset = 0;
double xDiff = 0 ;
QColor graphColor = QColor(0,0,0);
enum Modifier{X_PRESSED=0x1,Y_PRESSED=0x2,N_PRESSED=0x4,T_PRESSED=0x8,V_PRESSED=0x10,D_PRESSED=0x20};
QColor graphColor = QColor(0, 0, 0);
enum Modifier { X_PRESSED = 0x1, Y_PRESSED = 0x2, N_PRESSED = 0x4, D_PRESSED = 0x20 };
QFlags<Modifier> modifier;
#define INVALID_POS QPoint(std::numeric_limits<int>::max(),std::numeric_limits<int>::max())
QPoint lastMousePosition = INVALID_POS;
double hoverSegmentX = 0;
double hoverSegmentLength = 0;
void updateHoverSegment();
double selectedSegmentX = 0;
double selectedSegmentLength = 0;
void updateSelectedSegment();
static constexpr auto INVALID_POS = QPointF(std::numeric_limits<qreal>::lowest(), std::numeric_limits<qreal>::lowest());
QPointF lastMousePosition = INVALID_POS;
ulong mousePressTimestamp;
decltype(DMX::ChannelProgramm::timeline)::iterator getTimePointForPosition(int x, int y);
/**
......@@ -70,7 +74,7 @@ private:
* @param x the x coordinate
* @return x * xScale + totalXOffset
*/
double mapToVisualX(double x){return x * xScale + totalXOffset;}
double mapToVisualX(double x) const { return x * xScale + totalXOffset; }
/**
* @brief mapFromVisualX maps from the visual Position of the graph to real values
* @param xthe x coordinate
......@@ -80,44 +84,50 @@ private:
* result = (x-totalXOffset)*(1/xScale) = 100
* @return
*/
double mapFromVisualX(double x){return (x-totalXOffset) * (1/xScale);}
int getScaledY(int y){return 255 - int(y / height() * 255);}
double mapFromVisualX(double x) const { return (x - totalXOffset) * (1 / xScale); }
int getScaledY(int y) const { return 255 - int(y / height() * 255); }
public:
ChannelProgrammEditor();
void setBackgroundItem(QQuickItem*b);
QQuickItem* getBackgroundItem()const{return backgroundItem;}
void setValueChangeItem(QQuickItem*b);
QQuickItem* getValueChangeItem()const{return valueChangeItem;}
int getCurrentChangingValue()const{return haveCurrentTimePoint()?currentTimePoint->value:0;}
void setChannelProgramm(DMX::ChannelProgramm*p);
DMX::ChannelProgramm* getChannelProgramm()const{return channelProgramm;}
QString getTooltipText()const;
bool haveCurrentTimePoint()const{return channelProgramm?(currentTimePoint!=channelProgramm->timeline.cend()):false;}
DMX::TimePoint * getCurrentTimePoint();
CurrentTimePointWrapper * getCurrentTimePointWrapper(){return &currentTimePointWrapper;}
Q_INVOKABLE QEasingCurve * getCurveForPoint(int x);
void setChannelProgramm(DMX::ChannelProgramm *p);
DMX::ChannelProgramm *getChannelProgramm() const { return channelProgramm; }
bool haveCurrentTimePoint() const { return channelProgramm ? (currentTimePoint != channelProgramm->timeline.cend()) : false; }
DMX::TimePoint *getCurrentTimePoint();
CurrentTimePointWrapper *getCurrentTimePointWrapper() { return &currentTimePointWrapper; }
Q_INVOKABLE QEasingCurve *getCurveForPoint(int x);
double getHoverSegmentX() const { return hoverSegmentX; }
double getHoverSegmentLength() const { return hoverSegmentLength; }
double getSelectedSegmentX() const { return selectedSegmentX; }
double getSelectedSegmentLength() const { return selectedSegmentLength; }
QEasingCurve::Type getSelectedEasingCurve() const { return channelProgramm && currentSegment != channelProgramm->timeline.end() ? currentSegment->easingCurveToNextPoint.type() : static_cast<QEasingCurve::Type>(-1); }
void setSelectedEasingCurve(QEasingCurve::Type type);
double getMouseX() const { return lastMousePosition.x(); }
Q_INVOKABLE double getTimeForVisualX(double x) const { return mapFromVisualX(x); }
Q_INVOKABLE int getValueForVisualX(double x) const { return channelProgramm ? channelProgramm->getValueForTime(std::max(0., mapFromVisualX(x))) : -1; }
protected:
virtual void updatePolish()override;
virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)override;
virtual QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *)override;
virtual void mouseMoveEvent(QMouseEvent *event)override;
virtual void mousePressEvent(QMouseEvent *event)override;
virtual void mouseReleaseEvent(QMouseEvent *event)override;
virtual void hoverEnterEvent(QHoverEvent*e)override{lastMousePosition=e->pos();e->accept();forceActiveFocus(Qt::MouseFocusReason);}
virtual void hoverMoveEvent(QHoverEvent*e)override;
virtual void hoverLeaveEvent(QHoverEvent*e)override{lastMousePosition=INVALID_POS;update();e->accept();}
virtual void keyPressEvent(QKeyEvent *event)override;
virtual void keyReleaseEvent(QKeyEvent *event)override;
virtual void wheelEvent(QWheelEvent *event)override;
virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
virtual void hoverEnterEvent(QHoverEvent *e) override;
virtual void hoverMoveEvent(QHoverEvent *e) override;
virtual void hoverLeaveEvent(QHoverEvent *e) override;
virtual void keyPressEvent(QKeyEvent *event) override;
virtual void keyReleaseEvent(QKeyEvent *event) override;
virtual void wheelEvent(QWheelEvent *event) override;
signals:
void backgroundItemChanged();
void valueChangeItemChanged();
void currentChangingValueChanged(int);
void clickRadiusChanged();
void graphColorChanged();
void channelProgrammChanged();
void click(int x, int y);
void rightClick(int x, int y);
void hoverSegmentXChanged();
void hoverSegmentLengthChanged();
void selectedSegmentXChanged();
void selectedSegmentLengthChanged();
void selectedEasingCurveChanged();
void mouseXChanged();
};
} // namespace GUI
......
......@@ -6,13 +6,14 @@ import custom.licht 1.0
import "components"
ModelView{
model:programmPrototypeModel
id:modelView
model: programmPrototypeModel
id: modelView
addButton.text: "Add Prototype"
removeButton.text: "Remove Prototype"
onAddClicked: dialog.visible = true
onRemoveClicked: ModelManager.removeDmxProgramPrototype(remove);
rows: 5
sortModel: ListModel{
ListElement{
name: "Creation Date"
......@@ -27,136 +28,172 @@ ModelView{
sortPropertyName: "devicePrototype.name"
}
}
Label{
Layout.topMargin: 4
Layout.columnSpan: 2
text: "Die einzelnen ChannelProgramme :"
font.underline: true
}
ListView{
clip:true
Layout.column: 2
clip: true
Layout.columnSpan: 2
Layout.row: 2
Layout.fillHeight: true
Layout.fillWidth: true
header: Text{
text:"Die einzelnen ChannelProgramme : "
color: Material.foreground
height:70
id: channelView
spacing: 32
model: modelView.currentModelData ? modelView.currentModelData.channelProgramms : null
ScrollBar.vertical: ScrollBar {
policy: channelView.contentHeight > channelView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
}
id : channelView
spacing: 50
delegate:
ChannelProgrammEditor{
layer.samples: 4
clickRadius: 50
antialiasing: true
id:editor
width: parent.width
//onClicked: channelView.currentIndex = index
height:100
background: Rectangle{color:"lightgrey"}
channelProgramm: modelData
focus: true
informationDisplayTextItem:Text{}
delegate: ColumnLayout{
width: channelView.width
RowLayout{
height:50
anchors.bottom: editor.top
Layout.preferredHeight: 50
Layout.fillWidth: true
spacing: 8
Label{
text:editor.channelProgramm.channel.name
text: editor.channelProgramm.channel.name
font.bold: true
Layout.minimumWidth: 60
}
Label{
text:"time: "
text: "Time: "
visible: editor.currentTimePoint.hasCurrent
}
TextInputField{
Layout.minimumWidth: 30
text:editor.currentTimePoint.time
text: editor.currentTimePoint.time
visible: editor.currentTimePoint.hasCurrent
onAccepted: editor.currentTimePoint.time = text
}
Label{
text:"value: "
text:"Value: "
visible: editor.currentTimePoint.hasCurrent
}
TextInputField{
Layout.minimumWidth: 30
text:editor.currentTimePoint.value
text: editor.currentTimePoint.value
visible: editor.currentTimePoint.hasCurrent
onAccepted: editor.currentTimePoint.value = text
validator: IntValidator{bottom: 0; top:255}
}
Label{
text: "To Next:"
visible: editor.currentTimePoint.hasCurrent
text: "Curve type:"
visible: editor.selectedEasingCurve >= 0
}
ComboBox{
model: easingModel
property bool open: false
currentIndex: editor.currentTimePoint.curveToNext
visible: editor.currentTimePoint.hasCurrent
Layout.preferredWidth: 150
currentIndex: editor.selectedEasingCurve
visible: editor.selectedEasingCurve >= 0
onDownChanged: {
open|=down;
}
onHighlighted: {
if(open)editor.currentTimePoint.curveToNext = index
if(open)editor.selectedEasingCurve = index
}
onActivated: {
editor.currentTimePoint.curveToNext = index
editor.selectedEasingCurve = index
open=false;
}
}
}
onRightClick:{
console.log(x + " "+y)
}
}
} // RowLayout
ChannelProgrammEditor{
id: editor
Layout.preferredHeight: 100
Layout.fillWidth: true
clickRadius: 36
channelProgramm: modelData
Rectangle{
anchors.fill: parent
color: "lightgrey"
z: -1
}
Rectangle{
x: editor.hoverSegmentX
z: -0.5
width: editor.hoverSegmentLength
height: 100
color: Qt.rgba(.7,.7,.7)
}
Rectangle{
x: editor.selectedSegmentX
z: -0.4
width: editor.selectedSegmentLength
height: 100
color: Qt.rgba(.5,.5,.5)
}
Rectangle{
anchors.top: parent.bottom
visible: editor.mouseX >= 0
x: editor.mouseX
width: 1
height: 4
color: Material.foreground
Label{
anchors.top: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: editor.mouseX < contentWidth/2 ? -(editor.mouseX - contentWidth/2) : 0
horizontalAlignment: Text.AlignHCenter
text: editor.getValueForVisualX(editor.mouseX) + " at\n" + editor.getTimeForVisualX(editor.mouseX).toFixed(2);
}
}
} // ChannelProgrammEditor
} // delegate: ColumnLayout
} // ListView
model: modelView.currentModelData ? modelView.currentModelData.channelProgramms : null
Label{
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.margins: 5
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
text: "Hold N and click to create a new TimePoint. You can select a TimePoint to change its values. A selected TimePoint can be deleted with d. You can drag a TimePoint. Hold x while scrolling to change the zooming."
}
Popup{
modal: true
id:dialog
width:300
id: dialog
width: 300
x: (parent.width - width) / 2
y: (parent.height - height) / 2
contentItem: ColumnLayout{
spacing: 10
ComboBox{
Layout.fillWidth: true
id:prototype
id: prototype
model: devicePrototypeModel
textRole: "display"
}
RowLayout{
Label{
id:nameLabel
text:"Name :"
id: nameLabel
text: "Name :"
}
TextInputField{
Layout.fillWidth: true
id:name
id: name
}
}
RowLayout{
Label{
text:"Description :"
text: "Description :"
}
TextInputField{
Layout.fillWidth: true
id:description
id: description
}
}
RowLayout{
Button{
Layout.fillWidth: true
text:"Abbrechen"
text: "Abbrechen"
onClicked: dialog.visible = false
}
Button{
Layout.fillWidth: true
text:"Erzeugen"
text: "Erzeugen"
onClicked: {
if(name.text===""){
name.underlineColor = "red";
......@@ -165,9 +202,8 @@ ModelView{
ModelManager.addProgrammPrototype(prototype.currentIndex,name.text,description.text);
}
}
}
}
}
}
} // Button
} // RowLayout
} // contentItem: ColumnLayout
} // Popup
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment