| 1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
| 2 | // SPDX-License-Identifier: MIT |
| 3 | |
| 4 | global Utils { |
| 5 | public pure function MapRange(value: length, inMin: length, inMax: length, outMin: length, outMax: length) -> length { |
| 6 | return clamp(outMin, (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin, outMax); |
| 7 | } |
| 8 | } |
| 9 | component SunMoonThumb { |
| 10 | in property <float> scale: 1; |
| 11 | in property <length> thumb-position: 50px * scale; |
| 12 | |
| 13 | |
| 14 | Rectangle { |
| 15 | width: 200px * scale; |
| 16 | height: 100px * scale; |
| 17 | border-radius: self.height / 2; |
| 18 | clip: true; |
| 19 | Rectangle { |
| 20 | width: 0px; |
| 21 | height: 0px; |
| 22 | x: root.thumb-position; |
| 23 | Rectangle { |
| 24 | width: 260px * scale; |
| 25 | height: self.width; |
| 26 | background: white; |
| 27 | border-radius: self.width / 2; |
| 28 | opacity: 0.05; |
| 29 | } |
| 30 | Rectangle { |
| 31 | width: 200px * scale; |
| 32 | height: self.width; |
| 33 | background: white; |
| 34 | border-radius: self.width / 2; |
| 35 | opacity: 0.05; |
| 36 | } |
| 37 | Rectangle { |
| 38 | width: 140px * scale; |
| 39 | height: self.width; |
| 40 | background: white; |
| 41 | border-radius: self.width / 2; |
| 42 | opacity: 0.05; |
| 43 | } |
| 44 | |
| 45 | clipper :=Rectangle { |
| 46 | width: 85px * scale; |
| 47 | height: self.width; |
| 48 | border-radius: self.width / 2; |
| 49 | clip: true; |
| 50 | |
| 51 | sun := Rectangle { |
| 52 | width: 85px * scale; |
| 53 | height: self.width; |
| 54 | background: @radial-gradient(circle,#ffce08 0%, #fdd224 80%, #fce37f 100%); |
| 55 | border-radius: self.width / 2; |
| 56 | } |
| 57 | |
| 58 | moon := Rectangle { |
| 59 | x: Utils.MapRange(root.thumb-position, 50px * scale, 100px * scale, 85px * scale, 0px); |
| 60 | width: 85px * scale; |
| 61 | height: self.width; |
| 62 | background: @radial-gradient(circle,#bcbcbc 0%, #e7e7e7 80%, #ffffff 100%); |
| 63 | border-radius: self.width / 2; |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | |
| 71 | |
| 72 | enum Theme { day, night } |
| 73 | export component SunMoonSwitch { |
| 74 | property <Theme> theme: Theme.night; |
| 75 | in property <float> scale: 1; |
| 76 | width: 200px * scale; |
| 77 | height: 100px * scale; |
| 78 | |
| 79 | frameBacker := Rectangle { |
| 80 | width: parent.width; |
| 81 | height: parent.height; |
| 82 | background: #1e2232; |
| 83 | border-radius: self.height / 2; |
| 84 | } |
| 85 | |
| 86 | |
| 87 | Rectangle { |
| 88 | width: parent.width; |
| 89 | height: parent.height; |
| 90 | clip: true; |
| 91 | border-radius: self.height / 2; |
| 92 | |
| 93 | clouds-background :=Image { |
| 94 | x: 14px * scale; |
| 95 | y: -6px * scale; |
| 96 | width: 202px * scale; |
| 97 | source: @image-url("images/clouds-background.png" ); |
| 98 | } |
| 99 | clouds-foreground := Image { |
| 100 | x: 30px * scale; |
| 101 | y: 5px * scale; |
| 102 | width: 202px * scale; |
| 103 | source: @image-url("images/clouds-front.png" ); |
| 104 | } |
| 105 | |
| 106 | stars := Image { |
| 107 | x: 15px * scale; |
| 108 | y: 15px * scale; |
| 109 | width: 80px * scale; |
| 110 | source: @image-url("images/stars.png" ); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | |
| 115 | |
| 116 | Image { |
| 117 | x: -1px * scale; |
| 118 | width: 202px * scale; |
| 119 | source: @image-url("images/shadow-frame.png" ); |
| 120 | } |
| 121 | |
| 122 | thumb := SunMoonThumb { |
| 123 | scale: root.scale; |
| 124 | } |
| 125 | |
| 126 | TouchArea { |
| 127 | clicked => { |
| 128 | if root.theme == Theme.day { |
| 129 | root.theme = Theme.night; |
| 130 | } else { |
| 131 | root.theme = Theme.day; |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | states [ |
| 137 | nightMode when root.theme == Theme.night: { |
| 138 | thumb.thumb-position: root.width - thumb.width - 50px * scale; |
| 139 | frameBacker.background: #1e2232; |
| 140 | clouds-background.y: 120px; |
| 141 | clouds-foreground.y: 120px; |
| 142 | in { |
| 143 | animate frameBacker.background, stars.y, stars.opacity { |
| 144 | easing: ease-out-sine; |
| 145 | duration: 200ms; |
| 146 | } |
| 147 | animate thumb.thumb-position { |
| 148 | easing: cubic-bezier(0.61, 0.21, 0.68, 1.22); |
| 149 | duration: 300ms; |
| 150 | } |
| 151 | animate clouds-background.y, clouds-foreground.y { |
| 152 | easing: ease-in-sine; |
| 153 | duration: 150ms; |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | dayMode when root.theme == Theme.day: { |
| 158 | thumb.thumb-position: 50px * scale; |
| 159 | frameBacker.background: #3d85ba; |
| 160 | stars.y: - 60px * scale; |
| 161 | stars.opacity: 0.4; |
| 162 | in { |
| 163 | animate frameBacker.background, stars.y, stars.opacity { |
| 164 | easing: ease-out-sine; |
| 165 | duration: 200ms; |
| 166 | } |
| 167 | animate thumb.thumb-position { |
| 168 | easing: cubic-bezier(0.61, 0.21, 0.68, 1.22); |
| 169 | duration: 300ms; |
| 170 | } |
| 171 | animate clouds-background.y { |
| 172 | easing: ease-out-sine; |
| 173 | duration: 300ms; |
| 174 | } |
| 175 | animate clouds-foreground.y { |
| 176 | easing: cubic-bezier(0.61, 0.21, 0.68, 1.22); |
| 177 | duration: 350ms; |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | ] |
| 182 | } |
| 183 | |