diff --git a/qmlui/fixtureeditor/channeledit.cpp b/qmlui/fixtureeditor/channeledit.cpp index ff4d563ad4..2e61154334 100644 --- a/qmlui/fixtureeditor/channeledit.cpp +++ b/qmlui/fixtureeditor/channeledit.cpp @@ -17,6 +17,8 @@ limitations under the License. */ +#include + #include "qlcfixturedef.h" #include "qlccapability.h" @@ -29,6 +31,7 @@ ChannelEdit::ChannelEdit(QLCChannel *channel, QObject *parent) if (m_channel->capabilities().count() == 0) { QLCCapability *cap = new QLCCapability(0, UCHAR_MAX); + QQmlEngine::setObjectOwnership(cap, QQmlEngine::CppOwnership); cap->setWarning(QLCCapability::EmptyName); m_channel->addCapability(cap); } @@ -199,7 +202,7 @@ QVariantList ChannelEdit::capabilities() const return m_capabilities; } -QLCCapability *ChannelEdit::addCapability() +QLCCapability *ChannelEdit::addNewCapability() { int min = 0; if (m_channel->capabilities().count()) @@ -208,13 +211,50 @@ QLCCapability *ChannelEdit::addCapability() min = last->max() + 1; } QLCCapability *cap = new QLCCapability(min, UCHAR_MAX); + QQmlEngine::setObjectOwnership(cap, QQmlEngine::CppOwnership); cap->setWarning(QLCCapability::EmptyName); if (m_channel->addCapability(cap)) + { updateCapabilities(); + } + else + { + delete cap; + return nullptr; + } return cap; } +QLCCapability *ChannelEdit::addCapability(int min, int max, QString name) +{ + QLCCapability *cap = new QLCCapability(min, max); + QQmlEngine::setObjectOwnership(cap, QQmlEngine::CppOwnership); + cap->setName(name); + if (m_channel->addCapability(cap)) + { + updateCapabilities(); + } + else + { + delete cap; + return nullptr; + } + + return cap; +} + +void ChannelEdit::removeCapabilityAtIndex(int index) +{ + QList caps = m_channel->capabilities(); + + if (index < 0 || index >= caps.count()) + return; + + if (m_channel->removeCapability(caps[index])) + updateCapabilities(); +} + int ChannelEdit::getCapabilityPresetAtIndex(int index) { QList caps = m_channel->capabilities(); diff --git a/qmlui/fixtureeditor/channeledit.h b/qmlui/fixtureeditor/channeledit.h index af27323c7f..329f1669dd 100644 --- a/qmlui/fixtureeditor/channeledit.h +++ b/qmlui/fixtureeditor/channeledit.h @@ -54,7 +54,12 @@ class ChannelEdit : public QObject /** Get the list of capabilities for the channel being edited */ QVariantList capabilities() const; - Q_INVOKABLE QLCCapability *addCapability(); + /** Methods to add a new capability */ + Q_INVOKABLE QLCCapability *addNewCapability(); + Q_INVOKABLE QLCCapability *addCapability(int min, int max, QString name); + + /** Delete the capability at the given index */ + Q_INVOKABLE void removeCapabilityAtIndex(int index); /** Get/Set a preset for a capability at the given index */ Q_INVOKABLE int getCapabilityPresetAtIndex(int index); diff --git a/qmlui/fixtureeditor/editorview.cpp b/qmlui/fixtureeditor/editorview.cpp index 6a58a1da23..86c00d70e1 100644 --- a/qmlui/fixtureeditor/editorview.cpp +++ b/qmlui/fixtureeditor/editorview.cpp @@ -17,9 +17,10 @@ limitations under the License. */ +#include + #include "qlcfixturemode.h" #include "qlcfixturedef.h" -#include "qlcchannel.h" #include "channeledit.h" #include "editorview.h" @@ -181,6 +182,7 @@ ChannelEdit *EditorView::requestChannelEditor(QString name) if (ch == nullptr) { ch = new QLCChannel(); + QQmlEngine::setObjectOwnership(ch, QQmlEngine::CppOwnership); ch->setName(tr("New channel %1").arg(m_fixtureDef->channels().count() + 1)); m_fixtureDef->addChannel(ch); updateChannelList(); @@ -191,11 +193,82 @@ ChannelEdit *EditorView::requestChannelEditor(QString name) return m_channelEdit; } +void EditorView::addPresetChannel(QString name, int group) +{ + QLCChannel *channel = new QLCChannel(); + channel->setName(name); + if (group > QLCChannel::Nothing) + { + channel->setGroup(QLCChannel::Intensity); + channel->setColour(QLCChannel::PrimaryColour(group)); + + switch (QLCChannel::PrimaryColour(group)) + { + case QLCChannel::Red: + channel->setPreset(QLCChannel::IntensityRed); + break; + case QLCChannel::Green: + channel->setPreset(QLCChannel::IntensityGreen); + break; + case QLCChannel::Blue: + channel->setPreset(QLCChannel::IntensityBlue); + break; + case QLCChannel::White: + channel->setPreset(QLCChannel::IntensityWhite); + break; + case QLCChannel::Amber: + channel->setPreset(QLCChannel::IntensityAmber); + break; + case QLCChannel::UV: + channel->setPreset(QLCChannel::IntensityUV); + break; + default: + break; + } + } + else + { + channel->setGroup(QLCChannel::Group(group)); + + switch (QLCChannel::Group(group)) + { + case QLCChannel::Intensity: + channel->setPreset(QLCChannel::IntensityDimmer); + break; + case QLCChannel::Pan: + channel->setPreset(QLCChannel::PositionPan); + break; + case QLCChannel::Tilt: + channel->setPreset(QLCChannel::PositionTilt); + break; + case QLCChannel::Colour: + channel->setPreset(QLCChannel::ColorMacro); + break; + case QLCChannel::Shutter: + channel->setPreset(QLCChannel::ShutterStrobeSlowFast); + break; + case QLCChannel::Beam: + channel->setPreset(QLCChannel::NoFunction); + break; + case QLCChannel::Effect: + channel->setPreset(QLCChannel::NoFunction); + break; + default: + break; + } + } + channel->addPresetCapability(); + m_fixtureDef->addChannel(channel); + updateChannelList(); + setModified(true); +} + bool EditorView::deleteChannel(QLCChannel *channel) { // TODO: Tardis bool res = m_fixtureDef->removeChannel(channel); updateChannelList(); + setModified(true); return res; } @@ -250,7 +323,7 @@ void EditorView::updateModeList() void EditorView::modeNameChanged() { - setModified(); + setModified(true); updateModeList(); } diff --git a/qmlui/fixtureeditor/editorview.h b/qmlui/fixtureeditor/editorview.h index 3c4232bd24..0929912043 100644 --- a/qmlui/fixtureeditor/editorview.h +++ b/qmlui/fixtureeditor/editorview.h @@ -23,10 +23,10 @@ #include #include "physicaledit.h" +#include "qlcchannel.h" class QLCFixtureDef; class ChannelEdit; -class QLCChannel; class ListModel; class ModeEdit; @@ -99,6 +99,14 @@ class EditorView : public QObject * Channels ************************************************************************/ public: + enum CompositeChannelTypes + { + RGBChannel = QLCChannel::Nothing + 100, + RGBWChannel, + RGBAWChannel + }; + Q_ENUM(CompositeChannelTypes) + /** Get a list of all the available channels in the definition */ QVariant channels() const; @@ -106,6 +114,8 @@ class EditorView : public QObject * If $name is empty, a new channel is added */ Q_INVOKABLE ChannelEdit *requestChannelEditor(QString name); + Q_INVOKABLE void addPresetChannel(QString name, int group); + /** Delete the given $channel from the definition */ Q_INVOKABLE bool deleteChannel(QLCChannel *channel); diff --git a/qmlui/qml/fixtureeditor/ChannelEditor.qml b/qmlui/qml/fixtureeditor/ChannelEditor.qml index 17288e0628..e338fe0e7d 100644 --- a/qmlui/qml/fixtureeditor/ChannelEditor.qml +++ b/qmlui/qml/fixtureeditor/ChannelEditor.qml @@ -181,6 +181,25 @@ GridLayout id: removeCapButton imgSource: "qrc:/remove.svg" tooltip: qsTr("Delete the selected capabilities") + onClicked: { + editItem.visible = false + editor.removeCapabilityAtIndex(editItem.indexInList) + } + } + + IconButton + { + id: chWizButton + imgSource: "qrc:/wizard.svg" + tooltip: qsTr("Capability wizard") + onClicked: wizardPopup.open() + + PopupChannelWizard + { + id: wizardPopup + chEdit: editor + capabilityWizard: true + } } } @@ -220,7 +239,7 @@ GridLayout else if (index === capsList.count) { // create a new capability - editor.addCapability() + editor.addNewCapability() } var item = capsList.itemAtIndex(index) @@ -316,7 +335,7 @@ GridLayout id: minValBox width: UISettings.bigItemHeight height: UISettings.listItemHeight - label: cap.min + label: cap ? cap.min : 0 } Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } @@ -325,7 +344,7 @@ GridLayout id: maxValBox width: UISettings.bigItemHeight height: UISettings.listItemHeight - label: cap.max + label: cap ? cap.max : 255 } Rectangle { width: 1; height: UISettings.listItemHeight; color: UISettings.fgMedium } @@ -334,18 +353,18 @@ GridLayout id: capDescription Layout.fillWidth: true height: UISettings.listItemHeight - label: cap.name + label: cap ? cap.name : "" } IconButton { - visible: cap.warning + visible: cap ? cap.warning : false height: UISettings.listItemHeight width: height border.width: 0 faSource: FontAwesome.fa_warning faColor: "yellow" - tooltip: capsList.warningDescription(cap.warning) + tooltip: visible ? capsList.warningDescription(cap.warning) : "" } } diff --git a/qmlui/qml/fixtureeditor/EditorView.qml b/qmlui/qml/fixtureeditor/EditorView.qml index 90abf5025e..c8f6420087 100644 --- a/qmlui/qml/fixtureeditor/EditorView.qml +++ b/qmlui/qml/fixtureeditor/EditorView.qml @@ -255,6 +255,20 @@ Rectangle Layout.fillWidth: true color: "transparent" } + + IconButton + { + id: chWizButton + imgSource: "qrc:/wizard.svg" + tooltip: qsTr("Channel wizard") + onClicked: wizardPopup.open() + + PopupChannelWizard + { + id: wizardPopup + editorView: editorRoot.editorView + } + } } } // Rectangle - toolbar diff --git a/qmlui/qml/popup/PopupChannelWizard.qml b/qmlui/qml/popup/PopupChannelWizard.qml new file mode 100644 index 0000000000..36089f8349 --- /dev/null +++ b/qmlui/qml/popup/PopupChannelWizard.qml @@ -0,0 +1,264 @@ +/* + Q Light Controller Plus + PopupChannelWizard.qml + + Copyright (c) Massimo Callegari + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.2 + +import org.qlcplus.classes 1.0 +import "." + +CustomPopupDialog +{ + id: popupRoot + title: qsTr("Fixture Editor Wizard") + + property EditorRef editorView: null + property ChannelEdit chEdit: null + property bool capabilityWizard: false + property var itemsList: [] + + function updateItemsList(create) + { + pListModel.clear() + + for (var i = 0; i < amountSpin.value; i++) + { + var nStr = nameInputBox.text.replace(/#/g, i + 1) + + if (capabilityWizard) + { + var addrMin = startSpin.value + (widthSpin.value * i) + var addrMax = addrMin + widthSpin.value - 1 + + if (create) + { + chEdit.addCapability(addrMin, addrMax, nStr) + } + else + { + nStr = "[" + addrMin + " - " + addrMax + "] " + nStr + pListModel.append({"name": nStr}) + } + } + else + { + var chType = chTypesCombo.currValue + var compNum = 1 + var compTypes = [ chType ] + var compNames = [ nStr ] + + switch (chType) + { + case EditorRef.RGBChannel: + compNum = 3 + compTypes = [ QLCChannel.Red, QLCChannel.Green, QLCChannel.Blue ] + compNames = [ "Red", "Green", "Blue" ] + break + case EditorRef.RGBWChannel: + compNum = 4 + compTypes = [ QLCChannel.Red, QLCChannel.Green, QLCChannel.Blue, QLCChannel.White ] + compNames = [ "Red", "Green", "Blue", "White" ] + break + case EditorRef.RGBAWChannel: + compNum = 5 + compTypes = [ QLCChannel.Red, QLCChannel.Green, QLCChannel.Blue, QLCChannel.Amber, QLCChannel.White ] + compNames = [ "Red", "Green", "Blue", "Amber", "White" ] + break + } + + for (var j = 0; j < compNum; j++) + { + var str = (compNum == 1) ? nStr : compNames[j] + " " + (i + 1) + var type = (compNum == 1) ? chType : compTypes[j] + + if (create) + { + editorView.addPresetChannel(str, type) + } + else + { + pListModel.append({"name": str}) + } + } + } + } + } + + onOpened: updateItemsList(false) + + onAccepted: + { + updateItemsList(true) + } + + contentItem: + GridLayout + { + columns: 1 + columnSpacing: 5 + + GroupBox + { + title: qsTr("Properties") + Layout.fillWidth: true + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + RowLayout + { + RobotoText + { + height: UISettings.listItemHeight + visible: capabilityWizard + label: qsTr("Start") + } + + CustomSpinBox + { + id: startSpin + visible: capabilityWizard + from: 0 + to: 254 + onValueChanged: updateItemsList(false) + } + + RobotoText + { + height: UISettings.listItemHeight + visible: capabilityWizard + label: qsTr("Width") + } + + CustomSpinBox + { + id: widthSpin + visible: capabilityWizard + from: 1 + to: 255 + onValueChanged: updateItemsList(false) + } + + RobotoText + { + height: UISettings.listItemHeight + label: qsTr("Amount") + } + + CustomSpinBox + { + id: amountSpin + from: 1 + to: 1000 + onValueChanged: updateItemsList(false) + } + + RobotoText + { + visible: !capabilityWizard + height: UISettings.listItemHeight + label: qsTr("Type") + } + + CustomComboBox + { + id: chTypesCombo + visible: !capabilityWizard + + ListModel + { + id: chTypesModel + ListElement { mLabel: qsTr("Red"); mIcon: "qrc:/red.svg"; mValue: QLCChannel.Red } + ListElement { mLabel: qsTr("Green"); mIcon: "qrc:/green.svg"; mValue: QLCChannel.Green } + ListElement { mLabel: qsTr("Blue"); mIcon: "qrc:/blue.svg"; mValue: QLCChannel.Blue } + ListElement { mLabel: qsTr("White"); mIcon: "qrc:/white.svg"; mValue: QLCChannel.White } + ListElement { mLabel: qsTr("Amber"); mIcon: "qrc:/amber.svg"; mValue: QLCChannel.Amber } + ListElement { mLabel: qsTr("UV"); mIcon: "qrc:/uv.svg"; mValue: QLCChannel.UV } + ListElement { mLabel: qsTr("RGB"); mIcon: "qrc:/color.svg"; mValue: EditorRef.RGBChannel } + ListElement { mLabel: qsTr("RGBW"); mIcon: "qrc:/color.svg"; mValue: EditorRef.RGBWChannel } + ListElement { mLabel: qsTr("RGBAW"); mIcon: "qrc:/color.svg"; mValue: EditorRef.RGBAWChannel } + ListElement { mLabel: qsTr("Dimmer"); mIcon: "qrc:/dimmer.svg"; mValue: QLCChannel.Intensity } + ListElement { mLabel: qsTr("Pan"); mIcon: "qrc:/pan.svg"; mValue: QLCChannel.Pan } + ListElement { mLabel: qsTr("Tilt"); mIcon: "qrc:/tilt.svg"; mValue: QLCChannel.Tilt } + ListElement { mLabel: qsTr("Color Macro"); mIcon: "qrc:/colorwheel.svg"; mValue: QLCChannel.Colour } + ListElement { mLabel: qsTr("Shutter"); mIcon: "qrc:/shutter.svg"; mValue: QLCChannel.Shutter } + ListElement { mLabel: qsTr("Beam"); mIcon: "qrc:/beam.svg"; mValue: QLCChannel.Beam } + ListElement { mLabel: qsTr("Effect"); mIcon: "qrc:/star.svg"; mValue: QLCChannel.Effect } + + } + model: capabilityWizard ? null : chTypesModel + currValue: capabilityWizard ? 0 : QLCChannel.Red + onValueChanged: + { + currValue = value + updateItemsList(false) + } + } + } // RowLayout + } + + GroupBox + { + title: qsTr("Label") + Layout.fillWidth: true + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + CustomTextEdit + { + id: nameInputBox + Layout.fillWidth: true + text: capabilityWizard ? qsTr("Capability #") : qsTr("Channel #") + onAccepted: popupRoot.accept() + onTextChanged: updateItemsList(false) + } + } + + GroupBox + { + title: qsTr("Preview") + Layout.fillWidth: true + font.family: UISettings.robotoFontName + font.pixelSize: UISettings.textSizeDefault + palette.windowText: UISettings.fgMain + + ListView + { + id: previewList + width: parent.width + implicitHeight: UISettings.bigItemHeight * 2 + clip: true + boundsBehavior: Flickable.StopAtBounds + model: ListModel { id: pListModel } + + delegate: + RobotoText + { + height: UISettings.listItemHeight + width: previewList.width + label: modelData + } + + ScrollBar.vertical: CustomScrollBar { } + } + } + } // GridLayout +} diff --git a/qmlui/qmlui.qrc b/qmlui/qmlui.qrc index 81f1c87793..703afb84c4 100644 --- a/qmlui/qmlui.qrc +++ b/qmlui/qmlui.qrc @@ -62,6 +62,7 @@ qml/popup/CustomPopupDialog.qml qml/popup/PopupAbout.qml + qml/popup/PopupChannelWizard.qml qml/popup/PopupCreatePalette.qml qml/popup/PopupDisclaimer.qml qml/popup/PopupImportProject.qml diff --git a/resources/icons/svg/svgicons.qrc b/resources/icons/svg/svgicons.qrc index 68b3b8bbcc..52c6ce97d0 100644 --- a/resources/icons/svg/svgicons.qrc +++ b/resources/icons/svg/svgicons.qrc @@ -168,6 +168,7 @@ video.svg virtualconsole.svg white.svg + wizard.svg xypad.svg yellow.svg