Commit 74d5c700 authored by Leander Schulten's avatar Leander Schulten
Browse files

improve ProgramBlockEditor. You can now use drag and drop to add connections

parent 8f84332d
......@@ -77,6 +77,16 @@ Item{
id: programEditor
programBlock: listView.currentItem.itemData;
onShowPropertiesChanged: propertiesView.update()
onOpenRightClickEntry: {
rightClickMenu.x = x;
rightClickMenu.y = y;
rightClickMenu.open();
}
onAskToAddConnection: {
popup_addConnectionAsk.outputName = from
popup_addConnectionAsk.inputName = to
popup_addConnectionAsk.visible = true;
}
RoundButton{
id: button_run
checkable: true
......@@ -105,6 +115,34 @@ Item{
anchors.bottom: parent.bottom
anchors.left: button_add_entry.right
anchors.leftMargin: 15
onClicked: {
programEditor.updateInputOutputModels();
popup_addConnection.visible = true;
}
}
RoundButton{
id:button_help
text: "Help"
anchors.bottom: parent.bottom
anchors.left: button_add_connection.right
anchors.leftMargin: 15
onClicked: {
popup_help.visible = true;
}
}
Menu{
id:rightClickMenu
width: 270
MenuItem{
id:firstItem
text: "Remove incoming Connections"
onClicked: programEditor.removeIncomingConnections();
}
MenuItem{
text: "Remove entry"
onClicked: programEditor.removeEntry()
}
}
}
......@@ -307,4 +345,162 @@ Item{
}
}
Popup{
x: (parent.width - width) / 2
y: (parent.height - height) / 2
//modality: Qt.WindowModal
id:popup_addConnection
//title: "Choose entry"
width:300
contentItem: RowLayout {
anchors.left: popup_addConnection.left;
anchors.right: popup_addConnection.right;
Layout.fillWidth: true
ColumnLayout{
Layout.fillWidth: true
ComboBox{
textRole: "display"
id:comboBoxInput
Layout.fillWidth: true
model: programEditor.inputDataConsumerModel
}
RowLayout{
Label{
text:"Length/Size"
}
TextInputField{
Layout.fillWidth: true
id:connection_length
text:"50"
validator: IntValidator{
bottom: 0
top: 1000
}
}
}
ComboBox{
textRole: "display"
id:comboBoxOutput
Layout.fillWidth: true
model: programEditor.outputDataProducerModel
}
RowLayout{
Label{
text:"Startindex"
}
TextInputField{
Layout.fillWidth: true
id:connection_startIndex
text:"0"
validator: IntValidator{
bottom: 0
top: 1000
}
}
}
RowLayout{
Button{
Layout.fillWidth: true
text:"Cancel"
onClicked: popup_addConnection.visible = false
}
Button{
Layout.fillWidth: true
text:"Create"
enabled: comboBoxInput.currentIndex>=0 && connection_startIndex.text.length>0 && comboBoxOutput.currentIndex >= 0 && connection_length.text.length >= 0
onClicked: {
popup_addConnection.visible = false;
programEditor.addConnection(comboBoxInput.currentIndex,connection_length.text,comboBoxOutput.currentIndex,connection_startIndex.text);
}
}
}
}
}
}
Popup{
x: (parent.width - width) / 2
y: (parent.height - height) / 2
id: popup_help
contentItem: Label{
text:"Use drag and drop:<ul><li>Without Modifier to add a normal connection</li><li>With <i>Alt</i> to add Connections from a Entrie lower as the target</li><li>With <i>Shift</i> to move entries</li><li>With <i>Str</i> to move an entry temporarily</li></ul>"
wrapMode: "WordWrap"
width: 300
}
width: 500
}
Popup{
x: (parent.width - width) / 2
y: (parent.height - height) / 2
id: popup_addConnectionAsk
property string outputName
property string inputName
contentItem: ColumnLayout{
Label{
text:"Add a connection from the OutputDataProducer " + popup_addConnectionAsk.outputName + " to the InputDataConsumer " + popup_addConnectionAsk.inputName + "."
Layout.fillWidth: true
wrapMode: "WordWrap"
}
Label{
Layout.fillWidth: true
text: " Output Units will be taken from the OutputDataProducer"
wrapMode: "WordWrap"
TextInputField{
id: textInput_connectionWidth
width: 35
text:"50"
validator: IntValidator{
bottom: 1
top: 9999
}
anchors.left: parent.left
anchors.top: parent.top
font.pixelSize: parent.font.pixelSize
}
}
Label{
Layout.fillWidth: true
text: " is the index of the first Output Units in the array of output units of the OutputDataProducer"
wrapMode: "WordWrap"
TextInputField{
text:"0"
id: textInput_connectionStartIndex
width: 35
validator: IntValidator{
bottom: 0
top: 9999
}
anchors.left: parent.left
anchors.top: parent.top
font.pixelSize: parent.font.pixelSize
}
}
RowLayout{
Button{
Layout.fillWidth: true
text:"Cancel"
onClicked: popup_addConnectionAsk.visible = false
}
Button{
Layout.fillWidth: true
text:"Create"
enabled: textInput_connectionWidth.text.length>0 && textInput_connectionStartIndex.text.length > 0
onClicked: {
popup_addConnectionAsk.visible = false;
programEditor.addConnection(textInput_connectionWidth.text,textInput_connectionStartIndex.text);
}
}
}
}
contentWidth: 300
contentHeight: 220 // the normal implicitHeight is wrong
}
}
......@@ -23,6 +23,8 @@ Shape{
* <--- *
*/
startY: targetBaseline
strokeWidth: 0
strokeColor: "transparent"
PathLine{
id: topRight;
y: targetBaseline
......
This diff is collapsed.
......@@ -13,6 +13,9 @@ namespace detail {
typedef Modules::Property::Type Type;
Q_ENUM(Type)
Modules::Property * property = nullptr;
// wenn dieses Feld ungleich 0 ist, dann wird hier die InputOutputLength gespeichert
Modules::PropertyBase * named = nullptr;
std::function<void()> updateCallback;
private:
void updateValue();
//auto generated class:
......@@ -119,7 +122,6 @@ namespace detail {
void forwardPropertyNameChanged();
};
}
class PropertyInformationModel : public ModelVector<detail::PropertyInformation*>{
......@@ -133,29 +135,68 @@ class ProgramBlockEditor : public QQuickItem
PropertyInformationModel propertyInformationModel;
bool showProperties;
QStringListModel possibleEntryModel;
QStringListModel outputDataProducerModel;
QStringListModel inputDataConsumerModel;
Q_PROPERTY(Modules::ProgramBlock* programBlock READ getProgramBlock WRITE setProgramBlock NOTIFY programBlockChanged)
Q_PROPERTY(PropertyInformationModel * propertyInformationModel READ getPropertyInformationModel CONSTANT)
Q_PROPERTY(QAbstractListModel * possibleEntryModel READ getPossibleEntryModel CONSTANT)
Q_PROPERTY(QAbstractListModel * outputDataProducerModel READ getOutputDataProducerModel CONSTANT)
Q_PROPERTY(QAbstractListModel * inputDataConsumerModel READ getInputDataConsumerModel CONSTANT)
Q_PROPERTY(bool showProperties READ getShowProperties WRITE setShowProperties NOTIFY showPropertiesChanged)
Q_PROPERTY(bool run READ getRun WRITE setRun NOTIFY runChanged)
QQmlComponent programBlockEntry;
QQmlComponent programBlockConnection;
enum {None, MovePermanent, MoveTemporarily, AddConnection, AddReverseConnection} dragType = None;
QQuickItem * dragStartItem = nullptr;
//QPoint dragStartPoint;
QPointF dragOffsetInItem;
QQuickItem * dragEndItem = nullptr;
double scale = 2;
int spaceBetweenLayers = 70;
bool run;
private:
void recreateView();
/**
* @brief getItemWithPropertyBase travels through the hierarchy to find an entry component
* @return the component or nullptr
*/
QQuickItem * getItemWithPropertyBase(QMouseEvent *event);
public:
/** Wird vom ModuleProgramView aufgerufen wenn ein neuer entry hinzugefügt werden soll
*
*
*/
Q_INVOKABLE void updatePossibleEntries();
/**
* @brief updateInputOutputModels Wird vom ModuleProgramView aufgerufen, wenn eine neue Connection hinzugefügt werden soll.
*/
Q_INVOKABLE void updateInputOutputModels();
/**
* @brief addConnection adds a connection between a inputDataConsumer and an OuputDataProducer, this function is meant to be called from qml
* @param inputIndex The index in the inputDataConsumerModel
* @param length the Length of the connection
* @param outputIndex The index in the outputDataProducerModel
* @param startIndex the start index in the output from the outputDataProducer
*/
Q_INVOKABLE void addConnection(int inputIndex, int length, int outputIndex, int startIndex);
/**
* @brief addConnection you can call this method once when the signal @see askToAddConnection(QString,QString) gets emitted
* @param length the length of the connection
* @param startIndex the start index in the output of the target
*/
Q_INVOKABLE void addConnection(int length, int startIndex);
/**
* @brief addEntry fügt den Entry an position index im possibleEntryModel hinzu
* @param index Der index im possibleEntryModel
* @param size Die inputoutput größe des entries
*/
Q_INVOKABLE void addEntry(int index, int size);
/**
* @brief removeEntry removes the entry selected by mouse
*/
Q_INVOKABLE void removeEntry();
/**
* @brief removeIncomingConnections removes all incoming connections from the current entry
*/
Q_INVOKABLE void removeIncomingConnections();
static QQmlEngine * engine;
ProgramBlockEditor();
void setProgramBlock( Modules::ProgramBlock* _programBlock){
......@@ -164,6 +205,7 @@ public:
programBlock = _programBlock;
recreateView();
run = Modules::ModuleManager::singletone()->controller().isProgramRunning(programBlock);
setShowProperties(false);
emit runChanged();
emit programBlockChanged();
}
......@@ -177,6 +219,13 @@ public:
QAbstractListModel *getPossibleEntryModel() {
return &possibleEntryModel;
}
QAbstractListModel *getInputDataConsumerModel() {
return &inputDataConsumerModel;
}
QAbstractListModel *getOutputDataProducerModel() {
return &outputDataProducerModel;
}
void setShowProperties( const bool _showProperties){
//if(_showProperties != showProperties){
showProperties = _showProperties;
......@@ -207,6 +256,8 @@ signals:
void programBlockChanged();
void showPropertiesChanged();
void runChanged();
void askToAddConnection(QString from, QString to);
void openRightClickEntry(int x, int y);
};
#endif // PROGRAMBLOCKEDITOR_H
......@@ -165,6 +165,30 @@ namespace Modules {
}
return false;
}
void removeConnectionsToOutputDataProducer(OutputDataProducer*out){
for(auto & i : filter){
// entfere alle targets ab der ersten verbinndung zu dem zu entfernenem filter
auto first = i.second.targeds.cend();
for(auto t = i.second.targeds.cbegin() ; t != i.second.targeds.cend();++t){
if(t->second.targed == out){
first = t;
break;
}
}
i.second.targeds.erase(first,i.second.targeds.cend());
}
for(auto & i : consumer){
// entfere alle targets ab der ersten verbinndung zu dem zu entfernenem filter
auto first = i.targeds.cend();
for(auto t = i.targeds.cbegin() ; t != i.targeds.cend();++t){
if(t->second.targed == out){
first = t;
break;
}
}
i.targeds.erase(first,i.targeds.cend());
}
}
public:
/**
* @brief addProgramm adds a pointer to a programm. the pointer is owned
......@@ -183,7 +207,50 @@ namespace Modules {
void addConsumer(detail::Connection c);
template<typename F>
void removeConsumer(F f){
std::remove_if(consumer.begin(),consumer.end(),f);
// see https://stackoverflow.com/questions/24263259/c-stdseterase-with-stdremove-if
for (auto it{consumer.begin()}, end{consumer.end()}; it != end; ) {
if (f(*it)) {
it = consumer.erase(it);
}
else {
++it;
}
}
}
template<typename F>
void removeFilter(F f){
// see https://stackoverflow.com/questions/24263259/c-stdseterase-with-stdremove-if
for (auto it{filter.begin()}, end{filter.end()}; it != end; ) {
if (f(*it)) {
removeConnectionsToOutputDataProducer(static_cast<Filter*>(it->second.source.get()));
it = filter.erase(it);
}
else {
++it;
}
}
}
template<typename F>
void removeProgram(F f){
// see https://stackoverflow.com/questions/24263259/c-stdseterase-with-stdremove-if
for (auto it{programs.begin()}, end{programs.end()}; it != end; ) {
if (f(*it)) {
removeConnectionsToOutputDataProducer(static_cast<Program*>(it->get()));
it = programs.erase(it);
}
else {
++it;
}
}
}
void removeConsumer(Consumer * c){
removeConsumer([=](const auto &f){return f.source.get()==c;});
}
void removeFilter(Filter * filter){
removeFilter([=](const auto &f){return f.second.source.get()==filter;});
}
void removeProgram(Program * program){
removeProgram([=](const auto &f){return f.get()==program;});
}
const std::set<std::shared_ptr<Program>> & getPrograms()const{return programs;}
// verschiedene Ebenen von Filtern
......@@ -191,6 +258,12 @@ namespace Modules {
// a list of all consumer and their connections
const std::vector<detail::Connection> getConsumer()const{return consumer;}
std::set<std::shared_ptr<Program>> & getPrograms(){return programs;}
// verschiedene Ebenen von Filtern
std::multimap<int,detail::Connection> & getFilter(){return filter;}
// a list of all consumer and their connections
std::vector<detail::Connection> getConsumer(){return consumer;}
public:
ProgramBlock(QString name = "No name"):name(name){}
void setName( const QString _name){
......
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