add support for time based rules and rework rule dialog

This commit is contained in:
Michael Zanetti 2018-06-14 08:27:21 +02:00
parent 3566977f18
commit 6fa3604f52
45 changed files with 2748 additions and 351 deletions

View File

@ -34,6 +34,12 @@
#include "types/stateevaluator.h"
#include "types/stateevaluators.h"
#include "types/statedescriptor.h"
#include "types/timeeventitem.h"
#include "types/timeeventitems.h"
#include "types/timedescriptor.h"
#include "types/repeatingoption.h"
#include "types/calendaritems.h"
#include "types/calendaritem.h"
#include <QMetaEnum>
@ -245,6 +251,10 @@ QVariantMap JsonTypes::packRule(Rule *rule)
ret.insert("eventDescriptors", packEventDescriptors(rule->eventDescriptors()));
}
if (rule->timeDescriptor()->timeEventItems()->rowCount() > 0 || rule->timeDescriptor()->calendarItems()->rowCount() > 0) {
ret.insert("timeDescriptor", packTimeDescriptor(rule->timeDescriptor()));
}
if (rule->stateEvaluator()) {
ret.insert("stateEvaluator", packStateEvaluator(rule->stateEvaluator()));
}
@ -338,6 +348,67 @@ QVariantMap JsonTypes::packStateEvaluator(StateEvaluator *stateEvaluator)
return ret;
}
QVariantMap JsonTypes::packTimeDescriptor(TimeDescriptor *timeDescriptor)
{
QVariantMap ret;
QVariantList timeEventItems;
for (int i = 0; i < timeDescriptor->timeEventItems()->rowCount(); i++) {
timeEventItems.append(packTimeEventItem(timeDescriptor->timeEventItems()->get(i)));
}
if (!timeEventItems.isEmpty()) {
ret.insert("timeEventItems", timeEventItems);
}
QVariantList calendarItems;
for (int i = 0; i < timeDescriptor->calendarItems()->rowCount(); i++) {
calendarItems.append(packCalendarItem(timeDescriptor->calendarItems()->get(i)));
}
if (!calendarItems.isEmpty()) {
ret.insert("calendarItems", calendarItems);
}
return ret;
}
QVariantMap JsonTypes::packTimeEventItem(TimeEventItem *timeEventItem)
{
QVariantMap ret;
if (!timeEventItem->time().isNull()) {
ret.insert("time", timeEventItem->time().toString("hh:mm"));
}
if (!timeEventItem->dateTime().isNull()) {
ret.insert("dateTime", timeEventItem->dateTime().toSecsSinceEpoch());
}
ret.insert("repeating", packRepeatingOption(timeEventItem->repeatingOption()));
return ret;
}
QVariantMap JsonTypes::packCalendarItem(CalendarItem *calendarItem)
{
QVariantMap ret;
ret.insert("duration", calendarItem->duration());
if (!calendarItem->dateTime().isNull()) {
ret.insert("datetime", calendarItem->dateTime().toSecsSinceEpoch());
}
if (!calendarItem->startTime().isNull()) {
ret.insert("startTime", calendarItem->startTime().toString("hh:mm"));
}
ret.insert("repeating", packRepeatingOption(calendarItem->repeatingOption()));
return ret;
}
QVariantMap JsonTypes::packRepeatingOption(RepeatingOption *repeatingOption)
{
QVariantMap ret;
QMetaEnum repeatingModeEnum = QMetaEnum::fromType<RepeatingOption::RepeatingMode>();
ret.insert("mode", repeatingModeEnum.valueToKey(repeatingOption->repeatingMode()));
if (!repeatingOption->weekDays().isEmpty()) {
ret.insert("weekDays", repeatingOption->weekDays());
}
if (!repeatingOption->monthDays().isEmpty()) {
ret.insert("monthDays", repeatingOption->monthDays());
}
return ret;
}
DeviceClass::SetupMethod JsonTypes::stringToSetupMethod(const QString &setupMethodString)
{
if (setupMethodString == "SetupMethodJustAdd") {

View File

@ -43,6 +43,10 @@ class Rule;
class StateEvaluator;
class RuleActions;
class EventDescriptors;
class TimeDescriptor;
class TimeEventItem;
class CalendarItem;
class RepeatingOption;
class JsonTypes : public QObject
{
@ -65,6 +69,11 @@ public:
static QVariantList packEventDescriptors(EventDescriptors* eventDescriptors);
static QVariantMap packParam(Param *param);
static QVariantMap packStateEvaluator(StateEvaluator* stateEvaluator);
static QVariantMap packTimeDescriptor(TimeDescriptor* timeDescriptor);
static QVariantMap packTimeEventItem(TimeEventItem* timeEventItem);
static QVariantMap packCalendarItem(CalendarItem* calendarItem);
static QVariantMap packRepeatingOption(RepeatingOption* repeatingOption);
private:
static DeviceClass::SetupMethod stringToSetupMethod(const QString &setupMethodString);
static QList<DeviceClass::BasicTag> stringListToBasicTags(const QStringList &basicTagsStringList);

View File

@ -18,6 +18,12 @@
#include "types/ruleactionparam.h"
#include "types/eventdescriptors.h"
#include "types/eventdescriptor.h"
#include "types/timedescriptor.h"
#include "types/timeeventitems.h"
#include "types/timeeventitem.h"
#include "types/repeatingoption.h"
#include "types/calendaritems.h"
#include "types/calendaritem.h"
#include "types/rule.h"
#include "types/interfaces.h"
#include "types/interface.h"
@ -104,6 +110,12 @@ void registerQmlTypes() {
qmlRegisterUncreatableType<StateDescriptor>(uri, 1, 0, "StateDescriptor", "Uncreatable");
qmlRegisterUncreatableType<StateEvaluator>(uri, 1, 0, "StateEvaluator", "Uncreatable");
qmlRegisterUncreatableType<StateEvaluators>(uri, 1, 0, "StateEvaluators", "Uncreatable");
qmlRegisterUncreatableType<TimeDescriptor>(uri, 1, 0, "TimeDescriptor", "Uncreatable");
qmlRegisterUncreatableType<TimeEventItems>(uri, 1, 0, "TimeEventItems", "Uncreatable");
qmlRegisterUncreatableType<TimeEventItem>(uri, 1, 0, "TimeEventItem", "Uncreatable");
qmlRegisterUncreatableType<RepeatingOption>(uri, 1, 0, "RepeatingOption", "Uncreatable");
qmlRegisterUncreatableType<CalendarItems>(uri, 1, 0, "CalendarItems", "Uncreatable");
qmlRegisterUncreatableType<CalendarItem>(uri, 1, 0, "CalendarItem", "Uncreatable");
qmlRegisterUncreatableType<Interface>(uri, 1, 0, "Interface", "Uncreatable");
qmlRegisterSingletonType<Interfaces>(uri, 1, 0, "Interfaces", interfacesModel_provider);

View File

@ -12,7 +12,12 @@
#include "types/stateevaluator.h"
#include "types/stateevaluators.h"
#include "types/statedescriptor.h"
#include "types/timedescriptor.h"
#include "types/timeeventitems.h"
#include "types/timeeventitem.h"
#include "types/repeatingoption.h"
#include "types/calendaritems.h"
#include "types/calendaritem.h"
#include <QMetaEnum>
@ -179,6 +184,7 @@ Rule *RuleManager::parseRule(const QVariantMap &ruleMap)
parseEventDescriptors(ruleMap.value("eventDescriptors").toList(), rule);
parseRuleActions(ruleMap.value("actions").toList(), rule);
parseRuleExitActions(ruleMap.value("exitActions").toList(), rule);
parseTimeDescriptor(ruleMap.value("timeDescriptor").toMap(), rule);
rule->setStateEvaluator(parseStateEvaluator(ruleMap.value("stateEvaluator").toMap()));
return rule;
}
@ -259,12 +265,36 @@ void RuleManager::parseRuleExitActions(const QVariantList &ruleActions, Rule *ru
void RuleManager::parseTimeDescriptor(const QVariantMap &timeDescriptor, Rule *rule)
{
Q_UNUSED(rule)
Q_UNUSED(timeDescriptor)
// foreach (const QVariant &timeEventItemVariant, timeDescriptor.value("timeEventItems").toList()) {
// TimeEventItem *timeEventItem = new TimeEventItem();
// timeEventItem->setDateTime(QDateTime::fromSecsSinceEpoch(timeEventItemVariant.toMap().value("datetime").toULongLong()));
// timeEventItem->setTime(QTime::fromString(timeEventItemVariant.toMap().value("time").toString()));
// timeEventItem->setRepeatingOption();
// }
foreach (const QVariant &timeEventItemVariant, timeDescriptor.value("timeEventItems").toList()) {
TimeEventItem *timeEventItem = new TimeEventItem();
if (timeEventItemVariant.toMap().contains("datetime")) {
timeEventItem->setDateTime(QDateTime::fromSecsSinceEpoch(timeEventItemVariant.toMap().value("datetime").toULongLong()));
}
if (timeEventItemVariant.toMap().contains("time")){
timeEventItem->setTime(QTime::fromString(timeEventItemVariant.toMap().value("time").toString()));
}
QVariantMap repeatingOptionMap = timeEventItemVariant.toMap().value("repeating").toMap();
QMetaEnum modeEnum = QMetaEnum::fromType<RepeatingOption::RepeatingMode>();
timeEventItem->repeatingOption()->setRepeatingMode((RepeatingOption::RepeatingMode)modeEnum.keyToValue(repeatingOptionMap.value("mode").toByteArray()));
timeEventItem->repeatingOption()->setWeekDays(repeatingOptionMap.value("weekDays").toList());
timeEventItem->repeatingOption()->setMonthDays(repeatingOptionMap.value("monthDays").toList());
rule->timeDescriptor()->timeEventItems()->addTimeEventItem(timeEventItem);
}
foreach (const QVariant &calendarItemVariant, timeDescriptor.value("calendarItems").toList()) {
CalendarItem *calendarItem = new CalendarItem();
if (calendarItemVariant.toMap().contains("datetime")) {
calendarItem->setDateTime(QDateTime::fromSecsSinceEpoch(calendarItemVariant.toMap().value("datetime").toULongLong()));
}
if (calendarItemVariant.toMap().contains("startTime")) {
calendarItem->setStartTime(QTime::fromString(calendarItemVariant.toMap().value("startTime").toString()));
}
calendarItem->setDuration(calendarItemVariant.toMap().value("duration").toInt());
QVariantMap repeatingOptionMap = calendarItemVariant.toMap().value("repeating").toMap();
QMetaEnum modeEnum = QMetaEnum::fromType<RepeatingOption::RepeatingMode>();
calendarItem->repeatingOption()->setRepeatingMode((RepeatingOption::RepeatingMode)modeEnum.keyToValue(repeatingOptionMap.value("mode").toByteArray()));
calendarItem->repeatingOption()->setWeekDays(repeatingOptionMap.value("weekDays").toList());
calendarItem->repeatingOption()->setMonthDays(repeatingOptionMap.value("monthDays").toList());
rule->timeDescriptor()->calendarItems()->addCalendarItem(calendarItem);
}
// rule->timeDescriptor()
}

View File

@ -51,7 +51,8 @@ HEADERS += \
types/timeeventitem.h \
types/calendaritem.h \
types/timeeventitems.h \
types/calendaritems.h
types/calendaritems.h \
types/repeatingoption.h
SOURCES += \
types/vendor.cpp \
@ -93,7 +94,8 @@ SOURCES += \
types/timeeventitem.cpp \
types/calendaritem.cpp \
types/timeeventitems.cpp \
types/calendaritems.cpp
types/calendaritems.cpp \
types/repeatingoption.cpp
# install header file with relative subdirectory
for(header, HEADERS) {

View File

@ -1,6 +1,62 @@
#include "calendaritem.h"
#include "repeatingoption.h"
CalendarItem::CalendarItem(QObject *parent) : QObject(parent)
{
m_repeatingOption = new RepeatingOption(this);
}
int CalendarItem::duration() const
{
return m_duration;
}
void CalendarItem::setDuration(int duration)
{
if (m_duration != duration) {
m_duration = duration;
emit durationChanged();
}
}
QDateTime CalendarItem::dateTime() const
{
return m_dateTime;
}
void CalendarItem::setDateTime(const QDateTime &dateTime)
{
if (m_dateTime != dateTime) {
m_dateTime = dateTime;
emit dateTimeChanged();
}
}
QTime CalendarItem::startTime() const
{
return m_startTime;
}
void CalendarItem::setStartTime(const QTime &startTime)
{
if (m_startTime != startTime) {
m_startTime = startTime;
emit startTimeChanged();
}
}
RepeatingOption *CalendarItem::repeatingOption() const
{
return m_repeatingOption;
}
CalendarItem *CalendarItem::clone() const
{
CalendarItem* ret = new CalendarItem();
ret->m_dateTime = this->m_dateTime;
ret->m_duration = this->m_duration;
ret->m_repeatingOption = this->m_repeatingOption;
ret->m_startTime = this->m_startTime;
return ret;
}

View File

@ -2,16 +2,44 @@
#define CALENDARITEM_H
#include <QObject>
#include <QDateTime>
class RepeatingOption;
class CalendarItem : public QObject
{
Q_OBJECT
Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged)
Q_PROPERTY(QTime startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(RepeatingOption* repeatingOption READ repeatingOption CONSTANT)
public:
explicit CalendarItem(QObject *parent = nullptr);
signals:
int duration() const;
void setDuration(int duration);
public slots:
QDateTime dateTime() const;
void setDateTime(const QDateTime &dateTime);
QTime startTime() const;
void setStartTime(const QTime &startTime);
RepeatingOption* repeatingOption() const;
CalendarItem* clone() const;
signals:
void durationChanged();
void dateTimeChanged();
void startTimeChanged();
private:
int m_duration = 0;
QDateTime m_dateTime;
QTime m_startTime;
RepeatingOption* m_repeatingOption = nullptr;
};
#endif // CALENDARITEM_H
#endif // CALENDARITEM_H

View File

@ -1,6 +1,50 @@
#include "calendaritems.h"
#include "calendaritem.h"
CalendarItems::CalendarItems(QObject *parent) : QObject(parent)
CalendarItems::CalendarItems(QObject *parent) : QAbstractListModel(parent)
{
}
int CalendarItems::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_list.count();
}
QVariant CalendarItems::data(const QModelIndex &index, int role) const
{
return QVariant();
}
void CalendarItems::addCalendarItem(CalendarItem *calendarItem)
{
calendarItem->setParent(this);
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
m_list.append(calendarItem);
endInsertRows();
emit countChanged();
}
void CalendarItems::removeCalendarItem(int index)
{
if (index < 0 || index > m_list.count()) {
return;
}
beginRemoveRows(QModelIndex(), index, index);
m_list.takeAt(index)->deleteLater();
endRemoveRows();
}
CalendarItem *CalendarItems::createNewCalendarItem() const
{
return new CalendarItem();
}
CalendarItem *CalendarItems::get(int index) const
{
if (index < 0 || index > m_list.count()) {
return nullptr;
}
return m_list.at(index);
}

View File

@ -1,17 +1,31 @@
#ifndef CALENDARITEMS_H
#define CALENDARITEMS_H
#include <QObject>
#include <QAbstractListModel>
class CalendarItems : public QObject
class CalendarItem;
class CalendarItems : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
public:
explicit CalendarItems(QObject *parent = nullptr);
signals:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
public slots:
Q_INVOKABLE void addCalendarItem(CalendarItem* calendarItem);
Q_INVOKABLE void removeCalendarItem(int index);
Q_INVOKABLE CalendarItem* createNewCalendarItem() const;
Q_INVOKABLE CalendarItem* get(int index) const;
signals:
void countChanged();
private:
QList<CalendarItem*> m_list;
};
#endif // CALENDARITEMS_H
#endif // CALENDARITEMS_H

View File

@ -0,0 +1,45 @@
#include "repeatingoption.h"
RepeatingOption::RepeatingOption(QObject *parent) : QObject(parent)
{
}
RepeatingOption::RepeatingMode RepeatingOption::repeatingMode() const
{
return m_repeatingMode;
}
void RepeatingOption::setRepeatingMode(RepeatingOption::RepeatingMode repeatingMode)
{
if (m_repeatingMode != repeatingMode) {
m_repeatingMode = repeatingMode;
emit repeatingModeChanged();
}
}
QVariantList RepeatingOption::weekDays() const
{
return m_weekDays;
}
void RepeatingOption::setWeekDays(const QVariantList &weekDays)
{
if (m_weekDays != weekDays) {
m_weekDays = weekDays;
emit weekDaysChanged();
}
}
QVariantList RepeatingOption::monthDays() const
{
return m_monthDays;
}
void RepeatingOption::setMonthDays(const QVariantList &monthDays)
{
if (m_monthDays != monthDays) {
m_monthDays = monthDays;
emit monthDaysChanged();
}
}

View File

@ -0,0 +1,48 @@
#ifndef REPEATINGOPTION_H
#define REPEATINGOPTION_H
#include <QObject>
#include <QVariantList>
class RepeatingOption: public QObject
{
Q_OBJECT
Q_PROPERTY(RepeatingMode repeatingMode READ repeatingMode WRITE setRepeatingMode NOTIFY repeatingModeChanged)
Q_PROPERTY(QVariantList weekDays READ weekDays WRITE setWeekDays NOTIFY weekDaysChanged)
Q_PROPERTY(QVariantList monthDays READ monthDays WRITE setMonthDays NOTIFY monthDaysChanged)
public:
enum RepeatingMode {
RepeatingModeNone,
RepeatingModeHourly,
RepeatingModeDaily,
RepeatingModeWeekly,
RepeatingModeMonthly,
RepeatingModeYearly
};
Q_ENUM(RepeatingMode)
explicit RepeatingOption(QObject *parent = nullptr);
RepeatingMode repeatingMode() const;
void setRepeatingMode(RepeatingMode repeatingMode);
QVariantList weekDays() const;
void setWeekDays(const QVariantList &weekDays);
QVariantList monthDays() const;
void setMonthDays(const QVariantList &monthDays);
signals:
void repeatingModeChanged();
void weekDaysChanged();
void monthDaysChanged();
private:
RepeatingMode m_repeatingMode = RepeatingModeDaily;
QVariantList m_weekDays;
QVariantList m_monthDays;
};
#endif // REPEATINGOPTION_H

View File

@ -4,9 +4,16 @@
#include "eventdescriptors.h"
#include "stateevaluator.h"
#include "stateevaluators.h"
#include "statedescriptor.h"
#include "ruleaction.h"
#include "ruleactions.h"
#include "timedescriptor.h"
#include "timeeventitems.h"
#include "timeeventitem.h"
#include "calendaritems.h"
#include "calendaritem.h"
#include <QDebug>
Rule::Rule(const QUuid &id, QObject *parent) :
QObject(parent),
@ -14,9 +21,15 @@ Rule::Rule(const QUuid &id, QObject *parent) :
m_eventDescriptors(new EventDescriptors(this)),
// m_stateEvaluator(new StateEvaluator(this)),
m_actions(new RuleActions(this)),
m_exitActions(new RuleActions(this))
m_exitActions(new RuleActions(this)),
m_timeDescriptor(new TimeDescriptor(this))
{
qDebug() << "### Creating rule" << this;
}
Rule::~Rule()
{
qDebug() << "### Destroying rule" << this;
}
QUuid Rule::id() const
@ -113,11 +126,15 @@ Rule *Rule::clone() const
for (int i = 0; i < this->eventDescriptors()->rowCount(); i++) {
ret->eventDescriptors()->addEventDescriptor(this->eventDescriptors()->get(i)->clone());
}
// ret->stateEvaluator()->setStateDescriptor(this->stateEvaluator()->stateDescriptor()->clone());
// ret->stateEvaluator()->setStateOperator(this->stateEvaluator()->stateOperator());
// for (int i = 0; i < this->stateEvaluator()->childEvaluators()->rowCount(); i++) {
// ret->stateEvaluator()->childEvaluators()->
// }
for (int i = 0; i < this->timeDescriptor()->timeEventItems()->rowCount(); i++) {
ret->timeDescriptor()->timeEventItems()->addTimeEventItem(this->timeDescriptor()->timeEventItems()->get(i)->clone());
}
for (int i = 0; i < this->timeDescriptor()->calendarItems()->rowCount(); i++) {
ret->timeDescriptor()->calendarItems()->addCalendarItem(this->timeDescriptor()->calendarItems()->get(i)->clone());
}
if (this->stateEvaluator()) {
ret->setStateEvaluator(this->stateEvaluator()->clone());
}
for (int i = 0; i < this->actions()->rowCount(); i++) {
ret->actions()->addRuleAction(this->actions()->get(i)->clone());
}

View File

@ -23,6 +23,7 @@ class Rule : public QObject
Q_PROPERTY(TimeDescriptor* timeDescriptor READ timeDescriptor CONSTANT)
public:
explicit Rule(const QUuid &id = QUuid(), QObject *parent = nullptr);
~Rule();
QUuid id() const;
@ -45,7 +46,7 @@ public:
Q_INVOKABLE void createStateEvaluator();
Rule *clone() const;
Q_INVOKABLE Rule *clone() const;
signals:
void nameChanged();

View File

@ -59,3 +59,17 @@ StateEvaluator* StateEvaluator::addChildEvaluator()
m_childEvaluators->addStateEvaluator(stateEvaluator);
return stateEvaluator;
}
StateEvaluator *StateEvaluator::clone() const
{
StateEvaluator *ret = new StateEvaluator();
ret->m_operator = this->m_operator;
ret->m_stateDescriptor->setDeviceId(this->m_stateDescriptor->deviceId());
ret->m_stateDescriptor->setStateTypeId(this->m_stateDescriptor->stateTypeId());
ret->m_stateDescriptor->setValueOperator(this->m_stateDescriptor->valueOperator());
ret->m_stateDescriptor->setValue(this->m_stateDescriptor->value());
for (int i = 0; i < this->m_childEvaluators->rowCount(); i++) {
ret->m_childEvaluators->addStateEvaluator(this->m_childEvaluators->get(i)->clone());
}
return ret;
}

View File

@ -33,6 +33,8 @@ public:
Q_INVOKABLE StateEvaluator* addChildEvaluator();
StateEvaluator* clone() const;
signals:
void stateOperatorChanged();

View File

@ -1,6 +1,20 @@
#include "timedescriptor.h"
#include "timeeventitems.h"
#include "calendaritems.h"
TimeDescriptor::TimeDescriptor(QObject *parent) : QObject(parent)
{
m_timeEventItems = new TimeEventItems(this);
m_calendarItems = new CalendarItems(this);
}
TimeEventItems *TimeDescriptor::timeEventItems() const
{
return m_timeEventItems;
}
CalendarItems *TimeDescriptor::calendarItems() const
{
return m_calendarItems;
}

View File

@ -3,15 +3,28 @@
#include <QObject>
#include <QAbstractListModel>
class TimeEventItems;
class CalendarItems;
class TimeDescriptor : public QObject
{
Q_OBJECT
Q_PROPERTY(TimeEventItems* timeEventItems READ timeEventItems CONSTANT)
Q_PROPERTY(CalendarItems* calendarItems READ calendarItems CONSTANT)
public:
explicit TimeDescriptor(QObject *parent = nullptr);
TimeEventItems* timeEventItems() const;
CalendarItems* calendarItems() const;
signals:
public slots:
private:
TimeEventItems* m_timeEventItems = nullptr;
CalendarItems* m_calendarItems = nullptr;
};
#endif // TIMEDESCRIPTOR_H
#endif // TIMEDESCRIPTOR_H

View File

@ -1,8 +1,11 @@
#include "timeeventitem.h"
TimeEventItem::TimeEventItem(QObject *parent) : QObject(parent)
{
#include "repeatingoption.h"
TimeEventItem::TimeEventItem(QObject *parent):
QObject(parent),
m_repeatingOption(new RepeatingOption(this))
{
}
QDateTime TimeEventItem::dateTime() const
@ -30,3 +33,17 @@ void TimeEventItem::setTime(const QTime &time)
emit timeChanged();
}
}
RepeatingOption *TimeEventItem::repeatingOption() const
{
return m_repeatingOption;
}
TimeEventItem *TimeEventItem::clone() const
{
TimeEventItem* ret = new TimeEventItem();
ret->m_dateTime = this->m_dateTime;
ret->m_time = this->m_time;
ret->m_repeatingOption = this->m_repeatingOption;
return ret;
}

View File

@ -5,11 +5,14 @@
#include <QDateTime>
#include <QTime>
class RepeatingOption;
class TimeEventItem : public QObject
{
Q_OBJECT
Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY dateTimeChanged)
Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged)
Q_PROPERTY(RepeatingOption* repeatingOption READ repeatingOption CONSTANT)
public:
explicit TimeEventItem(QObject *parent = nullptr);
@ -20,6 +23,10 @@ public:
QTime time() const;
void setTime(const QTime &time);
RepeatingOption* repeatingOption() const;
TimeEventItem* clone() const;
signals:
void dateTimeChanged();
void timeChanged();
@ -27,6 +34,7 @@ signals:
private:
QDateTime m_dateTime;
QTime m_time;
RepeatingOption *m_repeatingOption = nullptr;
};

