mirror of
				https://github.com/slint-ui/slint.git
				synced 2025-10-31 12:04:33 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			176 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| // Copyright © SixtyFPS GmbH <info@slint.dev>
 | |
| // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
 | |
| 
 | |
| import { MaterialPalette, MaterialFontSettings } from "styling.slint";
 | |
| 
 | |
| export component Switch {
 | |
|     in property <string> text <=> i-label.text;
 | |
|     in property <bool> enabled: true;
 | |
|     out property <bool> has-focus: i-focus-scope.has-focus;
 | |
|     in-out property <bool> checked;
 | |
| 
 | |
|     callback toggled;
 | |
| 
 | |
|     function toggle-checked() {
 | |
|         if(!root.enabled) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         root.checked = !root.checked;
 | |
|         root.toggled();
 | |
|     }
 | |
| 
 | |
|     min-height: max(32px, i-layout.min-height);
 | |
|     vertical-stretch: 0;
 | |
|     horizontal-stretch: 0;
 | |
|     forward-focus: i-focus-scope;
 | |
|     accessible-enabled: root.enabled;
 | |
|     accessible-label <=> i-label.text;
 | |
|     accessible-checkable: true;
 | |
|     accessible-checked <=> root.checked;
 | |
|     accessible-role: switch;
 | |
|     accessible-action-default => {
 | |
|         root.checked = !root.checked;
 | |
|         root.toggled();
 | |
|     }
 | |
| 
 | |
|     states [
 | |
|         disabled-selected when !root.enabled && root.checked  : {
 | |
|             i-label.opacity: 0.38;
 | |
|             i-label.color: MaterialPalette.accent-background;
 | |
|             track.opacity: 0.12;
 | |
|             i-handle.opacity: 0.38;
 | |
|             i-handle.width: 24px;
 | |
|             i-handle.x: track.width - 28px - 4px;
 | |
|         }
 | |
|         disabled when !root.enabled : {
 | |
|             i-label.opacity: 0.38;
 | |
|             i-label.color: MaterialPalette.accent-background;
 | |
|             track.opacity: 0.12;
 | |
|             i-handle.opacity: 0.38;
 | |
|         }
 | |
|         pressed when i-touch-area.pressed && !root.checked : {
 | |
|             state-layer.opacity: 0.12;
 | |
|             i-handle.background: MaterialPalette.control-foreground-variant;
 | |
|             i-handle.width: 28px;
 | |
|             i-handle.x: track.border-width;
 | |
|         }
 | |
|         pressed-selected when i-touch-area.pressed && root.checked : {
 | |
|             state-layer.opacity: 0.12;
 | |
|             state-layer.background: MaterialPalette.control-foreground;
 | |
|             track.background: MaterialPalette.accent-background;
 | |
|             track.border-color: MaterialPalette.accent-background;
 | |
|             i-handle.background: MaterialPalette.accent-container;
 | |
|             i-handle.width: 28px;
 | |
|             i-handle.x: track.width - 28px - 4px;
 | |
|         }
 | |
|         hover-selected when i-touch-area.has-hover && root.checked : {
 | |
|             state-layer.opacity: 0.08;
 | |
|             track.background: MaterialPalette.accent-background;
 | |
|             track.border-color: MaterialPalette.accent-background;
 | |
|             i-handle.background: MaterialPalette.accent-container;
 | |
|             i-handle.width: 24px;
 | |
|             i-handle.x: track.width - 24px - 4px;
 | |
|         }
 | |
|         hover when i-touch-area.has-hover && !root.checked : {
 | |
|             state-layer.background: MaterialPalette.control-foreground;
 | |
|             state-layer.opacity: 0.08;
 | |
|             i-handle.background: MaterialPalette.control-foreground-variant;
 | |
|         }
 | |
|         selected when !i-touch-area.has-hover && root.checked : {
 | |
|             track.background: MaterialPalette.accent-background;
 | |
|             track.border-color: MaterialPalette.accent-background;
 | |
|             i-handle.background: MaterialPalette.accent-container;
 | |
|             i-handle.width: 24px;
 | |
|             i-handle.x: track.width - 24px - 4px;
 | |
|         }
 | |
|         focused-selected when i-focus-scope.has-focus && root.checked : {
 | |
|             state-layer.opacity: 0.12;
 | |
|             track.background: MaterialPalette.accent-background;
 | |
|             track.border-color: MaterialPalette.accent-background;
 | |
|             i-handle.background: MaterialPalette.accent-container;
 | |
|             i-handle.width: 24px;
 | |
|             i-handle.x: track.width - 24px - 4px;
 | |
|         }
 | |
|         focused when i-focus-scope.has-focus && !root.checked : {
 | |
|             state-layer.background: MaterialPalette.control-foreground;
 | |
|             state-layer.opacity: 0.12;
 | |
|         }
 | |
|     ]
 | |
| 
 | |
|     i-layout := VerticalLayout {
 | |
|         alignment: center;
 | |
| 
 | |
|         HorizontalLayout {
 | |
|             spacing: 16px;
 | |
| 
 | |
|             Rectangle {
 | |
|                 width: 52px;
 | |
|                 height: 32px;
 | |
| 
 | |
|                 track := Rectangle {
 | |
|                     border-radius: 16px;
 | |
|                     border-width: 2px;
 | |
|                     border-color: MaterialPalette.border;
 | |
|                     background: MaterialPalette.control-background-variant;
 | |
|                 }
 | |
| 
 | |
|                 i-handle := Rectangle {
 | |
|                     x: 8px;
 | |
|                     y: (parent.height - self.height) / 2;
 | |
|                     width: 16px;
 | |
|                     height: self.width;
 | |
|                     border-radius: self.width / 2;
 | |
|                     background: MaterialPalette.border;
 | |
| 
 | |
|                     state-layer := Rectangle {
 | |
|                         width: 40px;
 | |
|                         height: 40px;
 | |
|                         x: (parent.width - self.width) / 2;
 | |
|                         y: (parent.height - self.height) / 2;
 | |
|                         opacity: 0;
 | |
|                         background: MaterialPalette.accent-background;
 | |
|                         border-radius: 20px;
 | |
| 
 | |
|                         animate opacity { duration: 300ms; easing: ease; }
 | |
|                     }
 | |
| 
 | |
|                     animate background, width, x { duration: 75ms; easing: linear; }
 | |
|                 }
 | |
| 
 | |
|                 animate background, border-color{ duration: 75ms; easing: linear; }
 | |
|             }
 | |
| 
 | |
|             i-label := Text {
 | |
|                 color: MaterialPalette.control-foreground;
 | |
|                 horizontal-alignment: left;
 | |
|                 vertical-alignment: center;
 | |
|                 vertical-stretch: 0;
 | |
|                 font-size: MaterialFontSettings.title-small.font-size;
 | |
|                 font-weight: MaterialFontSettings.title-small.font-weight;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     i-touch-area := TouchArea {
 | |
|         enabled <=> root.enabled;
 | |
| 
 | |
|         clicked => {
 | |
|             root.toggle-checked();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     i-focus-scope := FocusScope {
 | |
|         x: 0;
 | |
|         width: 0px; // Do not react on clicks
 | |
|         enabled <=> root.enabled;
 | |
| 
 | |
|         key-pressed(event) => {
 | |
|             if (event.text == " " || event.text == "\n") {
 | |
|                  root.toggle-checked();
 | |
|                  return accept;
 | |
|             }
 | |
|             return reject;
 | |
|         }
 | |
|     }
 | |
| }
 | 
