/* LICENSE BEGIN This file is part of the SixtyFPS Project -- https://sixtyfps.io Copyright (c) 2020 Olivier Goffart Copyright (c) 2020 Simon Hausmann 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 day: true; property 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 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 DefaultFont: 12pt * f; property TinyFont: 9pt * f; property SmallFont: 10pt * f; property MediumFont: 13pt * f; property LargeFont: 20pt * f; property HugeFont: 27pt * f; // (also, bold) property TitleFont: 10pt * f; // (also, bold) } export Clock := VerticalLayout { property 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 <=> p.stroke; property progress; back := Path { width: 100%; height: 100%; stroke-width: parent.width / 6; stroke: #aaaaaa40; 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; } // FIXME: would be nice if there was another way to define the bounding box of the path MoveTo { x: 0; y: 0; } MoveTo { x: 100; y: 100; } } p := Path { width: 100%; height: 100%; stroke-width: parent.width / 6; 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; } // FIXME: would be nice if there was another way to define the bounding box of the path MoveTo { x: 0; y: 0; } MoveTo { x: 100; y: 100; } } } // 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 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 isBright; property isSmall; property 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 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 value <=> text; font_size: Skin.HugeFont; font_weight: 700; color: #929cb2; } TitleAndValueBox := VerticalLayout { horizontal-stretch: 100; } BoxWithButtons := Box { property iconFile <=> ri.iconName; //### in original, this is derived from title property value <=> val.value; property isBright <=> ri.isBright; property 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 humidity_percent <=> value; title_: "Humidity"; iconFile: @image-url("images/humidity.png"); value: "30%"; isBright: false; } // from MyDevices.cpp Device := VerticalLayout { property name <=> t.text; property iconName <=> ri.iconName; // ### based on the name in the original property 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 icon <=> i.source; property name <=> t.text; property 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 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"); } } }