View File

@ -1,6 +1,53 @@
#include "timeeventitems.h"
TimeEventItems::TimeEventItems(QObject *parent) : QObject(parent)
#include "timeeventitem.h"
TimeEventItems::TimeEventItems(QObject *parent):
QAbstractListModel(parent)
{
}
int TimeEventItems::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_list.count();
}
QVariant TimeEventItems::data(const QModelIndex &index, int role) const
{
return QVariant();
}
void TimeEventItems::addTimeEventItem(TimeEventItem *timeEventItem)
{
timeEventItem->setParent(this);
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
m_list.append(timeEventItem);
endInsertRows();
emit countChanged();
}
void TimeEventItems::removeTimeEventItem(int index)
{
if (index < 0 || index > m_list.count()) {
return;
}
beginRemoveRows(QModelIndex(), index, index);
m_list.takeAt(index)->deleteLater();
endRemoveRows();
emit countChanged();
}
TimeEventItem *TimeEventItems::get(int index) const
{
if (index < 0 || index > m_list.count()) {
return nullptr;
}
return m_list.at(index);
}
TimeEventItem *TimeEventItems::createNewTimeEventItem() const
{
return new TimeEventItem();
}

View File

@ -1,17 +1,32 @@
#ifndef TIMEEVENTITEMS_H
#define TIMEEVENTITEMS_H
#include <QObject>
#include <QAbstractListModel>
class TimeEventItems : public QObject
class TimeEventItem;
class TimeEventItems: public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
public:
explicit TimeEventItems(QObject *parent = nullptr);
TimeEventItems(QObject *parent);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
Q_INVOKABLE void addTimeEventItem(TimeEventItem *timeEventItem);
Q_INVOKABLE void removeTimeEventItem(int index);
Q_INVOKABLE TimeEventItem* get(int index) const;
Q_INVOKABLE TimeEventItem* createNewTimeEventItem() const;
signals:
void countChanged();
public slots:
private:
QList<TimeEventItem*> m_list;
};
#endif // TIMEEVENTITEMS_H
#endif // TIMEEVENTITEMS_H

