slint/examples/iot-dashboard/iot-dashboard.60
2021-05-21 19:51:00 +02:00

444 lines
13 KiB
Text

/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */
/*
The design from this file is inspired from the design in
https://github.com/peter-ha/qskinny/tree/iot-dashboard/examples/iot-dashboard
Original license:
/****************************************************************************
**
** Copyright 2021 Edelhirsch Software GmbH. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of the copyright holder nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
****************************************************************************/
*/
struct Palette := {
menuBar : brush,
mainContent : brush,
box : brush,
lightDisplay : brush,
pieChart : brush,
roundButton : brush,
weekdayBox : brush,
text : brush,
shadow : brush,
}
global Skin := {
property<bool> day: true;
property<Palette> palette : day ? {
menuBar : #6D7BFB,
mainContent : #fbfbfb,
box : #ffffff,
lightDisplay : #ffffff,
pieChart : #ffffff,
roundButton : #f7f7f7,
weekdayBox : #f4f4f4,
text : #000,
shadow : #0001, // ### added alpha
} : {
menuBar : #2937A7,
mainContent : #040404,
box : #000000,
lightDisplay : #000000,
pieChart : #000000,
roundButton : #0a0a0a,
weekdayBox : #0c0c0c,
text : #fff,
shadow : #fff1, // ### added alpha
};
property<float> f: 1.5; // FIXME: why do the font looks completely different with the same size on the QML demo
// From Skin::initHints in Skin.cpp
property <length> DefaultFont: 12pt * f;
property <length> TinyFont: 9pt * f;
property <length> SmallFont: 10pt * f;
property <length> MediumFont: 13pt * f;
property <length> LargeFont: 20pt * f;
property <length> HugeFont: 27pt * f; // (also, bold)
property <length> TitleFont: 10pt * f; // (also, bold)
}
export Clock := VerticalLayout {
property <string> time <=> time_label.text;
Text {
text: "Current time";
font_size: Skin.TitleFont;
font_weight: 700;
}
time_label := Text {
// FIXME: actual time
text: "10:02:45";
font_size: Skin.HugeFont;
font_weight: 700;
color: #6776FF;
}
}
PieChartPainted := Rectangle {
property <brush> brush <=> p.stroke;
property <float> progress;
back := Path {
width: 100%;
height: 100%;
stroke-width: parent.width / 6;
stroke: #aaaaaa40;
viewbox-width: 100;
viewbox-height: 100;
MoveTo {
x: 50;
y: 0;
}
ArcTo {
radius-x: 50;
radius-y: 50;
x: 50;
y: 100;
sweep: true;
large_arc: true;
}
MoveTo { x: 50; y: 100; }
ArcTo {
radius-x: 50;
radius-y: 50;
x: 50;
y: 0;
sweep: true;
large_arc: true;
}
}
p := Path {
width: 100%;
height: 100%;
stroke-width: parent.width / 6;
viewbox-width: 100;
viewbox-height: 100;
MoveTo {
x: 50;
y: 0;
}
ArcTo {
radius-x: 50;
radius-y: 50;
y: 50 - 50*cos(-progress * 360deg);
x: 50 - 50*sin(-progress * 360deg);
sweep: true;
large-arc: progress > 0.5;
}
}
}
// From TopBar.cpp
export TopBar := HorizontalLayout {
padding: 5px;
spacing: 0px;
for item in [
{ string: "Living Room", progress: 25, value: 175, color: #ff3122, gradient: @linear-gradient(0deg, #FF5C00, #FF3122) },
{ string: "Bedroom", progress: 45, value: 205, color: #6776ff, gradient: @linear-gradient(0deg, #6776FF, #6100FF) },
{ string: "Bathroom", progress: 15, value: 115, color: #f99055, gradient: @linear-gradient(0deg, #FFCE50, #FF3122) },
{ string: "Kitchen", progress: 86, value: 289, color: #6776ff, gradient: @linear-gradient(0deg, #6776FF, #6100FF) },
] : VerticalLayout {
padding: 0px;
spacing: 0px;
Text {
font_size: Skin.SmallFont;
text: item.string;
}
HorizontalLayout {
PieChartPainted {
brush: item.gradient;
progress: item.progress / 100;
Text {
width: 100%;
height: 100%;
vertical_alignment: center;
horizontal_alignment: center;
text: item.progress + "%";
color: item.color;
font_size: Skin.TinyFont;
}
}
VerticalLayout {
Text { text: item.value; }
Text { text: "kwH"; }
}
Rectangle {}
}
}
@children
}
// From Box.cpp
Box := Rectangle {
property <string> title;
background: Skin.palette.box;
drop-shadow-offset-x: 6px;
drop-shadow-offset-y: 6px;
drop-shadow-blur: 6px;
drop-shadow-color: Skin.palette.shadow;
VerticalLayout {
if (root.title != "") : Text {
text <=> root.title;
font_size: Skin.TitleFont;
}
padding: 15px;
@children
}
}
// From RoundedIcon.cpp
RoundedIcon := Rectangle {
property <bool> isBright;
property <bool> isSmall;
property <image> iconName <=> m_graphicLabel.source;
background: isBright ? @linear-gradient(180deg, #ff7d34, #ff3122) : @linear-gradient(180deg, #6776FF, #6100FF);
border-radius: 6px;
height: isSmall ? 60px : 68px;
width: isSmall ? 60px : 68px;
m_graphicLabel := Image {
x: (parent.width - width) / 2;
y: (parent.height - height) / 2;
}
}
//from Usage.cpp
UsageSpacer := Text {
text: "_____";
font_size: Skin.SmallFont;
color: #dddddd;
horizontal_stretch: 2;
}
export Usage := Box {
title: "Usage";
HorizontalLayout {
Text { text: "Usage Today"; font_size: Skin.SmallFont; }
UsageSpacer { }
Text { text: "0,5 kwH"; font_size: Skin.SmallFont; }
}
HorizontalLayout {
Text { text: "Usage this month"; font_size: Skin.SmallFont; }
UsageSpacer { }
Text { text: "60 kwH"; font_size: Skin.SmallFont; }
}
HorizontalLayout {
Text { text: "Total working hours"; font_size: Skin.SmallFont; }
UsageSpacer { }
Text { text: "125 hrs"; font_size: Skin.SmallFont; }
}
}
// From UpAndDownButton.cpp
RoundButton := Image { //### QskPushButton
property <bool> is_up; // ### QskAspect
Image {
source: is_up ? @image-url("images/up.svg") : @image-url("images/down.svg");
x: (parent.width - width) / 2;
y: (parent.height - height) / 2;
}
}
UpAndDownButton := Rectangle {
// FIXME: this is actually on the RoundButton
border-radius: 30px;
width: 45px;
background: Skin.palette.roundButton;
VerticalLayout {
RoundButton { is_up: true; }
RoundButton { is_up: false; }
}
}
// From BoxWithButtons.cpp
ButtonValueLabel := Text {
property <string> value <=> text;
font_size: Skin.HugeFont;
font_weight: 700;
color: #929cb2;
}
TitleAndValueBox := VerticalLayout { horizontal-stretch: 100; }
BoxWithButtons := Box {
property <image> iconFile <=> ri.iconName; //### in original, this is derived from title
property <string> value <=> val.value;
property <bool> isBright <=> ri.isBright;
property <string> title_ <=> titleLabel.text;
HorizontalLayout {
padding: 8px;
spacing: 20px;
VerticalLayout { // FIXME: there is no such layout in the original, we should so without it
alignment: center;
ri := RoundedIcon { }
}
TitleAndValueBox {
titleLabel := Text {
font_size: Skin.TitleFont;
font_weight: 700;
}
val := ButtonValueLabel { }
}
UpAndDownButton { }
}
}
export IndoorTemperature := BoxWithButtons {
title_: "Indoor Temperature";
iconFile: @image-url("images/indoor-temperature.png");
value: "+24";
isBright: true;
}
export Humidity := BoxWithButtons {
property <string> humidity_percent <=> value;
title_: "Humidity";
iconFile: @image-url("images/humidity.png");
value: "30%";
isBright: false;
}
// from MyDevices.cpp
Device := VerticalLayout {
property <string> name <=> t.text;
property <image> iconName <=> ri.iconName; // ### based on the name in the original
property <bool> isBright <=> ri.isBright;
HorizontalLayout { // ### FIXME: this latour is not in the original
alignment: center;
ri := RoundedIcon {
opacity: 0.15;
}
}
t := Text {
font_size: Skin.TinyFont;
horizontal_alignment: center;
}
}
export MyDevices := Box {
title: "My devices";
GridLayout {
spacing: 15px;
Row {
Device{
name: "Lamps";
iconName: @image-url("images/lamps.png");
isBright: true;
}
Device{
name: "Music System";
iconName: @image-url("images/music-system.png");
isBright: false;
}
}
Row {
Device{
name: "AC";
iconName: @image-url("images/ac.png");
isBright: false;
}
Device{
name: "Router";
iconName: @image-url("images/router.png");
isBright: true;
}
}
}
}
// ## FIXME
export UsageDiagram := Box {
Rectangle {}
}
// From LightIntensity.cpp
// ## FIXME
export LightIntensity := Box {
title: "Light intensity";
Rectangle {}
}
// From MenuBar.cpp
MenuItem := Rectangle {
property <image> icon <=> i.source;
property<string> name <=> t.text;
property<bool> active;
background: active ? rgba(100%, 100%, 100%, 14%) : ma.has_hover ? rgba(100%, 100%, 100%, 9%) : transparent;
ma := TouchArea {}
HorizontalLayout {
alignment: start;
padding: 8px;
padding-left: 30px;
padding-right: 30px;
i := Image { }
t := Text { color: white; }
}
}
// From MenuBar.cpp
export MenuBar := Rectangle {
background: Skin.palette.menuBar;
property<int> active: 0;
minimum-width: 140px;
VerticalLayout {
padding-left: 0px;
padding-top: 35px;
padding-right: 0px;
padding-bottom: 12px;
spacing: 8px;
Image {
source: @image-url("images/main-icon.png");
}
//### In the original, the icon is derived from the name
for entry[idx] in [
{ name: "Dashboard", icon: @image-url("images/dashboard.png") },
{ name: "Rooms", icon: @image-url("images/rooms.png") },
{ name: "Devices", icon: @image-url("images/devices.png") },
{ name: "Statistics", icon: @image-url("images/statistics.png") },
{ name: "Storage", icon: @image-url("images/storage.png") },
{ name: "Members", icon: @image-url("images/members.png") },
] : MenuItem {
name: entry.name;
icon: entry.icon;
active: root.active == idx;
}
Rectangle {}
MenuItem { name: "Logout"; icon: @image-url("images/logout.png"); }
}
}