UI Basics Tutorial
Build interactive user interfaces with Grey
Intermediate • Time: 30 minutesContents
Introduction
Welcome to the GreyOS UI Basics tutorial! In this guide, you'll learn how to create interactive user interfaces using the Grey programming language. Building on what you learned in the Hello World tutorial, you'll now explore how to construct graphical applications that users can interact with.
By the end of this tutorial, you'll understand:
- How to use Grey's UI components library
- How to structure layouts for responsive applications
- How to handle user events and interactions
- How to apply styling and themes to your application
Prerequisites
Before you begin, make sure you have:
- Completed the Hello World Tutorial
- Basic understanding of Grey syntax and concepts
- A GreyOS account with access to the Grey Shell
Note: This tutorial builds on concepts introduced in the Hello World tutorial. If you haven't completed it yet, we recommend starting there.
Step 1: UI Components
Grey's UI system is built around symbolic components that represent various interface elements. Let's start by exploring the basic components:
Text Component
// Import the UI module import { UI } from "greyos.ui"; // Create a text component let greeting = UI.Text { content: "Hello, GreyOS User!", style: { fontSize: "18px", color: "#ffffff" } };
Button Component
// Create a button component let submitButton = UI.Button { text: "Click Me", style: { background: "linear-gradient(135deg, #0062cc, #0084ff)", padding: "10px 20px", borderRadius: "4px" }, onClick: function() { print("Button clicked!"); } };
Input Component
// Create an input component let nameInput = UI.Input { placeholder: "Enter your name", type: "text", style: { padding: "8px", borderRadius: "4px", border: "1px solid rgba(0, 132, 255, 0.5)" }, onChange: function(value) { print("Input changed: " + value); } };
Key concept: UI components in Grey are symbolic structures that combine appearance and behavior in a single, cohesive unit.
Step 2: Layout Structure
Now that you understand basic components, let's see how to arrange them in layouts:
Container Component
// Create a container to hold other components let mainContainer = UI.Container { direction: "vertical", spacing: "10px", padding: "20px", children: [ greeting, nameInput, submitButton ], style: { background: "rgba(0, 20, 40, 0.7)", borderRadius: "8px", maxWidth: "500px" } };
Grid Layout
// Create a grid layout let gridLayout = UI.Grid { columns: 2, gap: "15px", children: [ UI.Text { content: "Name:" }, nameInput, UI.Text { content: "Action:" }, submitButton ], style: { padding: "15px", background: "rgba(0, 30, 60, 0.5)" } };
Important: All UI components must be properly nested within a container before rendering. Orphaned components will not be displayed.
Step 3: Event Handling
Interactive UIs require event handlers to respond to user actions. Let's look at how to implement them:
Button Click Event
// Create a button with a click handler let actionButton = UI.Button { text: "Submit Form", onClick: function() { let name = nameInput.getValue(); greeting.update({ content: "Hello, " + name + "!" }); notify("Form submitted!"); } };
Input Change Event
// Create an input with real-time validation let emailInput = UI.Input { placeholder: "Enter your email", type: "email", onChange: function(value) { if (value.includes("@") && value.includes(".")) { this.setStyle({ borderColor: "green" }); } else { this.setStyle({ borderColor: "red" }); } } };
Custom Events
// Create a custom event system let themeToggle = UI.Switch { value: false, onChange: function(isOn) { if (isOn) { UI.applyTheme("dark"); eventBus.emit("theme-changed", "dark"); } else { UI.applyTheme("light"); eventBus.emit("theme-changed", "light"); } } }; // Listen for theme changes eventBus.on("theme-changed", function(theme) { print("Theme changed to: " + theme); });
Pro tip: Grey's event system is fully reactive and propagates changes automatically through the UI hierarchy.
Step 4: Creating a Complete UI
Now let's put everything together to create a complete application:
// Import required modules import { UI } from "greyos.ui"; import { App } from "greyos.app"; // Define the application symbolic UserProfileApp { // App state state: { username: "", email: "", bio: "", theme: "dark" }, // UI Components components: { form: UI.Container { direction: "vertical", spacing: "15px", children: [ UI.Text { content: "User Profile", style: { fontSize: "24px", fontWeight: "bold" } }, UI.Input { id: "username", placeholder: "Username", onChange: function(value) { UserProfileApp.state.username = value; } }, UI.Input { id: "email", placeholder: "Email", type: "email", onChange: function(value) { UserProfileApp.state.email = value; } }, UI.TextArea { id: "bio", placeholder: "Tell us about yourself", rows: 4, onChange: function(value) { UserProfileApp.state.bio = value; } }, UI.Row { children: [ UI.Text { content: "Dark theme:" }, UI.Switch { value: true, onChange: function(isOn) { UserProfileApp.state.theme = isOn ? "dark" : "light"; UserProfileApp.updateTheme(); } } ] }, UI.Button { text: "Save Profile", style: { background: "linear-gradient(135deg, #0062cc, #0084ff)", padding: "10px", width: "100%" }, onClick: function() { UserProfileApp.saveProfile(); } } ], style: { padding: "20px", background: "rgba(0, 20, 40, 0.7)", borderRadius: "8px", maxWidth: "500px", margin: "0 auto" } } }, // Methods saveProfile: function() { print("Saving profile..."); print("Username: " + this.state.username); print("Email: " + this.state.email); print("Bio: " + this.state.bio); print("Theme: " + this.state.theme); UI.notify({ message: "Profile saved successfully!", type: "success", duration: 3000 }); }, updateTheme: function() { UI.applyTheme(this.state.theme); }, // App initialization initialize: function() { this.updateTheme(); return this.components.form; } } // Initialize and render the app App.render(UserProfileApp.initialize());
This complete example demonstrates:
- State management within a symbolic structure
- Component composition and nesting
- Event handling with business logic
- Application initialization and rendering
To run this application, save it to a file named profile_app.grey
and execute it with:
grey run profile_app.grey
User Profile
Next Steps
Congratulations! You've now learned the basics of building user interfaces with Grey. Here are some suggestions for continuing your journey:
- Enhance your application with more complex layouts and interactions
- Experiment with advanced UI components like tables, charts, and animations
- Learn about data binding and state management for larger applications
- Check out the Universal Absorption Tutorial to learn how to integrate existing code into your Grey applications
Remember: UI in Grey is symbolic, meaning components represent both their visual appearance and behavior together. This unified approach allows for automatic optimization and adaptation to different platforms.