View File

@ -13,7 +13,7 @@ win32:Debug:LIBS += -L$$top_builddir/libmea-core/debug \
win32:Release:LIBS += -L$$top_builddir/libmea-core/release \
-L$$top_builddir/libnymea-common/release
linux:!android:LIBS += -lavahi-client -lavahi-common
PRE_TARGETDEPS += ../libmea-core
PRE_TARGETDEPS += ../libmea-core ../libnymea-common
HEADERS += \
stylecontroller.h

View File

@ -187,5 +187,16 @@
<file>../LICENSE</file>
<file>ui/images/Built_with_Qt_RGB_logo.svg</file>
<file>ui/images/Built_with_Qt_RGB_logo_vertical.svg</file>
<file>ui/magic/TimeEventDelegate.qml</file>
<file>ui/magic/EditTimeEventItemPage.qml</file>
<file>ui/magic/EventDescriptorDelegate.qml</file>
<file>ui/components/MeaDialog.qml</file>
<file>ui/magic/RuleActionDelegate.qml</file>
<file>ui/magic/EditCalendarItemPage.qml</file>
<file>ui/magic/CalendarItemDelegate.qml</file>
<file>ui/images/alarm-clock.svg</file>
<file>ui/images/action.svg</file>
<file>ui/images/event.svg</file>
<file>ui/images/state.svg</file>
</qresource>
</RCC>

View File

@ -23,20 +23,23 @@ Page {
popup.open();
}
onCreateUserFailed: {
print("create user failed")
var text
print("createUser failed")
var message;
switch (error) {
case "UserErrorInvalidUserId":
text = qsTr("The email you've entered isn't valid.");
message = qsTr("The email you've entered isn't valid.")
break;
case "UserErrorDuplicateUserId":
message = qsTr("The email you've entered is already userd.")
break;
case "UserErrorBadPassword":
text = qsTr("The password you've chose is too weak.");
message = qsTr("The password you've chose is too weak.")
break;
case "UserErrorBackendError":
message = qsTr("An error happened with the user storage. Please make sure your %1 box is installed correctly.")
break;
default:
text = qsTr("An error happened creating the user.");
}
// var popup = errorDialog.createObject(root, {title: qsTr("Error creating user"), text: text})
var popup = errorDialog.createObject(root, {title: "Error creating user", text: text})
var popup = errorDialog.createObject(root, {text: message});
popup.open();
}
}

View File

