From f53a187d282e7011fb84d99199f9e72ec996e4fc Mon Sep 17 00:00:00 2001 From: Roman Sedaikin Date: Fri, 16 Oct 2020 12:32:24 +0300 Subject: [PATCH] Added tutorials for window management, tray, notifier, menu bar. --- .gitignore | 2 + .../TrayNotifierMenuBar.md | 188 +++++++++++++ tutorials/Window_API/WindowManagement.md | 262 ++++++++++++++++++ 3 files changed, 452 insertions(+) create mode 100755 tutorials/Tray_Notifications_MenuBar/TrayNotifierMenuBar.md create mode 100755 tutorials/Window_API/WindowManagement.md diff --git a/.gitignore b/.gitignore index c2b13ed18a..0d380d087a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.DS_Store + # Compiled class file *.class diff --git a/tutorials/Tray_Notifications_MenuBar/TrayNotifierMenuBar.md b/tutorials/Tray_Notifications_MenuBar/TrayNotifierMenuBar.md new file mode 100755 index 0000000000..5788ada821 --- /dev/null +++ b/tutorials/Tray_Notifications_MenuBar/TrayNotifierMenuBar.md @@ -0,0 +1,188 @@ +# Getting Started with Compose for Desktop + +## What is covered + +In this guide, we'll show you how to work with system tray, create application menu bar and create window specific menu bar, and send system notifications using Compose for Desktop. + +## Tray + +You can add an application icon into the system tray. Using Tray, you can also send notifications to the user. There are 3 types of notifications: + +1. notify - simple notification +2. warn - warning notification +3. error - error notification + +```kotlin +import androidx.compose.desktop.AppManager +import androidx.compose.desktop.Window +import androidx.compose.runtime.onActive +import androidx.compose.runtime.onDispose +import androidx.compose.ui.window.Item +import androidx.compose.ui.window.Tray + +fun main() { + Window { + onActive { + val tray = Tray().apply { + icon(getImageIcon()) // custom function that returns BufferedImage + menu( + Item( + name = "About", + onClick = { + println("This is MyApp") + } + ), + Item( + name = "Send notification", + onClick = { + tray.notify("Notification", "Message from MyApp!") + } + ), + Item( + name = "Exit", + onClick = { + AppManager.exit() + } + ) + ) + } + onDispose { + tray.remove() + } + } + } +} +``` + +## Notifier +You can send system notifications with Notifier without using the system tray. +Notifier also has 3 types of notifications: + +1. notify - simple notification +2. warn - warning notification +3. error - error notification + +```kotlin +import androidx.compose.desktop.Window +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.Text +import androidx.compose.material.Button +import androidx.compose.ui.window.Notifier + +fun main() { + val message = "Some message!" + Window { + Column { + Button(onClick = { Notifier().notify("Notification.", message) }) { + Text(text = "Notify") + } + Button(onClick = { Notifier().warn("Warning.", message) }) { + Text(text = "Warning") + } + Button(onClick = { Notifier().error("Error.", message) }) { + Text(text = "Error") + } + } + } +} +``` + +## MenuBar + +MenuBar is used to create and customize the common context menu of the application or any particular window. +To create a common context menu for all application windows, you need to configure the AppManager. + +```kotlin +import androidx.compose.desktop.AppManager +import androidx.compose.desktop.Window +import androidx.compose.ui.window.Item +import androidx.compose.ui.window.keyStroke +import androidx.compose.ui.window.Menu +import androidx.compose.ui.window.MenuBar +import java.awt.event.KeyEvent + +fun main() { + AppManager.menu( + MenuBar( + Menu( + name = "Actions", + Item( + name = "About", + onClick = { println("This is MyApp") }, + shortcut = keyStroke(KeyEvent.VK_I) + ), + Item( + name = "Exit", + onClick = { AppManager.exit() }, + shortcut = keyStroke(KeyEvent.VK_X) + ) + ), + Menu( + name = "File", + Item( + name = "Copy", + onClick = { println("Copy operation.") }, + shortcut = keyStroke(KeyEvent.VK_C) + ), + Item( + name = "Paste", + onClick = { println("Paste operation.") }, + shortcut = keyStroke(KeyEvent.VK_V) + ) + ) + ) + ) + + Window { + // content + } +} +``` + +You can to create a MenuBar for a specific window (the rest of the windows will use the common MenuBar, if defined). + +```kotlin +import androidx.compose.desktop.AppManager +import androidx.compose.desktop.Window +import androidx.compose.ui.window.Item +import androidx.compose.ui.window.keyStroke +import androidx.compose.ui.window.Menu +import androidx.compose.ui.window.MenuBar +import java.awt.event.KeyEvent + +fun main() { + Window( + menuBar = MenuBar( + Menu( + name = "Actions", + Item( + name = "About", + onClick = { println("This is MyApp") }, + shortcut = keyStroke(KeyEvent.VK_I) + ), + Item( + name = "Exit", + onClick = { AppManager.exit() }, + shortcut = keyStroke(KeyEvent.VK_X) + ) + ), + Menu( + name = "File", + Item( + name = "Copy", + onClick = { println("Copy operation.") }, + shortcut = keyStroke(KeyEvent.VK_C) + ), + Item( + name = "Paste", + onClick = { println("Paste operation.") }, + shortcut = keyStroke(KeyEvent.VK_V) + ) + ) + ) + ) { + // content + } +} +``` + diff --git a/tutorials/Window_API/WindowManagement.md b/tutorials/Window_API/WindowManagement.md new file mode 100755 index 0000000000..e301ccc5f8 --- /dev/null +++ b/tutorials/Window_API/WindowManagement.md @@ -0,0 +1,262 @@ +# Getting Started with Compose for Desktop + +## What is covered + +In this guide, we'll show you how to work with windows using Compose for Desktop. + +## Windows creation + +The main class for creating windows is AppWindow. The easiest way to create and launch a new window is to use an instance of the AppWindow class and call its method show(). You can see an example below: + +```kotlin +import androidx.compose.desktop.AppWindow + +fun main() { + AppWindow().show { + // content + } +} +``` + +There are two types of windows - modal and active. Below are functions for creating each type of window: + +1. Window - active window type. +2. Dialog - modal window type. Such a window locks its parent window until the user completes working with it and closes the modal window. + +You can see an example for both types of windows below. + +```kotlin +import androidx.compose.desktop.Window +import androidx.compose.material.Button +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.window.Dialog + +fun main() { + Window { + val dialogState = remember { mutableStateOf(false) } + + Button(onClick = { dialogState.value = true }) + + if (dialogState.value) { + Dialog( + onDismissEvent = { dialogState.value = false } + ) { + // dialog's content + } + } + } +} +``` + +## Window attributes + +Each window has 9 parameters listed below: + +1. title - window title +2. size - initial window size +3. location - initial window position +4. centered - set the window to the center of the display +5. icon - window icon +6. menuBar - window context menu +7. undecorated - disable native border and title bar of the window +8. events - window events +9. onDismissEvent - event when removing the window content from a composition + +An example of using window parameters at the creation step: + +```kotlin +import androidx.compose.desktop.Window +import androidx.compose.desktop.WindowEvents +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize + +fun main() { + Window( + title = "MyApp", + size = IntSize(800, 600), + location = IntOffset(200, 200), + centered = false, // true - by default + icon = getMyAppIcon(), // custom function that returns BufferedImage + menuBar = getMyAppMenuBar(), // custom function that returns MenuBar + undecorated = true, // false - by default + events = WindowEvents( + onOpen = { println("OnOpen") }, + ... // here may be other events + onResize = { size -> + println("Size: $size") + } + ) + ) { + // content + } +} +``` + +## Window properties + +AppWindow parameters correspond to the following properties: + +1. title - window title +2. width - window width +3. height - window height +4. x - position of the left top corner of the window along the X axis +5. y - position of the left top corner of the window along the Y axis +6. icon - window icon image +7. events - window events + +To get the properties of a window, it is enough to have a link to the current or specific window. There are two ways to get the current focused window: + +1. Using the global environment: + +```kotlin +import androidx.compose.desktop.AppWindowAmbient +import androidx.compose.desktop.Window + +fun main() { + Window { + val current = AppWindowAmbient.current + Button( + onClick = { + if (current != null) { + println("Title: ${current.title} ${current.x} ${current.y}") + } + } + ) + } +} +``` + +2. Using AppManager: + +```kotlin +import androidx.compose.desktop.AppManager +import androidx.compose.desktop.Window + +fun main() { + Window { + Button( + onClick = { + val current = AppManager.getCurrentFocusedWindow() + if (current != null) { + println("Title: ${current.title} ${current.x} ${current.y}") + } + } + ) + } +} +``` + +Using the following methods, one can change the properties of AppWindow: + +1. setTitle(title: String) - window title +2. setSize(width: Int, height: Int) - window size +3. setLocation(x: Int, y: Int) - window position +4. setWindowCentered() - set the window to the center of the display +5. setIcon(image: BufferedImage?) - window icon + +```kotlin +import androidx.compose.desktop.AppWindowAmbient +import androidx.compose.desktop.Window + +fun main() { + Window { + val current = AppWindowAmbient.current + Button( + onClick = { + if (current != null) { + current.setWindowCentered() + } + } + ) + } +} +``` + +## Window events + +Events could be defined using the events parameter at the window creation step or redefine using the events property at runtime. +Actions can be assigned to the following window events: + +1. onOpen - event during window opening +2. onClose - event during window closing +3. onMinimize - event during window minimizing +4. onMaximize - event during window maximizing +5. onRestore - event during restoring window size after window minimize/maximize +6. onFocusGet - event when window gets focus +7. onFocusLost - event when window loses focus +8. onResize - event on window resize (argument is window size as IntSize) +9. onRelocate - event of the window reposition on display (argument is window position as IntOffset) + +```kotlin +import androidx.compose.desktop.Window +import androidx.compose.desktop.WindowEvents + +fun main() { + Window( + events = WindowEvents( + onOpen = { println("OnOpen") }, + ... // here may be other events + onResize = { size -> + println("Size: $size") + } + ) + ) { + // content + } +} +``` + +## AppManager + +The AppManager class is used to customize the behavior of the entire application. Its main features: + +1. Description of common application events +```kotlin +AppManager.onEvent( + onAppStart = { println("OnAppStart") }, + onAppExit = { println("OnAppExit") } +) +``` +2. Customization of common application context menu +```kotlin +AppManager.menu( + getCommonAppMenuBar() // custom function that returns MenuBar +) +``` +3. Access to the application windows list +```kotlin +val windows = AppManager.getWindows() +``` +4. Getting the current focused window +```kotlin +val current = AppManager.getCurrentFocusedWindow() +``` +5. Application exit +```kotlin +AppManager.exit() // closes all windows +``` + +## Access to javax.swing components + +Compose for Desktop uses Swing components as the window system. For more detailed customization, you can access the JFrame (Swing window representation): + +```kotlin +import androidx.compose.desktop.AppManager +import androidx.compose.desktop.Window + +fun main() { + Window { + Button( + onClick = { + val current = AppManager.getCurrentFocusedWindow() + if (current != null) { + val jFrame = current.window + // do whatever you want with it, for example add some new listeners + } + } + ) + } +} +``` +