mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00

Let's keep source compatibility and define `drop-shadow-blur` to be a radius. The CSS spec says that the standard deviation is half of the radius. We just need to scale again and increase the shadow rect to make sure that no borders are visible.
458 lines
13 KiB
Text
458 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;
|
|
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 <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"); }
|
|
}
|
|
}
|