@ -13,30 +13,45 @@ Page {
HeaderButton {
imageSource: Qt.resolvedUrl("images/add.svg")
onClicked: {
var newRulePage = pageStack.push(Qt.resolvedUrl("magic/EditRulePage.qml"), {rule: Engine.ruleManager.createNewRule() });
newRulePage.onAccept.connect(function() {
Engine.ruleManager.addRule(newRulePage.rule);
d.editRulePage = pageStack.push(Qt.resolvedUrl("magic/EditRulePage.qml"), {rule: Engine.ruleManager.createNewRule() });
d.editRulePage.StackView.onRemoved.connect(function() {
d.editRulePage.rule.destroy()
d.editRulePage = null;
})
d.editRulePage.onAccept.connect(function() {
d.editRulePage.busy = true;
Engine.ruleManager.addRule(d.editRulePage.rule);
})
d.editRulePage.onCancel.connect(function() {
pageStack.pop();
})
}
}
}
QtObject {
id: d
property var editRulePage: null
}
Connections {
target: Engine.ruleManager
onAddRuleReply: {
d.editRulePage.busy = false;
if (ruleError == "RuleErrorNoError") {
pageStack.pop();
} else {
var popup = errorDialog.createObject(root, {text: ruleError })
var popup = errorDialog.createObject(app, {errorCode: ruleError })
popup.open();
}
}
onEditRuleReply: {
d.editRulePage.busy = false;
if (ruleError == "RuleErrorNoError") {
pageStack.pop();
} else {
var popup = errorDialog.createObject(root, {text: ruleError })
var popup = errorDialog.createObject(app, {errorCode: ruleError })
popup.open();
}
}
@ -66,10 +81,18 @@ Page {
}
onClicked: {
var editRulePage = pageStack.push(Qt.resolvedUrl("magic/EditRulePage.qml"), {rule: Engine.ruleManager.rules.get(index) })
editRulePage.onAccept.connect(function() {
d.editRulePage = pageStack.push(Qt.resolvedUrl("magic/EditRulePage.qml"), {rule: Engine.ruleManager.rules.get(index).clone()})
d.editRulePage.StackView.onRemoved.connect(function() {
d.editRulePage.rule.destroy();
d.editRulePage = null
})
d.editRulePage.onAccept.connect(function() {
d.editRulePage.busy = true;
Engine.ruleManager.editRule(editRulePage.rule);
})
d.editRulePage.onCancel.connect(function() {
pageStack.pop();
})
}
swipe.right: MouseArea {

View File

@ -22,6 +22,8 @@ ApplicationWindow {
property int iconSize: 30
property int delegateHeight: 60
property bool landscape: app.width > app.height
property var settings: Settings {
property string lastConnectedHost: ""
property int viewMode: ApplicationWindow.Maximized

View File

@ -38,8 +38,9 @@ Page {
target: networkManger.manager
onErrorOccured: {
print("Error occured", errorMessage)
errorDialog.errorText = errorMessage
errorDialog.open()
var errorDialog = Qt.createComponent(Qt.resolvedUrl("components/ErrorDialog.qml"));
var popup = errorDialog.createObject(app, {text: errorMessage})
popup.open()
}
onWirelessStatusChanged: {
@ -287,28 +288,6 @@ Page {
}
Dialog {
id: errorDialog
width: Math.min(parent.width * .9, 400)
x: (parent.width - width) / 2
y: (parent.height - height) / 2
standardButtons: Dialog.Ok
property string errorText
ColumnLayout {
anchors { left: parent.left; right: parent.right; top: parent.top }
spacing: app.margins
Label {
Layout.fillWidth: true
wrapMode: Text.WordWrap
text: errorDialog.errorText
}
}
}
Component {
id: settingsPage

View File

@ -2,51 +2,15 @@ import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
Dialog {
MeaDialog {
id: root
width: Math.min(parent.width * .8, contentLabel.implicitWidth)
x: (parent.width - width) / 2
y: (parent.height - height) / 2
modal: true
title: qsTr("Error")
property alias text: contentLabel.text
title: qsTr("Oh snap!")
headerIcon: "../images/dialog-error-symbolic.svg"
standardButtons: Dialog.Ok
property string errorCode: ""
header: Item {
implicitHeight: headerRow.height + app.margins * 2
implicitWidth: parent.width
RowLayout {
id: headerRow
anchors { left: parent.left; right: parent.right; top: parent.top; margins: app.margins }
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize * 2
Layout.preferredWidth: height
name: "../images/dialog-error-symbolic.svg"
color: app.guhAccent
}
text: qsTr("An unexpected error happened. We're sorry for that.") +
(errorCode.length > 0 ? "\n\n" + qsTr("Error code: %1").arg(errorCode) : "")
Label {
id: titleLabel
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
text: root.title
color: app.guhAccent
font.pixelSize: app.largeFont
}
}
}
ColumnLayout {
id: content
anchors { left: parent.left; top: parent.top; right: parent.right }
Label {
id: contentLabel
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
}
}

View File

@ -0,0 +1,54 @@
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
Dialog {
id: root
width: Math.min(parent.width * .8, Math.max(contentLabel.implicitWidth, 400))
x: (parent.width - width) / 2
y: (parent.height - height) / 2
property alias headerIcon: headerColorIcon.name
property alias text: contentLabel.text
default property alias children: content.children
standardButtons: Dialog.Ok
header: Item {
implicitHeight: headerRow.height + app.margins * 2
implicitWidth: parent.width
RowLayout {
id: headerRow
anchors { left: parent.left; right: parent.right; top: parent.top; margins: app.margins }
spacing: app.margins
ColorIcon {
id: headerColorIcon
Layout.preferredHeight: app.iconSize * 2
Layout.preferredWidth: height
color: app.guhAccent
visible: name.length > 0
}
Label {
id: titleLabel
Layout.fillWidth: true
Layout.margins: app.margins
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
text: root.title
color: app.guhAccent
font.pixelSize: app.largeFont
}
}
}
ColumnLayout {
id: content
anchors { left: parent.left; top: parent.top; right: parent.right }
Label {
id: contentLabel
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
visible: text.length > 0
}
}
}

View File

@ -39,60 +39,72 @@ Item {
model: logsModel
clip: true
onCountChanged: positionViewAtEnd()
delegate: ItemDelegate {
delegate: SwipeDelegate {
id: logEntryDelegate
width: parent.width
implicitHeight: app.delegateHeight
contentItem: RowLayout {
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/event.svg"
color: app.guhAccent
}
ColumnLayout {
Layout.fillWidth: true
RowLayout {
Label {
id: timeStampLabel
Layout.fillWidth: true
ColorIcon {
Layout.preferredHeight: timeStampLabel.height
Layout.preferredWidth: height
name: "../images/clock-app-symbolic.svg"
}
Label {
id: timeStampLabel
Layout.fillWidth: true
text: Qt.formatDateTime(model.timestamp,"dd.MM.yy - hh:mm:ss")
}
text: Qt.formatDateTime(model.timestamp,"dd.MM.yy - hh:mm:ss")
}
RowLayout {
Label {
Layout.fillWidth: true
Label {
text: qsTr("Data:")
}
Label {
Layout.fillWidth: true
text: model.value.trim()
elide: Text.ElideRight
}
text: qsTr("Data: %1").arg(model.value.trim())
elide: Text.ElideRight
font.pixelSize: app.smallFont
}
}
HeaderButton {
imageSource: "../images/magic.svg"
color: {
for (var i = 0; i < rulesFilterModel.count; i++) {
var rule = rulesFilterModel.get(i);
for (var j = 0; j < rule.eventDescriptors.count; j++) {
var eventDescriptor = rule.eventDescriptors.get(j);
if (eventDescriptor.eventTypeId === root.logsModel.typeId) {
var matching = true;
for (var k = 0; k < eventDescriptor.paramDescriptors.count; k++) {
var paramDescriptor = eventDescriptor.paramDescriptors.get(k);
if (paramDescriptor.value === model.value) {
return app.guhAccent;
}
}
}
}
}
return keyColor;
}
// ColorIcon {
// Layout.preferredWidth: app.iconSize
// Layout.preferredHeight: width
// name: "../images/magic.svg"
// color: {
// for (var i = 0; i < rulesFilterModel.count; i++) {
// var rule = rulesFilterModel.get(i);
// for (var j = 0; j < rule.eventDescriptors.count; j++) {
// var eventDescriptor = rule.eventDescriptors.get(j);
// if (eventDescriptor.eventTypeId === root.logsModel.typeId) {
// var matching = true;
// for (var k = 0; k < eventDescriptor.paramDescriptors.count; k++) {
// var paramDescriptor = eventDescriptor.paramDescriptors.get(k);
// if (paramDescriptor.value === model.value) {
// return app.guhAccent;
// }
// }
// }
// }
// }
// return keyColor;
// }
onClicked: root.addRuleClicked(model.value)
// }
}
swipe.right: MouseArea {
height: logEntryDelegate.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/magic.svg"
}
onClicked: root.addRuleClicked(model.value)
}
onClicked: {
if (swipe.complete) {
swipe.close()
} else {
swipe.open(SwipeDelegate.Right)
}
}
}

View File

@ -51,7 +51,7 @@ Page {
popup.open();
return;
default:
var popup = errorDialog.createObject(root, {text: qsTr("Remove device error: %1").arg(JSON.stringify(params.deviceError)) })
var popup = errorDialog.createObject(root, {errorCode: params.deviceError})
popup.open();
}
}

172
mea/ui/images/action.svg Normal file
View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
viewBox="0 0 96 96.000001"
sodipodi:docname="action.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="14.049998"
inkscape:cx="54.082123"
inkscape:cy="46.974137"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:window-width="2880"
inkscape:window-height="1698"
inkscape:window-x="0"
inkscape:window-y="44"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="84,-8.0000001"
id="guide4080"
inkscape:locked="false" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170"
inkscape:locked="false" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172"
inkscape:locked="false" />
<sodipodi:guide
position="92,-8.0000001"
orientation="1,0"
id="guide4760"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<rect
transform="rotate(90)"
y="-28.142855"
x="78.505043"
height="96"
width="96"
id="rect4782-637"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:3.99999976;marker:none;enable-background:accumulate" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect4747-4"
d="m 18.142883,124.50503 1.000025,3.99998 h -32.999999 v -3.99998 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;stroke:none;stroke-width:8.99999905;marker:none;enable-background:accumulate"
inkscape:transform-center-x="-26.500014" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 5.6230724,101.05661 C -4.6712646,90.767855 -20.167327,87.685805 -33.61715,93.253885 c -13.44979,5.56807 -22.23047,18.700765 -22.23047,33.251945 0,14.55118 8.78068,27.68194 22.23047,33.25002 13.449823,5.56804 28.9458854,2.48602 39.2402224,-7.80276 l -2.828107,-2.82811 c -9.158173,9.15322 -22.9166364,11.88745 -34.8828054,6.93358 -11.96618,-4.95383 -19.75979,-16.60879 -19.75979,-29.55273 0,-12.94393 7.79361,-24.60083 19.75979,-29.554705 11.966169,-4.95382 25.7246324,-2.2196 34.8828054,6.933635 z"
id="path4145"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path5837"
d="m 5.1430854,114.50443 0.0076,24 c 3.6500406,-1.66873 7.3659206,-3.53593 11.1491896,-5.59872 3.747742,-2.06778 7.362822,-4.2005 10.84286,-6.40026 -3.480038,-2.15576 -7.095118,-4.26806 -10.84286,-6.33584 -3.785348,-2.06393 -7.503345,-3.95188 -11.1553126,-5.66518 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.99940658;marker:none;enable-background:accumulate" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="0.91+devel r"
viewBox="0 0 96 96.000001"
sodipodi:docname="reminder.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6199993"
inkscape:cx="4.0836252"
inkscape:cy="58.033793"
inkscape:document-units="px"
inkscape:current-layer="g4780"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073" />
<sodipodi:guide
orientation="1,0"
position="92,-8.0000001"
id="guide4075" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076" />
<sodipodi:guide
orientation="0,1"
position="-5,12"
id="guide4078" />
<sodipodi:guide
orientation="1,0"
position="84,-9.0000001"
id="guide4080" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<g
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
id="g4845"
style="display:inline">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="next01.png"
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
id="g4778"
inkscape:label="Layer 1">
<g
transform="matrix(-1,0,0,1,575.99999,611)"
id="g4780"
style="display:inline">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
id="rect4782"
width="96.037987"
height="96"
x="-438.00244"
y="345.36221"
transform="scale(-1,1)" />
<path
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#808080;fill-opacity:1;stroke:none"
d="m 364.0904,368.96573 c -0.0215,-0.0161 -0.0354,-0.0404 -0.0567,-0.0566 -0.0253,-0.0201 -0.057,-0.0305 -0.0821,-0.0508 z"
id="path4157" />
<path
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#808080;fill-opacity:1;stroke:none"
d="m 364.07673,417.80167 -0.13873,0.10742 c 0.0251,-0.0203 0.0569,-0.0307 0.0821,-0.0508 0.0214,-0.0162 0.0353,-0.0405 0.0567,-0.0566 z"
id="path4344" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 417.99454,395.36222 -3.75148,0 -22.5089,0 -3.75149,0 0,-3.99941 3.75149,0 22.5089,0 3.75148,0 0,3.99941 z"
id="path5329"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 391.98425,395.36222 -4.00329,0 0,-3.7143 0,-18.57141 0,-3.71429 4.00329,0 0,3.71429 0,18.57141 0,3.7143 z"
id="path5331"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079107;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 432,393.36133 c 0,23.17267 -18.83507,42 -42.01562,42 -23.18056,0 -42.01758,-18.82733 -42.01758,-42 0,-23.17267 18.83702,-42 42.01758,-42 23.18055,0 42.01562,18.82733 42.01562,42 z m -4.00195,0 c 0,-21.00963 -16.99445,-37.99805 -38.01367,-37.99805 -21.01923,0 -38.01563,16.98842 -38.01563,37.99805 0,21.00963 16.9964,38 38.01563,38 21.01922,0 38.01367,-16.99037 38.01367,-38 z"
id="path4151"
inkscape:connector-curvature="0" />
<path
inkscape:transform-center-y="-28.541779"
sodipodi:nodetypes="zscccsz"
inkscape:connector-curvature="0"
id="path4187"
d="m 427.05076,356.30961 c 9.26681,9.26315 9.26681,13.89473 0,23.15787 l -0.8205,0.82018 c -1.47358,-5.56141 -4.38677,-10.63666 -8.44632,-14.71491 -4.0766,-4.05955 -9.15035,-6.97497 -14.71167,-8.45201 l 0.81144,-0.81113 c 9.26682,-9.26316 13.90023,-9.26316 23.16705,0 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;stroke:none;stroke-width:9;marker:none;enable-background:accumulate"
inkscape:transform-center-x="-28.537261" />
<path
inkscape:transform-center-x="28.541784"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;stroke:none;stroke-width:9;marker:none;enable-background:accumulate"
d="m 427.05076,430.41481 c -9.26682,9.26316 -13.90023,9.26316 -23.16705,0 l -0.82049,-0.82018 c 5.56361,-1.47299 10.64087,-4.38503 14.72072,-8.44297 4.06116,-4.07499 6.97774,-9.14674 8.45537,-14.70585 l 0.81145,0.81112 c 9.26681,9.26315 9.26681,13.89473 0,23.15788 z"
id="path4085"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zscccsz"
inkscape:transform-center-y="-28.537254" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

180
mea/ui/images/event.svg Normal file
View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
viewBox="0 0 96 96.000001"
sodipodi:docname="event.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="28.099996"
inkscape:cx="22.257505"
inkscape:cy="41.601488"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:window-width="2880"
inkscape:window-height="1698"
inkscape:window-x="0"
inkscape:window-y="44"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="84,-8.0000001"
id="guide4080"
inkscape:locked="false" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170"
inkscape:locked="false" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172"
inkscape:locked="false" />
<sodipodi:guide
position="92,-8.0000001"
orientation="1,0"
id="guide4760"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<rect
transform="rotate(90)"
y="-28.142855"
x="78.505043"
height="96"
width="96"
id="rect4782-637"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:3.99999976;marker:none;enable-background:accumulate" />
<g
id="g842"
transform="matrix(-1,0,0,1,-39.714411,2.025e-5)">
<path
inkscape:connector-curvature="0"
id="path4145"
d="M 5.6230724,101.05661 C -4.6712646,90.767855 -20.167327,87.685805 -33.61715,93.253885 c -13.44979,5.56807 -22.23047,18.700765 -22.23047,33.251945 0,14.55118 8.78068,27.68194 22.23047,33.25002 13.449823,5.56804 28.9458854,2.48602 39.2402224,-7.80276 l -2.828107,-2.82811 c -9.158173,9.15322 -22.9166364,11.88745 -34.8828054,6.93358 -11.96618,-4.95383 -19.75979,-16.60879 -19.75979,-29.55273 0,-12.94393 7.79361,-24.60083 19.75979,-29.554705 11.966169,-4.95382 25.7246324,-2.2196 34.8828054,6.933635 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
transform="matrix(-1,0,0,1,13.285644,0)"
id="g836">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect4747-4"
d="m 18.142883,124.50503 1.000025,3.99998 h -32.999999 v -3.99998 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;stroke:none;stroke-width:8.99999905;marker:none;enable-background:accumulate"
inkscape:transform-center-x="-26.500014" />
<path
inkscape:connector-curvature="0"
id="path5837"
d="m 5.1430854,114.50443 0.0076,24 c 3.6500406,-1.66873 7.3659206,-3.53593 11.1491896,-5.59872 3.747742,-2.06778 7.362822,-4.2005 10.84286,-6.40026 -3.480038,-2.15576 -7.095118,-4.26806 -10.84286,-6.33584 -3.785348,-2.06393 -7.503345,-3.95188 -11.1553126,-5.66518 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.99940658;marker:none;enable-background:accumulate" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

19
mea/ui/images/state.svg Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="svg4874" width="96" height="96" version="1.1" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata id="metadata4879">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1" transform="translate(67.857 -78.505)">
<rect id="rect4782-2" transform="rotate(90)" x="78.505" y="-28.143" width="96" height="96" style="color:#000000;fill:none"/>
<path id="path4643-2" d="m-39.869 98.504-0.01134 2e-3c-5.0328 0.0582-8.7136-0.12019-11.725 1.541-1.5055 0.83062-2.6968 2.2356-3.3555 3.9902-0.65866 1.7546-0.89647 3.8364-0.89647 6.4668v48.002c0 2.6304 0.23773 4.7122 0.89647 6.4668 0.65866 1.7546 1.85 3.1596 3.3555 3.9902 3.011 1.6613 6.6918 1.4848 11.725 1.543h0.01134 40.023 0.01134c5.0328-0.0582 8.7136 0.1183 11.725-1.543 1.5055-0.83066 2.6968-2.2356 3.3555-3.9902 0.65866-1.7546 0.8965-3.8364 0.8965-6.4668v-48.002c0-2.6304-0.23773-4.7122-0.8965-6.4668-0.65866-1.7547-1.85-3.1596-3.3555-3.9902-3.011-1.6613-6.6918-1.4829-11.725-1.5411l-0.01134-2e-3 -12.012 2e-3v4l11.977-2e-3c5.0542 0.0586 8.3726 0.23547 9.8398 1.0449 0.73364 0.40479 1.1527 0.85493 1.543 1.8945 0.39024 1.0396 0.64059 2.691 0.64059 5.0606v48.002c0 2.3695-0.2502 4.0209-0.64059 5.0605-0.39027 1.0396-0.80935 1.4898-1.543 1.8945-1.4645 0.80806-4.7782 0.98615-9.8164 1.0449h-39.977-0.02268c-5.0383-0.059-8.3519-0.23697-9.8164-1.0449-0.73364-0.40475-1.1508-0.85489-1.541-1.8945-0.39027-1.0396-0.6426-2.691-0.6426-5.0605v-48.002c0-2.3696 0.25247-4.0209 0.6426-5.0606 0.39024-1.0396 0.80734-1.4897 1.541-1.8945 1.4645-0.80807 4.7782-0.98616 9.8164-1.0449l12 2e-3v-4z" style="color-rendering:auto;color:#000000;fill:#808080;font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;image-rendering:auto;isolation:auto;mix-blend-mode:normal;shape-padding:0;shape-rendering:auto;solid-color:#000000;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-transform:none;white-space:normal"/>
<path id="path4237" d="m-21.858 82.505v50h4v-50z" style="fill:#808080"/>
<path id="path5588-9-2-96-04" d="m-7.8571 124.51-24 8e-3c1.6687 3.6501 3.5359 7.366 5.5987 11.149 2.0678 3.7477 4.2005 7.3628 6.4003 10.843 2.1557-3.48 4.268-7.0951 6.3358-10.843 2.0639-3.7854 3.9519-7.5033 5.6652-11.155z" style="color:#000000;fill:#808080"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,111 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
SwipeDelegate {
id: root
implicitHeight: app.delegateHeight
property var calendarItem: null
readonly property bool isDateBased: calendarItem.repeatingOption.repeatingMode === RepeatingOption.RepeatingModeNone ||
calendarItem.repeatingOption.repeatingMode === RepeatingOption.RepeatingModeYearly
signal removeCalendarItem();
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: "../images/clock-app-symbolic.svg"
color: app.guhAccent
}
ColumnLayout {
Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: qsTr("From %1 to %2")
.arg(root.isDateBased ? Qt.formatDateTime(root.calendarItem.dateTime) : Qt.formatTime(root.calendarItem.startTime))
.arg(root.isDateBased ? Qt.formatDateTime(new Date(root.calendarItem.dateTime.getTime() + root.calendarItem.duration * 60000)) : Qt.formatTime(new Date(root.calendarItem.startTime.getTime() + root.calendarItem.duration * 60000)))
}
Label {
Layout.fillWidth: true
elide: Text.ElideRight
font.pixelSize: app.smallFont
text: qsTr("repeated %3")
.arg(repeatingString)
property string repeatingString: {
switch (root.calendarItem.repeatingOption.repeatingMode) {
case RepeatingOption.RepeatingModeNone:
return qsTr("never");
case RepeatingOption.RepeatingModeHourly:
return qsTr("hourly");
case RepeatingOption.RepeatingModeDaily:
return qsTr("daily");
case RepeatingOption.RepeatingModeWeekly:
var weekdays = []
for (var i = 0; i < root.calendarItem.repeatingOption.weekDays.length; i++) {
switch (root.calendarItem.repeatingOption.weekDays[i]) {
case 1:
weekdays.push(qsTr("Mon"));
break;
case 2:
weekdays.push(qsTr("Tue"));
break;
case 3:
weekdays.push(qsTr("Wed"));
break;
case 4:
weekdays.push(qsTr("Thu"));
break;
case 5:
weekdays.push(qsTr("Fri"));
break;
case 6:
weekdays.push(qsTr("Sat"));
break;
case 7:
weekdays.push(qsTr("Sun"));
break;
}
}
return qsTr("weekly on %1").arg(weekdays.join(', '));
case RepeatingOption.RepeatingModeMonthly:
return qsTr("monthly on the %1").arg(root.calendarItem.repeatingOption.monthDays.join(', '));
case RepeatingOption.RepeatingModeYearly:
return qsTr("every year");
}
}
}
}
}
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.removeCalendarItem()
}
onClicked: {
var page = pageStack.push(Qt.resolvedUrl("EditCalendarItemPage.qml"), {calendarItem: root.calendarItem})
page.onBackPressed.connect(function() {pageStack.pop()})
page.onDone.connect(function() {
pageStack.pop()
print("calendarItem.time is now", root.calendarItem.time)
})
}
}

View File

@ -21,14 +21,23 @@ Page {
}
// Rule is optional and might be initialized with anything wanted. A new, empty one will be created if null
// This Page will take ownership of the rule and delete it eventually.
function addRule(rule) {
if (rule === null || rule === undefined) {
rule = Engine.ruleManager.createNewRule();
}
var page = pageStack.push(Qt.resolvedUrl("EditRulePage.qml"), {rule: rule});
page.onAccept.connect(function() {
d.editRulePage = pageStack.push(Qt.resolvedUrl("EditRulePage.qml"), {rule: rule});
d.editRulePage.StackView.onRemoved.connect(function() {
d.editRulePage.rule.destroy();
d.editRulePage = null
})
d.editRulePage.onAccept.connect(function() {
d.editRulePage.busy = true;
Engine.ruleManager.addRule(page.rule);
})
d.editRulePage.onCancel.connect(function() {
pageStack.pop();
})
// if (rule.eventDescriptors.count === 0) {
// var eventDescriptor = rule.eventDescriptors.createNewEventDescriptor();
@ -38,18 +47,32 @@ Page {
}
QtObject {
id: d
property var editRulePage: null
}
Connections {
target: Engine.ruleManager
onAddRuleReply: {
d.editRulePage.busy = false;
if (ruleError == "RuleErrorNoError") {
pageStack.pop();
} else {
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
var popup = errorDialog.createObject(root, {errorCode: ruleError })
popup.open();
}
}
onEditRuleReply: {
print("have add rule reply")
d.editRulePage.busy = false;
if (ruleError == "RuleErrorNoError") {
pageStack.pop();
} else {
var errorDialog = Qt.createComponent(Qt.resolvedUrl("../components/ErrorDialog.qml"));
var popup = errorDialog.createObject(root, {errorCode: ruleError })
popup.open();
}
}
}
@ -72,7 +95,7 @@ Page {
height: app.iconSize
width: height
name: "../images/magic.svg"
color: !model.enabled ? "gray" : (model.active ? "red" : app.guhAccent)
color: !model.enabled ? "red" : (model.active ? app.guhAccent : "grey")
}
Label {
@ -82,9 +105,16 @@ Page {
}
onClicked: {
var editRulePage = pageStack.push(Qt.resolvedUrl("EditRulePage.qml"), {rule: rulesFilterModel.get(index) })
editRulePage.onAccept.connect(function() {
Engine.ruleManager.editRule(editRulePage.rule);
d.editRulePage = pageStack.push(Qt.resolvedUrl("EditRulePage.qml"), {rule: rulesFilterModel.get(index).clone() })
d.editRulePage.StackView.onRemoved.connect(function() {
d.editRulePage.rule.destroy();
})
d.editRulePage.onAccept.connect(function() {
d.editRulePage.busy = true
Engine.ruleManager.editRule(d.editRulePage.rule);
})
d.editRulePage.onCancel.connect(function() {
pageStack.pop();
})
}

View File

@ -0,0 +1,402 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import Qt.labs.calendar 1.0
import QtQuick.Layouts 1.3
import "../components"
import Mea 1.0
Page {
id: root
property var calendarItem: null
signal done()
signal backPressed()
readonly property bool isDateBased: repeatingBox.currentIndex === 0 ||
repeatingBox.currentIndex === 5
readonly property bool isWeekDayBased: repeatingBox.currentIndex === 3
readonly property bool isMonthDayBased: repeatingBox.currentIndex === 4
header: GuhHeader {
text: qsTr("Pick a time frame")
onBackPressed: root.backPressed();
}
Component.onCompleted: {
var date = root.isDateBased ? root.calendarItem.dateTime : root.calendarItem.startTime
print("starting with time:", root.calendarItem.startTime, root.calendarItem.dateTime)
if (isNaN(date)) {
console.log("Date in rule not valid, using current datetime");
date = new Date();
}
hourBox.currentIndex = date.getHours();
minuteBox.currentIndex = date.getMinutes();
dayBox.currentIndex = date.getDate() - 1;
monthBox.currentIndex = date.getMonth();
print("should set year to", date.getFullYear())
yearBox.currentIndex = date.getFullYear() - 1970;
var endDate = new Date(date.getTime() + root.calendarItem.duration * 60000);
toHourBox.currentIndex = endDate.getHours();
toMinuteBox.currentIndex = endDate.getMinutes();
toDayBox.currentIndex = endDate.getDate() - 1;
toMonthBox.currentIndex = endDate.getMonth();
toYearBox.currentIndex = endDate.getFullYear() - 1970
}
function pad(num, size) {
var s = "000000000" + num;
return s.substr(s.length-size);
}
Flickable {
anchors.fill: parent
contentHeight: mainColumn.implicitHeight
ColumnLayout {
id: mainColumn
anchors { left: parent.left; top: parent.top; right: parent.right }
GridLayout {
columns: app.landscape ? 2 : 1
Layout.alignment: app.landscape ? Qt.AlignHCenter : Qt.AlignLeft
Layout.margins: app.margins
Layout.fillWidth: !app.landscape
Label {
text: qsTr("From")
}
RowLayout {
ComboBox {
id: hourBox
model: {
if (!enabled) {
return ["--"]
}
var ret = [];
for (var i = 0; i < 24; i++) {
ret.push(pad(i, 2));
}
return ret;
}
enabled: repeatingBox.currentIndex !== 1
}
Label {
text: ":"
}
ComboBox {
id: minuteBox
model: {
var ret = [];
for (var i = 0; i < 60; i++) {
ret.push(pad(i, 2));
}
return ret;
}
}
}
RowLayout {
Layout.fillHeight: !app.landscape
Layout.topMargin: app.landscape ? app.margins : 0
visible: root.isDateBased
ComboBox {
id: dayBox
Layout.fillWidth: true
model: {
var ret = [];
for (var i = 1; i < 31; i++) {
ret.push(pad(i, 2));
}
return ret;
}
}
ComboBox {
id: monthBox
Layout.fillWidth: true
model: [
qsTr("Jan"),
qsTr("Feb"),
qsTr("Mar"),
qsTr("Apr"),
qsTr("May"),
qsTr("Jun"),
qsTr("Jul"),
qsTr("Aug"),
qsTr("Sep"),
qsTr("Oct"),
qsTr("Nov"),
qsTr("Dez")
]
}
ComboBox {
id: yearBox
Layout.fillWidth: true
model: {
var ret = [];
for (var i = 1970; i < 2100; i++) {
ret.push(i);
}
return ret;
}
}
}
Label {
text: qsTr("To")
}
RowLayout {
ComboBox {
id: toHourBox
model: {
if (!enabled) {
return ["--"]
}
var ret = [];
for (var i = 0; i < 24; i++) {
ret.push(pad(i, 2));
}
return ret;
}
enabled: repeatingBox.currentIndex !== 1
}
Label {
text: ":"
}
ComboBox {
id: toMinuteBox
model: {
var ret = [];
for (var i = 0; i < 60; i++) {
ret.push(pad(i, 2));
}
return ret;
}
}
}
RowLayout {
Layout.fillHeight: !app.landscape
Layout.topMargin: app.landscape ? app.margins : 0
visible: root.isDateBased
ComboBox {
id: toDayBox
Layout.fillWidth: true
model: {
var ret = [];
for (var i = 1; i < 31; i++) {
ret.push(pad(i, 2));
}
return ret;
}
onActivated: {
var date = root.calendarItem.dateTime
date.setDate(index)
root.calendarItem.dateTime = date;
}
}
ComboBox {
id: toMonthBox
Layout.fillWidth: true
model: [
qsTr("Jan"),
qsTr("Feb"),
qsTr("Mar"),
qsTr("Apr"),
qsTr("May"),
qsTr("Jun"),
qsTr("Jul"),
qsTr("Aug"),
qsTr("Sep"),
qsTr("Oct"),
qsTr("Nov"),
qsTr("Dez")
]
}
ComboBox {
id: toYearBox
Layout.fillWidth: true
model: {
var ret = [];
for (var i = 1970; i < 2100; i++) {
ret.push(i);
}
return ret;
}
}
}
Label {
text: qsTr("Repeat")
Layout.topMargin: app.margins
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: app.landscape ? app.margins : 0
ComboBox {
id: repeatingBox
model: [qsTr("never"), qsTr("hourly"), qsTr("daily"), qsTr("weekly"), qsTr("monthly"), qsTr("yearly")]
currentIndex: {
switch (root.calendarItem.repeatingOption.repeatingMode) {
case RepeatingOption.RepeatingModeNone:
return 0;
case RepeatingOption.RepeatingModeHourly:
return 1;
case RepeatingOption.RepeatingModeDaily:
return 2;
case RepeatingOption.RepeatingModeWeekly:
return 3;
case RepeatingOption.RepeatingModeMonthly:
return 4;
case RepeatingOption.RepeatingModeYearly:
return 5;
}
return 0;
}
}
}
Label {
text: qsTr("Weekdays")
Layout.topMargin: app.margins
visible: root.isWeekDayBased
}
DayOfWeekRow {
id: weekDayRow
property var weekDays: root.calendarItem.repeatingOption.weekDays
visible: root.isWeekDayBased
Layout.fillWidth: !app.landscape
Layout.topMargin: app.landscape ? app.margins : 0
delegate: ToolButton {
text: model.shortName
checked: weekDayRow.weekDays.indexOf(index + 1) >= 0
onClicked: {
var copy = weekDayRow.weekDays
var idx = copy.indexOf(index + 1);
if (idx >= 0) {
copy.splice(idx, 1);
} else {
copy.push(index + 1)
}
weekDayRow.weekDays = copy
}
}
}
Label {
text: qsTr("Day of month")
Layout.topMargin: app.margins
visible: root.isMonthDayBased
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
}
GridLayout {
id: monthDayGrid
columns: Math.sqrt(children.length)
Layout.fillWidth: !app.landscape
Layout.topMargin: app.landscape ? app.margins : 0
visible: root.isMonthDayBased
property var monthDays: root.calendarItem.repeatingOption.monthDays
Repeater {
model: 31
delegate: ToolButton {
Layout.fillWidth: true
checked: monthDayGrid.monthDays.indexOf(index + 1) >= 0
text: modelData + 1
onClicked: {
var copy = monthDayGrid.monthDays
var idx = copy.indexOf(index + 1);
if (idx >= 0) {
copy.splice(idx, 1);
} else {
copy.push(index + 1)
}
monthDayGrid.monthDays = copy
}
}
}
}
}
Button {
Layout.fillWidth: !app.landscape
Layout.margins: app.margins
Layout.alignment: Qt.AlignRight
text: qsTr("OK")
onClicked: {
if (root.isDateBased) {
var date = isNaN(root.calendarItem.dateTime) ? new Date() : root.calendarItem.dateTime
date.setHours(hourBox.currentIndex);
date.setMinutes(minuteBox.currentIndex);
date.setDate(dayBox.currentIndex + 1);
date.setMonth(monthBox.currentIndex);
date.setFullYear(yearBox.currentText);
root.calendarItem.dateTime = date;
var endDate = new Date();
endDate.setHours(toHourBox.currentIndex);
endDate.setMinutes(toMinuteBox.currentIndex);
endDate.setDate(toDayBox.currentIndex + 1);
endDate.setMilliseconds(toMonthBox.currentIndex);
endDate.setFullYear(toYearBox.currentText);
root.calendarItem.duration = (endDate.getTime() - date.getTime()) / 60000;
} else {
var time = isNaN(root.calendarItem.startTime) ? new Date() : root.calendarItem.startTime
time.setHours(hourBox.currentIndex);
time.setMinutes(minuteBox.currentIndex)
root.calendarItem.startTime = time;
var endTime = new Date(time);
endTime.setHours(toHourBox.currentIndex);
endTime.setMinutes(toMinuteBox.currentIndex);
root.calendarItem.duration = (endTime.getTime() - time.getTime()) / 60000;
print("duration is", endTime.getTime() - time.getTime(), time, endTime, toMinuteBox.currentIndex)
}
switch (repeatingBox.currentIndex) {
case 0:
root.calendarItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeNone;
break;
case 1:
root.calendarItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeHourly;
break;
case 2:
root.calendarItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeDaily;
break;
case 3:
root.calendarItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeWeekly;
break;
case 4:
root.calendarItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeMonthly;
break;
case 5:
root.calendarItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeYearly;
break;
}
if (root.isWeekDayBased) {
root.calendarItem.repeatingOption.weekDays = weekDayRow.weekDays;
}
if (root.isMonthDayBased) {
root.calendarItem.repeatingOption.monthDays = monthDayGrid.monthDays;
}
root.done()
}
}
}
}
}

View File

@ -8,15 +8,17 @@ Page {
id: root
property var rule: null
property bool busy: false
signal accept();
onAccept: busyOverlay.opacity = 1
readonly property bool isStateBased: rule.eventDescriptors.count === 0
readonly property bool actionsVisible: rule.eventDescriptors.count > 0 || rule.stateEvaluator !== null
readonly property bool isEventBased: rule.eventDescriptors.count > 0 || rule.timeDescriptor.timeEventItems.count > 0
readonly property bool isStateBased: (rule.stateEvaluator !== null || rule.timeDescriptor.calendarItems.count > 0) && !isEventBased
readonly property bool actionsVisible: !isEmpty
readonly property bool exitActionsVisible: actionsVisible && isStateBased
readonly property bool hasExitActions: rule.exitActions.count > 0
readonly property bool isEmpty: !isEventBased && !isStateBased
signal accept();
signal cancel();
function addEventDescriptor() {
var eventDescriptor = root.rule.eventDescriptors.createNewEventDescriptor();
@ -41,6 +43,32 @@ Page {
})
}
function addTimeEventItem() {
var timeEventItem = root.rule.timeDescriptor.timeEventItems.createNewTimeEventItem();
var page = pageStack.push(Qt.resolvedUrl("EditTimeEventItemPage.qml"), {timeEventItem: timeEventItem});
page.onBackPressed.connect(function() {
pageStack.pop()
timeEventItem.destroy();
})
page.onDone.connect(function() {
root.rule.timeDescriptor.timeEventItems.addTimeEventItem(timeEventItem);
pageStack.pop();
})
}
function addCalendarItem() {
var calendarItem = root.rule.timeDescriptor.calendarItems.createNewCalendarItem();
var page = pageStack.push(Qt.resolvedUrl("EditCalendarItemPage.qml"), {calendarItem: calendarItem});
page.onBackPressed.connect(function() {
pageStack.pop();
calendarItem.destroy();
})
page.onDone.connect(function() {
root.rule.timeDescriptor.calendarItems.addCalendarItem(calendarItem);
pageStack.pop();
})
}
function editStateEvaluator() {
print("opening page", root.rule.stateEvaluator)
var page = pageStack.push(Qt.resolvedUrl("EditStateEvaluatorPage.qml"), { stateEvaluator: root.rule.stateEvaluator })
@ -91,15 +119,14 @@ Page {
}
header: GuhHeader {
text: qsTr("New rule")
onBackPressed: pageStack.pop()
text: root.rule.name.length === 0 ? qsTr("Add new magic") : qsTr("Edit %1").arg(root.rule.name)
onBackPressed: root.cancel()
HeaderButton {
imageSource: "../images/tick.svg"
enabled: actionsRepeater.count > 0 && root.rule.name.length > 0
opacity: enabled ? 1 : .3
onClicked: {
root.accept()
}
onClicked: root.accept()
}
}
@ -109,24 +136,47 @@ Page {
ColumnLayout {
id: contentColumn
anchors { left: parent.left; top: parent.top; right: parent.right; topMargin: app.margins }
anchors { left: parent.left; top: parent.top; right: parent.right; }
ColumnLayout {
id: ruleSettings
Layout.fillWidth: true
Layout.margins: app.margins
Label {
Layout.fillWidth: true
text: qsTr("Rule name")
}
TextField {
Layout.fillWidth: true
text: root.rule.name
onTextChanged: {
root.rule.name = text;
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
Layout.topMargin: app.margins
property bool showDetails: false
}
}
RowLayout {
Layout.fillWidth: true
spacing: app.margins
Label {
text: qsTr("Name:")
}
TextField {
Layout.fillWidth: true
text: root.rule.name
onTextChanged: root.rule.name = text;
}
ColorIcon {
name: "../images/settings.svg"
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
MouseArea {
anchors.fill: parent
onClicked: ruleSettings.showDetails = !ruleSettings.showDetails
}
}
}
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: ruleSettings.showDetails ? implicitHeight : 0
opacity: ruleSettings.showDetails ? 1 : 0
Behavior on Layout.preferredHeight { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad} }
Behavior on opacity { NumberAnimation {duration: 200; easing.type: Easing.InOutQuad } }
Label {
Layout.fillWidth: true
text: qsTr("This rule is enabled")
@ -140,83 +190,49 @@ Page {
}
}
ThinDivider { visible: !root.hasExitActions }
ThinDivider { visible: !root.isStateBased }
Label {
Layout.fillWidth: true
Layout.margins: app.margins
font.pixelSize: app.mediumFont
text: qsTr("Events triggering this rule")
visible: !root.hasExitActions
wrapMode: Text.WordWrap
text: eventsRepeater.count === 0 && timeEventRepeater.count === 0 ?
qsTr("Execute actions when something happens.") :
qsTr("When any of these events happen...")
visible: !root.isStateBased
font.bold: true
}
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
wrapMode: Text.WordWrap
font.pixelSize: app.smallFont
font.italic: true
text: qsTr("Examples:\n• When a button is pressed...\n• When the temperature changes...\n• At 7 am...")
visible: root.isEmpty
}
Repeater {
id: eventsRepeater
model: root.hasExitActions ? null : root.rule.eventDescriptors
delegate: SwipeDelegate {
id: eventDelegate
delegate: EventDescriptorDelegate {
Layout.fillWidth: true
readonly property var eventDescriptor: root.rule.eventDescriptors.get(index)
property var device: Engine.deviceManager.devices.getDevice(eventDescriptor.deviceId)
property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
property var iface: eventDescriptor.interfaceName ? Interfaces.findByName(eventDescriptor.interfaceName) : null
property var eventType: deviceClass ? deviceClass.eventTypes.getEventType(eventDescriptor.eventTypeId)
: iface ? iface.eventTypes.findByName(eventDescriptor.interfaceEvent) : null
contentItem: ColumnLayout {
Label {
text: qsTr("%1 - %2").arg(eventDelegate.device ? eventDelegate.device.name : eventDelegate.iface.displayName).arg(eventDelegate.eventType.displayName)
Layout.fillWidth: true
elide: Text.ElideRight
}
RowLayout {
Layout.fillWidth: true
spacing: app.margins
Repeater {
model: eventDelegate.eventDescriptor.paramDescriptors
Label {
text: {
var ret = eventDelegate.eventType.paramTypes.getParamType(model.id).displayName
switch (model.operator) {
case ParamDescriptor.ValueOperatorEquals:
ret += " = ";
break;
case ParamDescriptor.ValueOperatorNotEquals:
ret += " != ";
break;
case ParamDescriptor.ValueOperatorGreater:
ret += " > ";
break;
case ParamDescriptor.ValueOperatorGreaterOrEqual:
ret += " >= ";
break;
case ParamDescriptor.ValueOperatorLess:
ret += " < ";
break;
case ParamDescriptor.ValueOperatorLessOrEqual:
ret += " <= ";
break;
default:
ret += " ? ";
}
eventDescriptor: root.rule.eventDescriptors.get(index)
onRemoveEventDescriptor: root.rule.eventDescriptors.removeEventDescriptor(index)
}
}
ret += model.value
return ret;
}
}
}
}
}
swipe.right: MouseArea {
height: eventDelegate.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.rule.eventDescriptors.removeEventDescriptor(index)
Repeater {
id: timeEventRepeater
model: root.rule.timeDescriptor.timeEventItems
delegate: TimeEventDelegate {
Layout.fillWidth: true
timeEventItem: root.rule.timeDescriptor.timeEventItems.get(index)
onRemoveTimeEventItem: {
root.rule.timeDescriptor.timeEventItems.removeTimeEventItem(index);
}
}
}
@ -224,18 +240,58 @@ Page {
Button {
Layout.fillWidth: true
Layout.margins: app.margins
text: eventsRepeater.count == 0 ? qsTr("Add an event...") : qsTr("Add another event...")
onClicked: root.addEventDescriptor();
visible: !root.hasExitActions
text: eventsRepeater.count == 0 && timeEventRepeater.count === 0 ? qsTr("Configure...") : qsTr("Add another...")
visible: !root.isStateBased
onClicked: {
if (root.rule.timeDescriptor.calendarItems.count > 0) {
root.addEventDescriptor()
} else {
var popup = eventQuestionDialogComponent.createObject(root)
popup.open();
}
}
}
ThinDivider {}
Label {
text: qsTr("Conditions to be met")
text: root.isEmpty ?
qsTr("Do something while a condition is met.") :
root.isEventBased ?
qsTr("...but only if those conditions are met...") :
qsTr("When this condition...")
font.pixelSize: app.mediumFont
Layout.fillWidth: true
Layout.margins: app.margins
font.bold: true
visible: {
if (root.isEventBased) {
if (root.rule.timeDescriptor.calendarItems.count === 0) {
return true;
}
if (root.rule.stateEvaluator === null) {
return false;
}
} else {
if (root.rule.stateEvaluator === null && root.rule.timeDescriptor.calendarItems.count > 0) {
return false;
}
}
return true;
}
}
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
wrapMode: Text.WordWrap
font.pixelSize: app.smallFont
font.italic: true
text: qsTr("Examples:\n• While I'm at home...\n• When the temperature is below 0...\n• Between 9 am and 6 pm...")
visible: root.isEmpty
}
StateEvaluatorDelegate {
@ -244,70 +300,68 @@ Page {
visible: root.rule.stateEvaluator !== null
}
Label {
text: root.rule.stateEvaluator === null && !root.isEventBased ?
qsTr("When time is in...") :
qsTr("...during this time...")
font.pixelSize: app.mediumFont
Layout.fillWidth: true
Layout.margins: app.margins
font.bold: true
visible: root.rule.timeDescriptor.timeEventItems.count === 0 && (root.rule.timeDescriptor.calendarItems.count > 0 || root.rule.stateEvaluator !== null)
}
Repeater {
model: root.rule.timeDescriptor.calendarItems
delegate: CalendarItemDelegate {
Layout.fillWidth: true
calendarItem: root.rule.timeDescriptor.calendarItems.get(index)
onRemoveCalendarItem: {
root.rule.timeDescriptor.calendarItems.removeCalendarItem(index)
}
}
}
Button {
Layout.fillWidth: true
Layout.margins: app.margins
text: qsTr("Add a condition")
visible: root.rule.stateEvaluator === null
text: root.rule.stateEvaluator === null && root.rule.timeDescriptor.calendarItems.count === 0 ?
qsTr("Configure...") :
qsTr("Add another...")
visible: root.rule.timeDescriptor.timeEventItems.count === 0 || root.rule.stateEvaluator === null
onClicked: {
root.rule.createStateEvaluator();
// root.editStateEvaluator()
if (root.rule.timeDescriptor.timeEventItems.count > 0) {
root.rule.createStateEvaluator()
} else if (root.rule.stateEvaluator !== null) {
root.addCalendarItem();
} else {
var popup = stateQuestionDialogComponent.createObject(root)
popup.open()
}
}
}
ThinDivider { visible: root.actionsVisible }
Label {
text: root.isStateBased ? qsTr("Active state enter actions") : qsTr("Actions to execute")
text: root.isStateBased ?
(root.rule.stateEvaluator === 0 ? qsTr("...come true, execute those actions:") : qsTr("...comes true, execute those actions:")) :
qsTr("...execute those actions:")
font.pixelSize: app.mediumFont
Layout.fillWidth: true
Layout.margins: app.margins
wrapMode: Text.WordWrap
visible: root.actionsVisible
font.bold: true
}
Repeater {
id: actionsRepeater
model: root.actionsVisible ? root.rule.actions : null
delegate: SwipeDelegate {
id: actionDelegate
delegate: RuleActionDelegate {
Layout.fillWidth: true
property var ruleAction: root.rule.actions.get(index)
property var device: ruleAction.deviceId ? Engine.deviceManager.devices.getDevice(ruleAction.deviceId) : null
property var iface: ruleAction.interfaceName ? Interfaces.findByName(ruleAction.interfaceName) : null
property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
property var actionType: deviceClass ? deviceClass.actionTypes.getActionType(ruleAction.actionTypeId)
: iface ? iface.actionTypes.findByName(ruleAction.interfaceAction) : null
contentItem: ColumnLayout {
Label {
Layout.fillWidth: true
text: qsTr("%1 - %2").arg(actionDelegate.device ? actionDelegate.device.name : actionDelegate.iface.displayName).arg(actionDelegate.actionType.displayName)
}
RowLayout {
Layout.fillWidth: true
spacing: app.margins
Repeater {
model: actionDelegate.ruleAction.ruleActionParams
Label {
text: actionDelegate.actionType.paramTypes.getParamType(model.paramTypeId).displayName + " -> " +
(model.eventParamTypeId.length > 0 ? qsTr("value from event") : model.value)
font.pixelSize: app.smallFont
}
}
}
}
swipe.right: MouseArea {
height: actionDelegate.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.rule.actions.removeRuleAction(index)
}
ruleAction: root.rule.actions.get(index)
onRemoveRuleAction: root.rule.actions.removeRuleAction(index)
}
}
@ -322,56 +376,23 @@ Page {
ThinDivider { visible: root.exitActionsVisible }
Label {
text: qsTr("Active state exit actions")
font.pixelSize: app.mediumFont
text: qsTr("...isn't met any more, execute those actions:")
Layout.fillWidth: true
Layout.margins: app.margins
wrapMode: Text.WordWrap
visible: root.exitActionsVisible
font.pixelSize: app.mediumFont
font.bold: true
}
Repeater {
id: exitActionsRepeater
model: root.exitActionsVisible ? root.rule.exitActions : null
delegate: SwipeDelegate {
id: exitActionDelegate
delegate: RuleActionDelegate {
Layout.fillWidth: true
property var ruleAction: root.rule.exitActions.get(index)
property var device: ruleAction.deviceId ? Engine.deviceManager.devices.getDevice(ruleAction.deviceId) : null
property var iface: ruleAction.interfaceName ? Interfaces.findByName(ruleAction.interfaceName) : null
property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
property var actionType: deviceClass ? deviceClass.actionTypes.getActionType(ruleAction.actionTypeId)
: iface ? iface.actionTypes.findByName(ruleAction.interfaceAction) : null
contentItem: ColumnLayout {
Label {
Layout.fillWidth: true
text: qsTr("%1 - %2").arg(exitActionDelegate.device ? exitActionDelegate.device.name : exitActionDelegate.iface.displayName).arg(exitActionDelegate.actionType.displayName)
}
RowLayout {
Layout.fillWidth: true
spacing: app.margins
Repeater {
model: exitActionDelegate.ruleAction.ruleActionParams
Label {
text: exitActionDelegate.actionType.paramTypes.getParamType(model.paramTypeId).displayName + " -> " + model.value
font.pixelSize: app.smallFont
}
}
}
}
swipe.right: MouseArea {
height: exitActionDelegate.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.rule.exitActions.removeRuleAction(index)
}
ruleAction: root.rule.exitActions.get(index)
onClicked: root.rule.exitActions.removeRuleAction(index)
}
}
@ -389,11 +410,145 @@ Page {
id: busyOverlay
anchors.fill: parent
color: "#55000000"
opacity: 0
opacity: root.busy ? 1 : 0
Behavior on opacity { NumberAnimation {duration: 200 } }
BusyIndicator {
anchors.centerIn: parent
running: parent.opacity > 0
}
}
Component {
id: eventQuestionDialogComponent
MeaDialog {
id: questionDialog
title: qsTr("Add event...")
standardButtons: Dialog.Cancel
Button {
Layout.fillWidth: true
Layout.preferredHeight: (app.largeFont * 2) + (app.margins * 3)
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/event.svg"
color: "black"
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("When one of my things triggers an event")
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
}
}
onClicked: {
root.addEventDescriptor()
questionDialog.close()
}
}
Label {
text: qsTr("or")
Layout.fillWidth: true
Layout.margins: app.margins
horizontalAlignment: Text.AlignHCenter
}
Button {
Layout.fillWidth: true
Layout.preferredHeight: (app.largeFont * 2) + (app.margins * 3)
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/alarm-clock.svg"
color: "black"
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("At a particular time or date")
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
}
}
onClicked: {
root.addTimeEventItem()
questionDialog.close()
}
}
}
}
Component {
id: stateQuestionDialogComponent
MeaDialog {
id: questionDialog
title: qsTr("Add condition...")
standardButtons: Dialog.Cancel
Button {
Layout.fillWidth: true
Layout.preferredHeight: (app.largeFont * 2) + (app.margins * 3)
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/state.svg"
color: "black"
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("When one of my things is in a certain state")
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
}
}
onClicked: {
root.rule.createStateEvaluator()
questionDialog.close()
}
}
Label {
text: qsTr("or")
Layout.fillWidth: true
Layout.margins: app.margins
horizontalAlignment: Text.AlignHCenter
}
Button {
Layout.fillWidth: true
Layout.preferredHeight: (app.largeFont * 2) + (app.margins * 3)
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/clock-app-symbolic.svg"
color: "black"
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("During a given time")
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
}
}
onClicked: {
root.addCalendarItem()
questionDialog.close()
}
}
}
}
}

View File

@ -0,0 +1,301 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import Qt.labs.calendar 1.0
import QtQuick.Layouts 1.3
import "../components"
import Mea 1.0
Page {
id: root
property var timeEventItem: null
signal done()
signal backPressed()
readonly property bool isDateBased: repeatingBox.currentIndex === 0 ||
repeatingBox.currentIndex === 5
readonly property bool isWeekDayBased: repeatingBox.currentIndex === 3
readonly property bool isMonthDayBased: repeatingBox.currentIndex === 4
header: GuhHeader {
text: qsTr("Pick a time")
onBackPressed: root.backPressed();
}
Component.onCompleted: {
var date = root.isDateBased ? root.timeEventItem.dateTime : root.timeEventItem.time
print("starting with time:", root.timeEventItem.time, root.timeEventItem.dateTime)
if (isNaN(date)) {
console.log("Date in rule not valid, using current datetime");
date = new Date();
}
hourBox.currentIndex = date.getHours();
minuteBox.currentIndex = date.getMinutes();
dayBox.currentIndex = date.getDate() - 1;
monthBox.currentIndex = date.getMonth();
print("should set year to", date.getFullYear())
yearBox.currentIndex = date.getFullYear() - 1970;
}
function pad(num, size) {
var s = "000000000" + num;
return s.substr(s.length-size);
}
Flickable {
anchors.fill: parent
contentHeight: mainColumn.implicitHeight
ColumnLayout {
id: mainColumn
anchors { left: parent.left; top: parent.top; right: parent.right }
GridLayout {
columns: app.landscape ? 2 : 1
Layout.alignment: app.landscape ? Qt.AlignHCenter : Qt.AlignLeft
Layout.margins: app.margins
Layout.fillWidth: !app.landscape
Label {
text: qsTr("Time")
}
RowLayout {
ComboBox {
id: hourBox
model: {
if (!enabled) {
return ["--"]
}
var ret = [];
for (var i = 0; i < 24; i++) {
ret.push(pad(i, 2));
}
return ret;
}
enabled: repeatingBox.currentIndex !== 1
}
Label {
text: ":"
}
ComboBox {
id: minuteBox
model: {
var ret = [];
for (var i = 0; i < 60; i++) {
ret.push(pad(i, 2));
}
return ret;
}
}
}
Label {
text: qsTr("Repeat")
Layout.topMargin: app.margins
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: app.landscape ? app.margins : 0
ComboBox {
id: repeatingBox
model: [qsTr("never"), qsTr("hourly"), qsTr("daily"), qsTr("weekly"), qsTr("monthly"), qsTr("yearly")]
currentIndex: {
switch (root.timeEventItem.repeatingOption.repeatingMode) {
case RepeatingOption.RepeatingModeNone:
return 0;
case RepeatingOption.RepeatingModeHourly:
return 1;
case RepeatingOption.RepeatingModeDaily:
return 2;
case RepeatingOption.RepeatingModeWeekly:
return 3;
case RepeatingOption.RepeatingModeMonthly:
return 4;
case RepeatingOption.RepeatingModeYearly:
return 5;
}
return 0;
}
}
}
Label {
text: qsTr("Date")
Layout.topMargin: app.margins
visible: root.isDateBased
}
RowLayout {
Layout.fillHeight: !app.landscape
Layout.topMargin: app.landscape ? app.margins : 0
visible: root.isDateBased
ComboBox {
id: dayBox
Layout.fillWidth: true
model: {
var ret = [];
for (var i = 1; i < 31; i++) {
ret.push(pad(i, 2));
}
return ret;
}
}
ComboBox {
id: monthBox
Layout.fillWidth: true
model: [
qsTr("Jan"),
qsTr("Feb"),
qsTr("Mar"),
qsTr("Apr"),
qsTr("May"),
qsTr("Jun"),
qsTr("Jul"),
qsTr("Aug"),
qsTr("Sep"),
qsTr("Oct"),
qsTr("Nov"),
qsTr("Dez")
]
}
ComboBox {
id: yearBox
Layout.fillWidth: true
model: {
var ret = [];
for (var i = 1970; i < 2100; i++) {
ret.push(i);
}
return ret;
}
}
}
Label {
text: qsTr("Weekdays")
Layout.topMargin: app.margins
visible: root.isWeekDayBased
}
DayOfWeekRow {
id: weekDayRow
property var weekDays: root.timeEventItem.repeatingOption.weekDays
visible: root.isWeekDayBased
Layout.fillWidth: !app.landscape
Layout.topMargin: app.landscape ? app.margins : 0
delegate: ToolButton {
text: model.shortName
checked: weekDayRow.weekDays.indexOf(index + 1) >= 0
onClicked: {
var copy = weekDayRow.weekDays
var idx = copy.indexOf(index + 1);
if (idx >= 0) {
copy.splice(idx, 1);
} else {
copy.push(index + 1)
}
weekDayRow.weekDays = copy
}
}
}
Label {
text: qsTr("Day of month")
Layout.topMargin: app.margins
visible: root.isMonthDayBased
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
}
GridLayout {
id: monthDayGrid
columns: Math.sqrt(children.length)
Layout.fillWidth: !app.landscape
Layout.topMargin: app.landscape ? app.margins : 0
visible: root.isMonthDayBased
property var monthDays: root.timeEventItem.repeatingOption.monthDays
Repeater {
model: 31
delegate: ToolButton {
Layout.fillWidth: true
checked: monthDayGrid.monthDays.indexOf(index + 1) >= 0
text: modelData + 1
onClicked: {
var copy = monthDayGrid.monthDays
var idx = copy.indexOf(index + 1);
if (idx >= 0) {
copy.splice(idx, 1);
} else {
copy.push(index + 1)
}
monthDayGrid.monthDays = copy
}
}
}
}
}
Button {
Layout.fillWidth: !app.landscape
Layout.margins: app.margins
Layout.alignment: Qt.AlignRight
text: qsTr("OK")
onClicked: {
if (root.isDateBased) {
var date = isNaN(root.timeEventItem.dateTime) ? new Date() : root.timeEventItem.dateTime
date.setHours(hourBox.currentIndex);
date.setMinutes(minuteBox.currentIndex);
date.setDate(dayBox.currentIndex + 1);
date.setMonth(monthBox.currentIndex);
date.setYear(yearBox.currentText);
root.timeEventItem.dateTime = date;
} else {
var time = isNaN(root.timeEventItem.time) ? new Date() : root.timeEventItem.time
time.setHours(hourBox.currentIndex);
time.setMinutes(minuteBox.currentIndex)
root.timeEventItem.time = time;
}
switch (repeatingBox.currentIndex) {
case 0:
root.timeEventItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeNone;
break;
case 1:
root.timeEventItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeHourly;
break;
case 2:
root.timeEventItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeDaily;
break;
case 3:
root.timeEventItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeWeekly;
break;
case 4:
root.timeEventItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeMonthly;
break;
case 5:
root.timeEventItem.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeYearly;
break;
}
if (root.isWeekDayBased) {
root.timeEventItem.repeatingOption.weekDays = weekDayRow.weekDays;
}
if (root.isMonthDayBased) {
root.timeEventItem.repeatingOption.monthDays = monthDayGrid.monthDays;
}
root.done()
}
}
}
}
}

View File

@ -0,0 +1,99 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
SwipeDelegate {
id: root
implicitHeight: app.delegateHeight
property var eventDescriptor: null
readonly property var device: eventDescriptor ? Engine.deviceManager.devices.getDevice(eventDescriptor.deviceId) : null
readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
readonly property var iface: eventDescriptor.interfaceName ? Interfaces.findByName(eventDescriptor.interfaceName) : null
readonly property var eventType: deviceClass ? deviceClass.eventTypes.getEventType(eventDescriptor.eventTypeId)
: iface ? iface.eventTypes.findByName(eventDescriptor.interfaceEvent) : null
signal removeEventDescriptor()
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: "../images/event.svg"
color: app.guhAccent
}
ColumnLayout {
Label {
text: qsTr("%1 - %2").arg(root.device ? root.device.name : root.iface.displayName).arg(root.eventType.displayName)
Layout.fillWidth: true
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
elide: Text.ElideRight
font.pixelSize: app.smallFont
text: {
var ret = qsTr("anytime");
for (var i = 0; i < root.eventDescriptor.paramDescriptors.count; i++) {
var paramDescriptor = root.eventDescriptor.paramDescriptors.get(i)
var operatorString;
switch (paramDescriptor.operatorType) {
case ParamDescriptor.ValueOperatorEquals:
operatorString = " = ";
break;
case ParamDescriptor.ValueOperatorNotEquals:
operatorString = " != ";
break;
case ParamDescriptor.ValueOperatorGreater:
operatorString = " > ";
break;
case ParamDescriptor.ValueOperatorGreaterOrEqual:
operatorString = " >= ";
break;
case ParamDescriptor.ValueOperatorLess:
operatorString = " < ";
break;
case ParamDescriptor.ValueOperatorLessOrEqual:
operatorString = " <= ";
break;
default:
operatorString = " ? ";
}
if (i === 0) {
// TRANSLATORS: example: "only if temperature > 5"
ret = qsTr("only if %1 %2 %3")
.arg(root.eventType.paramTypes.getParamType(paramDescriptor.paramTypeId).displayName)
.arg(operatorString)
.arg(paramDescriptor.value)
} else {
// TRANSLATORS: example: "and temperature > 5"
ret += " " + qsTr("and %1 %2 %3")
.arg(root.eventType.paramTypes.getParamType(paramDescriptor.paramTypeId).displayName)
.arg(operatorString)
.arg(model.value)
}
}
return ret;
}
}
}
}
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.removeEventDescriptor()
}
}

View File

@ -0,0 +1,66 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
SwipeDelegate {
id: root
implicitHeight: app.delegateHeight
property var ruleAction: null
property var device: ruleAction.deviceId ? Engine.deviceManager.devices.getDevice(ruleAction.deviceId) : null
property var iface: ruleAction.interfaceName ? Interfaces.findByName(ruleAction.interfaceName) : null
property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
property var actionType: deviceClass ? deviceClass.actionTypes.getActionType(ruleAction.actionTypeId)
: iface ? iface.actionTypes.findByName(ruleAction.interfaceAction) : null
signal removeRuleAction()
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: "../images/action.svg"
color: app.guhAccent
}
ColumnLayout {
Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: qsTr("%1 - %2").arg(root.device ? root.device.name : root.iface.displayName).arg(root.actionType.displayName)
}
Label {
Layout.fillWidth: true
elide: Text.ElideRight
font.pixelSize: app.smallFont
text: {
var ret = [];
for (var i = 0; i < root.ruleAction.ruleActionParams.count; i++) {
var ruleActionParam = root.ruleAction.ruleActionParams.get(i)
var paramString = qsTr("%1: %2")
.arg(root.actionType.paramTypes.getParamType(ruleActionParam.paramTypeId).displayName)
.arg(ruleActionParam.eventParamTypeId.length > 0 ? qsTr("value from event") : ruleActionParam.value)
ret.push(paramString)
}
return ret.join(', ')
}
}
}
}
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.removeRuleAction()
}
}

View File

@ -96,6 +96,7 @@ Page {
anchors.fill: parent
anchors.margins: app.margins
text: eventDescriptorParamsFilterModel.device.name + " - " + eventDescriptorParamsFilterModel.eventType.displayName + " - " + eventDescriptorParamsFilterModel.paramDescriptor.displayName
elide: Text.ElideRight
}
}

View File

@ -0,0 +1,109 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
SwipeDelegate {
id: root
implicitHeight: app.delegateHeight
property var timeEventItem: null
readonly property bool isDateBased: timeEventItem.repeatingOption.repeatingMode === RepeatingOption.RepeatingModeNone ||
timeEventItem.repeatingOption.repeatingMode === RepeatingOption.RepeatingModeYearly
signal removeTimeEventItem();
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: "../images/alarm-clock.svg"
color: app.guhAccent
}
ColumnLayout {
Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: qsTr("At %1").arg(root.isDateBased ? Qt.formatDateTime(root.timeEventItem.dateTime) : Qt.formatTime(root.timeEventItem.time))
}
Label {
Layout.fillWidth: true
text: qsTr("repeated %1").arg(repeatingString)
elide: Text.ElideRight
font.pixelSize: app.smallFont
property string repeatingString: {
switch (root.timeEventItem.repeatingOption.repeatingMode) {
case RepeatingOption.RepeatingModeNone:
return qsTr("never");
case RepeatingOption.RepeatingModeHourly:
return qsTr("hourly");
case RepeatingOption.RepeatingModeDaily:
return qsTr("daily");
case RepeatingOption.RepeatingModeWeekly:
var weekdays = []
for (var i = 0; i < root.timeEventItem.repeatingOption.weekDays.length; i++) {
switch (root.timeEventItem.repeatingOption.weekDays[i]) {
case 1:
weekdays.push(qsTr("Mon"));
break;
case 2:
weekdays.push(qsTr("Tue"));
break;
case 3:
weekdays.push(qsTr("Wed"));
break;
case 4:
weekdays.push(qsTr("Thu"));
break;
case 5:
weekdays.push(qsTr("Fri"));
break;
case 6:
weekdays.push(qsTr("Sat"));
break;
case 7:
weekdays.push(qsTr("Sun"));
break;
}
}
return qsTr("weekly on %1").arg(weekdays.join(', '));
case RepeatingOption.RepeatingModeMonthly:
return qsTr("monthly on the %1").arg(root.timeEventItem.repeatingOption.monthDays.join(', '));
case RepeatingOption.RepeatingModeYearly:
return qsTr("every year");
}
}
}
}
}
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.removeTimeEventItem()
}
onClicked: {
var page = pageStack.push(Qt.resolvedUrl("EditTimeEventItemPage.qml"), {timeEventItem: root.timeEventItem})
page.onBackPressed.connect(function() {pageStack.pop()})
page.onDone.connect(function() {
pageStack.pop()
print("timeeventItem.time is now", root.timeEventItem.time)
})
}
}

View File

@ -30,7 +30,7 @@ Page {
pageStack.pop();
} else {
console.warn("Error saving plugin params:", JSON.stringify(params))
var dialog = errorDialog.createObject(root, {title: "Error", text: qsTr("Error saving params: ") + JSON.stringify(params.params.deviceError)});
var dialog = errorDialog.createObject(root, {errorCode: params.params.deviceError});
dialog.open();
}
}