{"id":13798627,"url":"https://github.com/dlsc-software-consulting-gmbh/WorkbenchFX","last_synced_at":"2025-05-13T06:31:32.282Z","repository":{"id":40499547,"uuid":"148480967","full_name":"dlsc-software-consulting-gmbh/WorkbenchFX","owner":"dlsc-software-consulting-gmbh","description":"A lightweight RCP framework for JavaFX applications.","archived":false,"fork":false,"pushed_at":"2023-09-05T11:25:10.000Z","size":58776,"stargazers_count":492,"open_issues_count":11,"forks_count":65,"subscribers_count":22,"default_branch":"master-11","last_synced_at":"2025-05-01T20:01:56.900Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dlsc-software-consulting-gmbh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-09-12T13:00:41.000Z","updated_at":"2025-04-27T21:47:41.000Z","dependencies_parsed_at":"2022-07-04T10:34:42.574Z","dependency_job_id":"79a29e95-72ae-44db-b301-0341ef0ce08c","html_url":"https://github.com/dlsc-software-consulting-gmbh/WorkbenchFX","commit_stats":null,"previous_names":["dlemmermann/workbenchfx"],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlsc-software-consulting-gmbh%2FWorkbenchFX","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlsc-software-consulting-gmbh%2FWorkbenchFX/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlsc-software-consulting-gmbh%2FWorkbenchFX/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlsc-software-consulting-gmbh%2FWorkbenchFX/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dlsc-software-consulting-gmbh","download_url":"https://codeload.github.com/dlsc-software-consulting-gmbh/WorkbenchFX/tar.gz/refs/heads/master-11","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253888841,"owners_count":21979515,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T00:00:47.507Z","updated_at":"2025-05-13T06:31:30.973Z","avatar_url":"https://github.com/dlsc-software-consulting-gmbh.png","language":"Java","funding_links":[],"categories":["Community"],"sub_categories":["Libraries"],"readme":"[![JFXCentral](https://img.shields.io/badge/Find_me_on-JFXCentral-blue?logo=googlechrome\u0026logoColor=white)](https://www.jfx-central.com/libraries/workbenchfx)\n\n# WorkbenchFX\n[![Codecov.io Code Coverage](https://codecov.io/gh/dlsc-software-consulting-gmbh/WorkbenchFX/branch/master/graph/badge.svg)](https://codecov.io/gh/dlsc-software-consulting-gmbh/WorkbenchFX)\n[![Maven Central](https://img.shields.io/maven-central/v/com.dlsc.workbenchfx/workbenchfx-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.dlsc.workbenchfx%22%20AND%20a:%22workbenchfx-core%22)\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fdlsc-software-consulting-gmbh%2FWorkbenchFX.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fdlsc-software-consulting-gmbh%2FWorkbenchFX?ref=badge_shield)\n\n**The one and only framework to build large JavaFX Applications!**\n\n![screenshot of an application created with WorkbenchFX](docs/images/workbenchFX_in_use.png) \n\n## Maven\n\nTo use this framework as part of your Maven build simply add the following dependency to your pom.xml file:\n\n### Java 8\n```XML\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.dlsc.workbenchfx\u003c/groupId\u003e\n  \u003cartifactId\u003eworkbenchfx-core\u003c/artifactId\u003e\n  \u003cversion\u003e8.1.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Java 11\n```XML\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.dlsc.workbenchfx\u003c/groupId\u003e\n  \u003cartifactId\u003eworkbenchfx-core\u003c/artifactId\u003e\n  \u003cversion\u003e11.1.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Gradle\n\nTo use this framework as part of your gradle build simply add the following to your build.gradle file and use the following dependency definition:\n\n### Java 8\n```groovy\ndependencies {\n    compile group: 'com.dlsc.workbenchfx', name: 'workbenchfx-core', version: '8.1.0'\n}\n```\n\n### Java 11\n```groovy\ndependencies {\n    compile group: 'com.dlsc.workbenchfx', name: 'workbenchfx-core', version: '11.1.0'\n}\n```\n\n# Table of Contents\n- [What is WorkbenchFX?](#what-is-workbenchfx)\n- [Advantages](#advantages)\n- [Main Components](#main-components)\n- [Documentation](#documentation)\n- [Basic Structure](#basic-structure)\n  - [Workbench Concept](#workbench-concept)\n  - [Module Lifecycle](#module-lifecycle)\n- [Demos](#demos)\n- [Getting Started](#getting-started)\n  - [Extending the WorkbenchModule](#extending-the-workbenchmodule)\n  - [Creating the Workbench](#creating-the-workbench)\n  - [Optional Methods](#optional-methods)\n    - [WorkbenchBuilder](#workbenchbuilder)\n    - [Workbench](#workbench)\n    - [WorkbenchModule](#workbenchmodule)\n- [Using the Components](#using-the-components)\n  - [ToolbarItem](#toolbaritem)\n  - [Dialog](#dialog)\n    - [Predefined Dialog Types](#predefined-dialog-types)\n    - [Custom Dialog](#custom-dialog)\n  - [Prevent module from closing](#prevent-module-from-closing)\n  - [Drawer](#drawer)\n  - [Custom Overlay](#custom-overlay)\n- [Restyling](#restyling)\n  - [Basic Styling](#basic-styling)\n    - [Changing Colors](#changing-colors)\n    - [Setting a Logo](#setting-a-logo)\n  - [Advanced Styling](#advanced-styling)\n- [Team](#team)\n\n# What is WorkbenchFX?\nWorkbenchFX is an out of the box solution to build large applications from multiple views, called modules.\nIt offers you good user experience and a beautiful design.\n\nAs a developer, you often start with the views, so you can quickly show your progress to your customer.\nAfter that, some question may arise such as:\n- \"How do I bring all those views together?\"\n- \"How do I build the navigation between those views?\"\n- \"How do I establish a good user experience?\"\n\nExactly when those questions appear, *WorkbenchFX* comes into play:\nWith *WorkbenchFX* you can focus on designing your views and meanwhile we're building the application around them.\n\n*WorkbenchFX* also scales with growing requirements.\nIn the beginning you just want to navigate through the views, but later on you probably would want to use a menu or a toolbar.\nEven that is supported by *WorkbenchFX* and you don't have to build anything by yourself.\n\nIf you still manage to start outgrowing the workbench, you can even replace whole parts of it with your own implementations, without having to rewrite the whole workbench.\n\n# Advantages\n- Less error-prone\n- Less code needed\n- Easy to learn\n- Easy to understand\n- Easy to use, especially for developers which have not much experience in working with JavaFX\n- A well designed, adaptable UI, inspired by the material design standards\n- Multiple, independent *workbench modules*, displayed in Tabs combine into one great application\n- The `jdk8` branch works well with [JPRO](https://www.jpro.one/)\n- FXML \u0026 [Scene Builder](https://gluonhq.com/products/scene-builder/) support\n\n# Main Components\nThe most important components are noted in the picture and the corresponding table below:\n\n![screenshot of the addModulePage](docs/images/components/addModulePage.png)\n\nNr. | Component           | Description\n--- | ------------------- | -----------\n _  | `WorkbenchModule`   | A `Workbench` consists of multiple modules. It contains a title, an icon and the content to be displayed in it. It represents the *views* mentioned in chapter [What is WorkbenchFX?](#what-is-workbenchfx)\n 2  | `Tile`              | For each `WorkbenchModule` a `Tile` will be created. Clicking on the `Tile` opens the corresponding module\n 3  | `Tab`               | A `Tab` will be displayed for each open module. Clicking on a `Tab` opens and shows the view of the corresponding module. Pressing the *'x'* button closes the module\n 4  | Tab bar             | The upper section of the window, where the `Tab`s of the current open modules are displayed\n 5  | Add button          | The button used to open a new module. It opens an overview of all available modules\n 6  | `AddModulePage`     | Stores all the `Page`s on which the `Tile`s are displayed\n 7  | `Page`              | When more modules are loaded than defined in the `modulesPerPage()` attribute, the `Workbench` creates multiple `Page`s on which the `Tile`s are displayed\n 8  | Pagination dots     | Are only displayed when having multiple `Page`s and can be used for navigating through them\n 9  | Toolbar             | It contains `ToolbarItem`s. If the bar does not contain any items, the *toolbar* will be hidden automatically\n10  | `ToolbarItem`       | Depending on the defined attributes, the item behaves like a JavaFX `Label`, `Button` or `MenuButton` (more about `ToolbarItem`s: [ToolbarItem](#toolbaritem))\n11  | Menu button         | It opens the `NavigationDrawer`. The position of the button varies depending on the amount of items to be displayed in the *toolbar* and the `NavigationDrawer`. If the `NavigationDrawer` does not contain any items, the button will not be displayed at all. If any items are in the *toolbar*, it will be displayed on the left side of the *toolbar*, otherwise on the left side of the *tab bar*\n\n![screenshot of the navigationDrawer](docs/images/components/navigationDrawer.png)\n\nNr. | Component           | Description\n--- | ------------------- | -----------\n12  | `NavigationDrawer`  | It displays a *logo* which can be set in the stylesheet (described in chapter [Setting a Logo](#setting-a-logo)) and the defined `MenuItem`s. The default hover behavior over its items can be changed using the method call `navigationDrawer.setMenuHoverBehavior()`. It can be closed by clicking on the `GlassPane` or by pressing the back arrow button\n13  | `GlassPane`         | The `GlassPane` prevents click events on the components below and adds a scrim to the background. Unless a blocking (modal) overlay is being displayed, clicking on the `GlassPane` closes the overlay (more about the *blocking* attribute: [Custom Overlay](#custom-overlay), [Custom Dialog](#custom-dialog))\n\n![screenshot of the drawer](docs/images/components/drawer.png)\n\nNr. | Component           | Description\n--- | ------------------- | -----------\n14  | `Drawer`            | It is possible to use `workbench.showDrawer()` to show drawers with custom content. All four sides of the window are supported (more about `Drawer`s: [Drawer](#drawer))\n\n![screenshot of the dialog](docs/images/components/dialog.png)\n\nNr. | Component           | Description\n--- | ------------------- | -----------\n15  | `DialogControl`     | Dialogs can be shown using a variety of predefined dialog types like `showInformationDialog()`, `showErrorDialog`, etc. Calling `workbench.showDialog(WorkbenchDialog)` shows a custom dialog (more about dialogs: [Dialog](#dialog))\n\n![screenshot of the moduleToolbar](docs/images/components/moduleToolbar.png)\n\nNr. | Component           | Description\n--- | ------------------- | -----------\n16  | `Module toolbar`    | Displays the module's toolbar items ([Workbench Module](#workbenchmodule)). The toolbar will automatically be shown as soon as there are items to be displayed and it will be hidden when there are none\n\nFor further information about the components, refer to the *Javadoc*\n\n# Documentation\nThe detailed documentation can be found in: `workbenchfx-demo/src/main/resources/com/dlsc/workbenchfx/modules/webview/index.html`\n\nIt can also be read by opening the **Documentation** module in `ExtendedDemo` or the `CustomDemo`.\n\n# Basic Structure\n## Workbench Concept\n*WorkbenchFX* uses the builder pattern to create the `Workbench` object, since it allows to use optional features in a flexible way.\nThe minimal usage requires only to specify the `WorkbenchModule` objects to be used in the `Workbench`.\nAfterwards, optional features can be defined using their respective method call before calling `build()`.\n\nFor better illustration, the basic concept of creating a `Workbench` object is shown below:\n```Java\nWorkbench workbench = \n    Workbench.builder( // Using the static method call\n        new CustomWorkbenchModule() // class CustomWorkbenchModule extends WorkbenchModule\n        ...\n    )\n    // .toolbarRight(...) // optional usage of additional features like navigationDrawer(), modulesPerPage(), etc.\n    .build(); // The build call creates, initializes and returns the Workbench object\n```\n\nNote:\n- The result of the `build()` call is a `Control` which can be set in a scene\n- For use with FXML \u0026 [Scene Builder](https://gluonhq.com/products/scene-builder/), there is also a default constructor `new Workbench()`. However, in this case, modules and other optional features need to be defined separately afterwards\n\n## Module Lifecycle\nThe lifecycle methods are implicitly being called by the workbench.\nYou **must not** call any of the lifecycle methods by yourself.\nTo close and open modules use only `workbench.openModule()` and `workbench.closeModule()`.\n\nThe abstract class `WorkbenchModule` contains four different lifecycle methods which can be overridden:\n\nMethod         | Description\n-------------- | -----------\n`init()`       | Gets called when the module is being opened from the overview for the first time\n`activate()`   | Gets called whenever the currently displayed content is being switched to this module\n`deactivate()` | Gets called whenever this module's currently displayed content is being switched to the content of another module\n`destroy()`    | Gets called when this module is explicitly being closed by the user by clicking on the *'x'* symbol in the `Tab`\n\n**When extending `WorkbenchModule`, it is only required to implement the `activate()` method.**\n([Extending the WorkbenchModule](#extending-the-workbenchmodule))\n\nOverriding all other lifecycle methods is optional and only needs to be done to perform additional actions in the lifecycle.\nBesides a call to `super()`, no further workbench-related code is required when overriding a lifecycle method.\n\nNote:\n- For further information, refer to our documentation or the *Javadoc*\n- The full documentation about the module lifecycle can be found in the documentation file `workbenchfx-demo/src/main/resources/com/dlsc/workbenchfx/modules/webview/index.html`, in the section *WorkbenchModule Lifecycle*\n\n# Demos\nWe created several demos to visualize the capabilities of *WorkbenchFX* in the `workbenchfx-demo` folder:\n\nFile                | Description\n------------------- | -----------\n`SimpleDemo.java`   | Shows the simplest usage of *WorkbenchFX* with only three modules and no optional features used\n`ExtendedDemo.java` | Shows a simple workbench application with most of the features used in a simple way.\n`CustomDemo.java`   | A workbench application which uses all features, to demonstrate the full capability of *WorkbenchFX*\n`FXMLDemo.java`     | A minimal example of how to use *WorkbenchFX* with FXML \u0026 [Scene Builder](https://gluonhq.com/products/scene-builder/)\n\n# Getting started\n## Extending the WorkbenchModule\nIt is required to create a new class and extend `WorkbenchModule`, in order to create a custom module:\n\n```Java\npublic class CustomModule extends WorkbenchModule {\n  \n}\n```\n\nIt is then required to call the `super()` constructor and pass in a `String` as the name and either an `Image`, `FontAwesomeIcon` or `MaterialDesignIcon` as an icon for the module\n(icon cheatsheets: [materialdesignicons.com](https://materialdesignicons.com/), [fontawesome.com](https://fontawesome.com/v4.7.0/)):\n\n```Java\npublic CustomModule() {\n  super(\"My first Workbench module\", MaterialDesign.MDI_THUMB_UP); // a name and an icon is required\n}\n```\n\nFurthermore, overriding the `activate()` method is also required.\nThis lifecycle method will be called when clicking on the `Tile` to open the module (see [Module Lifecycle](#module-lifecycle)):\n\n```Java\n@Override\npublic Node activate() {\n  return new Label(\"Hello World\"); // return here the actual content to display\n}\n```\n\nThe minimal implementation of a custom `WorkbenchModule` finally looks like the code snippet below.\nReturning a *Hello World Label* represents the view which will be displayed in the final application.\nFor further information, refer to the *Javadoc*.\n\n```Java\npublic class CustomModule extends WorkbenchModule {\n  public CustomModule() {\n      super(\"My first Workbench module\", MaterialDesign.MDI_THUMB_UP); // A name and an icon is required\n  }\n  @Override\n  public Node activate() {\n      return new Label(\"Hello World\"); // return here the actual content to display\n  }\n}\n```\n\n## Creating the Workbench\nAfter extending the `WorkbenchModule`, the `Workbench` can be created.\nTo do this, access the `WorkbenchBuilder` by calling `Workbench.builder()`, passing in the previously created module as an object and build the `Workbench` by calling `workbench.build()`:\n\n```Java\n// Creating the Workbench\nWorkbench customWorkbench = Workbench.builder( // Getting a WorkbenchBuilder\n    new CustomModule()                         // Adding the CustomModule\n).build();                                     // Building the Workbench\n```\n\nFor the *final application*, it can then be used in a `Scene` as follows:\n\n```Java\npublic class CustomDemo extends Application {\n  public static void main(String[] args) {\n    launch(args);\n  }\n\n  @Override\n  public void start(Stage primaryStage) {\n    \n    Workbench customWorkbench = Workbench.builder(\n        new CustomModule()\n    ).build();\n    \n    Scene myScene = new Scene(customWorkbench);\n    primaryStage.setScene(myScene);\n    primaryStage.setWidth(700);\n    primaryStage.setHeight(450);\n    primaryStage.show();\n  }\n}\n```\n\nThis code snippet results in the following application:\n\n![custom workbench](docs/images/customWorkbench.png)\n\nThe default implementation comes with a clickable `Tile` to open the module.\nOpening the module, creates a `Tab` with the defined icon and text.\nThe content returned in the `activate()` method is displayed in the center.\nBy clicking on the *add button*, you can get back to the `AddModulePage`.\nClosing the opened module is achieved through clicking on the close button in the `Tab`.\n\n### Single Module Layout\nIf the workbench consists of only one module, a single module layout is used to display the module.\nIn the single module layout, the tab bar and the add module button are not visible and the module is \nautomatically opened on startup.  \nThis results in a very basic application as can be seen below\n\n![single module layout](docs/images/singleModuleLayout.png)\n\n## Optional Methods\n### WorkbenchBuilder\nThese optional method calls are called after adding the custom modules to the builder:\n\n```Java\nWorkbench workbench = Workbench.builder(...)\n.modulesPerPage(6) // call the optional methods\n.build();\n```\n\nThe following methods are optionally available to further configure the `Workbench`:\n\nMethod in WorkbenchBuilder | Description\n-------------------------- | -----------\n`modulesPerPage()`         | Defines the amount of `Tile`s that should be shown per `Page` in `AddModulePage`. The default value is set to 6\n`navigationDrawerItems()`  | Allows to add `MenuItem`s which are then displayed in the `NavigationDrawer`.\n`toolbarLeft()`            | Allows to add `ToolbarItem`s on the left side of the toolbar on top of the `Tab`s\n`toolbarRight()`           | Allows to add `ToolbarItem`s on the right side of the toolbar on top of the `Tab`s\n\nWhen the default layout of `Page`, `Tab`, `Tile` or `NavigationDrawer` don't fulfill the desired requirements, it is possible to replace them:\n\nMethod in WorkbenchBuilder | Description\n-------------------------- | -----------\n`navigationDrawer()`       | Allows setting a custom implementation of the `NavigationDrawer` control, which will then be used\n`pageFactory()`            | Requires a `Callback` function which takes a `Workbench` and then returns a custom implementation of a `Page` control\n`tabFactory()`             | Requires a `Callback` function which takes a `Workbench` and then returns a custom implementation of a `Tab` control\n`tileFactory()`            | Requires a `Callback` function which takes a `Workbench` and then returns a custom implementation of a `Tile` control\n\n### Workbench\nAfter the `build()` call on `WorkbenchBuilder`, the `Workbench` is created.\nThe following selective calls might be of interest:\n\nMethod in Workbench          | Description\n---------------------------- | -----------\n`showNavigationDrawer()`     | Shows the `NavigationDrawer`\n`getNavigationDrawer()`      | Returns the `NavigationDrawer`\n`getNavigationDrawerItems()` | Returns the `ObservableList` of the drawer's `ToolbarItem`s\n`openModule()`               | Opens the specified module\n`getModules()`               | Returns a list of all modules stored in the workbench\n`show...Dialog()`            | Shows a [predefined dialog](#predefined-dialogs)\n`showDialog()`               | Shows a [custom dialog](#custom-dialog)\n`showDrawer()`               | Shows a [custom drawer](#drawer)\n`getToolbarControlsLeft()`   | Returns the list of items on the left of the `Toolbar`\n`getToolbarControlsRight()`  | Returns the list of items on the right of the `Toolbar`\n`showOverlay()`              | Shows a [custom overlay](#custom-overlay)\n`hideOverlay()`              | Hides a [custom overlay](#custom-overlay)\n\n### WorkbenchModule\nThe `WorkbenchModule` also provides useful functionality.\nIt is possible to add `ToolbarItem`s to the toolbar of the module (just like in the workbench):\n\nMethod (WorkbenchModule)    | Description\n--------------------------- | -----------\n`getWorkbench()`            | In the `init()` call, the `Workbench` is stored in the module. Calling this enables to call methods on the `Workbench` within the `WorkbenchModule`.\n`getToolbarControlsLeft()`  | Returns a list of `ToolbarItem`s. Adding items to the list will automatically create a toolbar between the tab bar and the module content and show the items on the left side\n`getToolbarControlsRight()` | Returns a list of `ToolbarItem`s. Adding items to the list will automatically create a toolbar between the tab bar and the module content and show the items on the right side\n`close()`                   | Will immediately close the module, without calling `destroy()` first (see [Module Lifecycle](#module-lifecycle))\n\n# Using the Components\n## ToolbarItem\nThe `ToolbarItem`s which can be set in the toolbars of either the workbench or the module are styled and behave differently based on their content.\nIf for example the item contains a `String` as text and a `MenuItem` it is automatically assumed that the styling and behavior of a `MenuButton` is needed.\nIf on the other hand only an `IconView` is defined, it is assumed, the behavior of a `Label` is desired.\n\nAdding different attributes to the `ToolbarItem` results in different representations: \nThey can also be seen in the *toolbar* of the `CustomDemo`\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eSyntax\u003c/th\u003e\n    \u003cth\u003eRepresentation\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Label with text\nToolbarItem toolbarItem = new ToolbarItem(\"Hello World\");\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/label_text.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Label with graphic\nToolbarItem toolbarItem = new ToolbarItem(\n  new FontIcon(MaterialDesign.MDI_THUMB_UP)\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/label_icon.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Label with text and graphic\nToolbarItem toolbarItem = new ToolbarItem(\n  \"Hello World\",\n  new FontIcon(MaterialDesign.MDI_THUMB_UP)\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/label_text_icon.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Button with text\nToolbarItem toolbarItem = new ToolbarItem(\n  \"Hello World\", event -\u003e System.out.println(\"Hello World\")\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/button_text.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Button with graphic\nToolbarItem toolbarItem = new ToolbarItem(\n  new FontIcon(MaterialDesign.MDI_THUMB_UP),\n  event -\u003e System.out.println(\"Hello World\")\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/button_icon.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Button with text and graphic\nToolbarItem toolbarItem = new ToolbarItem(\n  \"Hello World\",\n  new FontIcon(MaterialDesign.MDI_THUMB_UP),\n  event -\u003e System.out.println(\"Hello World\")\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/button_text_icon.png\"\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// MenuButton with text\nToolbarItem toolbarItem = new ToolbarItem(\n  \"Hello World\",\n  new MenuItem(\"Content 1\"), new MenuItem(\"Content 2\")\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/menuButton_text.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// MenuButton with graphic\nToolbarItem toolbarItem = new ToolbarItem(\n  new FontIcon(MaterialDesign.MDI_THUMB_UP),\n  new MenuItem(\"Content 1\"), new MenuItem(\"Content 2\")\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/menuButton_icon.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// MenuButton with text and graphic\nToolbarItem toolbarItem = new ToolbarItem(\n  \"Hello World\",\n  new FontIcon(MaterialDesign.MDI_THUMB_UP),\n  new MenuItem(\"Content 1\"), new MenuItem(\"Content 2\")\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/menuButton_text_icon.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// MenuButton with a MenuItem containing custom content\nToolbarItem toolbarItem = new ToolbarItem(\n  \"Account\",\n  new FontIcon(MaterialDesign.MDI_ACCOUNT),\n  new MenuItem(\"\",\n    new HBox(\n      new Label(\"Login: \"),\n      new TextField(),\n      new Button(\"\", new FontIcon(\n        MaterialDesign.MDI_PLUS))\n    )\n  )\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/toolbarItems/custom_content.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Dialog\nA demo of the dialogs can be found in the `DialogTestModule` of the [Custom Demo](#demos) \n\n### Predefined Dialog Types\n*WorkbenchFX* comes with a lot of predefined dialog types.\nUsing them is as simple as calling `workbench.show...Dialog()` with the desired dialog type.\nAfter clicking on one of the `Button`s of a dialog, the corresponding `ButtonType` is returned as the result of the dialog.\nTherefore it is required to define a `Consumer\u003cButtonType\u003e` for every dialog to validate the answer.\nA few examples on how to use them are listed below: \n\n```Java\n// Precondition\nWorkbench workbench = Workbench.builder(...).build; // Creating the workbench\nButton dialogBtn = new Button(\"Show Dialog\"); // Assuming the button is used in a module\n```\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eSyntax\u003c/th\u003e\n    \u003cth\u003eOutcome\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Confirmation Dialog\ndialogBtn.setOnAction(event -\u003e\n  workbench.showConfirmationDialog(\n    \"Continue without saving?\",\n    \"Are you sure you want to continue without saving\"\n      + \"your document?\",\n    buttonType -\u003e { // Proceed and validate the result }\n  )\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/dialogs/confirmation.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Error Dialog\ndialogBtn.setOnAction(event -\u003e\n  workbench.showErrorDialog(\n    \"Button click failed!\",\n    \"During the click of this button, something went\"\n      + \"horribly wrong.\",\n    buttonType -\u003e { // Proceed and validate the result }\n  )\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/dialogs/error.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr id=\"exception-dialog\"\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Error Dialog with exception\ndialogBtn = null; // Provokes an exception\ntry {\n  dialogBtn.setOnAction(\n    event -\u003e System.out.println(\"Throws NPE!\"));\n} catch (NullPointerException exception) {\n  workbench.showErrorDialog(\n    \"Button click failed!\",\n    \"During the click of this button, something went \"\n      + \"horribly wrong. Please forward the content \"\n      + \"below to anyone but the WorkbenchFX \"\n      + \"developers to track down the issue:\",\n    exception\n    buttonType -\u003e { // Proceed and validate the result }\n  );\n}\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/dialogs/exception.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr id=\"details-dialog\"\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Error Dialog with details\ndialogBtn.setOnAction(event -\u003e\n  workbench.showErrorDialog(\n    \"Button click failed!\",\n    \"During the click of this button, something went\"\n      + \"horribly wrong.\",\n    \"Details about this exception are not present.\",\n    buttonType -\u003e { // Proceed and validate the result }\n  )\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/dialogs/details.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Warning Dialog\ndialogBtn.setOnAction(event -\u003e\n  workbench.showWarningDialog(\n    \"Reset settings?\",\n    \"This will reset your device to its default\"\n      + \"factory settings.\",\n    buttonType -\u003e { // Proceed and validate the result }\n  )\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/dialogs/warning.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Information Dialog\ndialogBtn.setOnAction(event -\u003e\n  workbench.showInformationDialog(\n    \"Just to let you know\",\n    \"(This is an information dialog)\",\n    buttonType -\u003e { // Proceed and validate the result }\n  )\n);\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/dialogs/information.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### Custom Dialog\nSometimes just using the default dialog types are not enough.\nFor such special cases, the `workbench.showDialog()` method can be used.\nWith `WorkbenchDialog.builder()` a custom dialog can be created.\nThe builder provides some useful methods which can be used:\n\nWorkbenchDialog.builder(*Parameters*) | Description\n------------------------------------- | -----------\n`String title`                        | Required and defines the `title` of the dialog\n`String message`                      | Optionally either `message` or `content` can be defined. The `message` is located below the `title`\n`Node content`                        | A `Node` with custom content\n`Type type`                           | Defines one of the default dialog types like `Type.ERROR`, `Type.INFORMATION`, etc. The corresponding buttons and style class will automatically be set\n`ButtonType... buttonTypes`           | All the specified button types will be set (eg. `OK`, `CANCEL` and `APPLY` for a preferences dialog)\n\nNote: \n- Defining `content` will override further definitions of `message`, `details` or `exception`\n\nWorkbenchDialog.builder().*Parameters* | Description\n-------------------------------------- | -----------\n`blocking(boolean)`                    | Defines whether clicking on the `GlassPane` closes the dialog or not (i.e. forcing a decision when blocking)\n`onResult(Consumer\u003cButtonType\u003e)`       | After clicking on a dialog button, the clicked `ButtonType` is returned. Enables to define an action to be performed when a dialog button was pressed\n`details(String)`                      | The dialog will display the specified error message ([Error Dialog With Details](#details-dialog)). Will not be displayed when defining a content\n`exception(Exception)`                 | The dialog will display the stacktrace of a specified `Exception` ([Error Dialog With Exception](#exception-dialog)). Will not be displayed when defining a content\n`maximized(boolean)`                   | Defines whether the dialog's size should take up the whole window or only as much as needed by the content\n`showButtonsBar(boolean)`              | Defines whether the dialog's buttons should be shown or not\n`onShown(EventHandler\u003cEvent\u003e)`         | The `EventHandler` which is called when the dialog is showing\n`onHidden(EventHandler\u003cEvent\u003e)`        | The `EventHandler` which is called when the dialog is hidden\n`dialogControl(DialogControl)`         | Makes it possible to define a custom `DialogControl`\n`build()`                              | Builds the `WorkbenchDialog`\n\nWorkbenchDialog                    | Description\n---------------------------------- | -----------\n`getButton(ButtonType buttonType)` | Returns an `Optional\u003cButton\u003e` of the dialog. Useful when accessing the buttons of the dialog is needed\n\nUsing the builder it is possible to write some interesting custom dialogs:\n\n```Java\n// Precondition\nWorkbench workbench = Workbench.builder(...).build; // Creating the workbench\nButton dialogBtn = new Button(\"Show Dialog\"); // Assuming the button is used in a module\n```\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eSyntax\u003c/th\u003e\n    \u003cth\u003eOutcome\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cpre lang=\"java\"\u003e\n// Dialog which requires input to proceed\n// Create a CheckBox which will be set as content\nCheckBox checkBox = \n  new CheckBox(\"I accept the Terms and Conditions\");\ndialogBtn.setOnAction(event -\u003e {\n  // Building the dialog with the CheckBox as content\n  WorkbenchDialog dialog = WorkbenchDialog.builder(\n  \"Check the box to continue\", checkBox, ButtonType.OK)\n    .blocking(true)\n    .build();\n  // Bind the OK button to the CheckBox\n  dialog.setOnShown(event1 -\u003e {\n    dialog.getButton(ButtonType.OK).ifPresent(\n      button -\u003e button.disableProperty().bind(\n        checkBox.selectedProperty().not()));\n  });\n  getWorkbench().showDialog(dialog);\n});\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"docs/images/dialogs/conditional.png\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nOther examples can be found in the `DialogTestModule` of the [Custom Demo](#demos) \n\n## Prevent module from closing\nIn some cases it is necessary to prevent a module from closing.\nFor example, the following dialog asks about saving before closing:\n\n![Image of a dialog which asks about saving before closing the module](docs/images/dialogs/save.png)\n\nAs mentioned in [Module Lifecycle](#module-lifecycle), the `destroy()` method will be called when closing the module.\nThe module will be closed as soon as the `destroy()` method returns `true`.\nIf you want to prevent the module from closing, return `false` and then close the module by calling `close()`, as soon as you are ready.\n\nThe code snippet below results in the dialog displayed in the image on top:\n\n```Java\n@Override\npublic boolean destroy() {\n  \n  // Perform an asynchronous task (in our case showing a dialog)\n  getWorkbench().showDialog(WorkbenchDialog.builder(\n      \"Save before closing?\",\n      \"Do you want to save your progress? Otherwise it will be lost.\",\n      ButtonType.YES, ButtonType.NO, ButtonType.CANCEL)\n      .blocking(true)\n      .onResult(buttonType -\u003e {\n        // If CANCEL was not pressed\n        if (!ButtonType.CANCEL.equals(buttonType)) {\n          if (ButtonType.YES.equals(buttonType)) {\n            // YES was pressed -\u003e Proceed with saving\n            ...\n          }\n          close(); // Close the module since CANCEL was not pressed \n        }\n      })\n      .build());\n  \n  return false; // return false, because we're closing manually\n}\n```\n\n## Drawer\nCalling `workbench.showDrawer()`, enables you to show a custom drawer (like the `NavigationDrawer`).\nThere are two possibilities for showing a drawer: \n\n    workbench.showDrawer(\n        Region drawer, // Drawer to be shown\n        Side side      // From which side the drawer should come from\n    );\n\nCalling this, the width of the drawer is calculated automatically and takes the width which fits best for the drawer content defined.\nThe other possibility comes into action when a specific width is desired:\n    \n    workbench.showDrawer(\n        Region drawer, // Drawer to be shown\n        Side side      // From which side the drawer should come from\n        int percentage // Defines how much of the screen should be covered\n    );\n\nThe `percentage` can be defined in an `Integer` range between *0* and *100*.\nIt represents the percentage of the window the drawer covers when showing.  \n\nExamples of drawers can be found in the `DrawerTestModule` of the [Custom Demo](#demos)\n\n## Custom Overlay\nThe foundation of [Dialogs](#dialog) and [Drawers](#drawer) are **overlays**.\nIt is possible to define a custom overlay by calling `workbench.showOverlay()`.\nThe defined overlay will be stacked on top of a `GlassPane`.\n\n    workbench.showOverlay(\n        Region overlay,  // Overlay to be shown\n        boolean blocking // true, if the overlay should not be closed when clicking on the GlassPane\n    );\n    \nThe overlay can essentially be any `Region` (for example a custom `Control`).\nPer default, the defined content will be displayed in the top-left corner of the window.\nTo center the content of an overlay, perform the following call on the overlay:\n\n```Java\nStackPane.setAlignment(overlay, Pos.CENTER); // is needed to center the overlay on the screen\n```\n\n# Restyling\n## Basic Styling\nFirst of all:\n**WorkbenchFX does not interfere with the styles of the individual modules.**\nThis way each module can be styled independently and you do not have to worry about the workbench influencing the styling.\n\nBut it is possible to alter the styles of the workbench itself.\n*WorkbenchFX* comes with an out of the box styling.\nIt is strongly inspired by [Material Design](https://material.io/).\n\nThe workbench's styling can be altered by referencing a stylesheet, like in the [Custom Demo](#demos):\n\n```Java\nworkbench.getStylesheets().add(CustomDemo.class.getResource(\"customTheme.css\").toExternalForm());\n```\n\nIn the file `customTheme.css`, some default colors are referenced:\n\n```css\n* {\n  -primary-color: #6200EE;\n  -primary-variant-color: #3700b3;\n  -secondary-color: #6300ff;\n  -secondary-variant-color: #1e005f;\n  -background-color: #FFFFFF;\n  -surface-color: #FFFFFF;\n  -error-color: #B00020;\n  -on-primary-color: #FFFFFF;\n  -on-secondary-color: #FFFFFF;\n  -on-background-color: #000000;\n  -on-surface-color: #000000;\n  -on-error-color: #FFFFFF;\n}\n\n.logo {\n  /* Reference to the applications logo */\n  -fx-graphic: url(\"logo.png\");\n}\n```\n\nThe colors are named according to the [Material Design guidelines](https://material.io/design/color/the-color-system.html#color-theme-creation).\nChanging those colors leads to a complete restyling of the workbench.\nFor example, a file `darkTheme.css` is also referenced in the demo and leads to the following result:\n\n![screenshot of the workbench's darkTheme version](docs/images/workbenchFX_in_use_dark.png)\n\n### Changing Colors\n\nIf you want to change the colors of the application, create a new css file `customTheme.css` and add it to the workbench:\n\n```Java\nworkbench.getStylesheets().add(CustomDemo.class.getResource(\"customTheme.css\").toExternalForm());\n``` \n\nIn context, the code looks like this:\n\n```Java\npublic class CustomDemo extends Application {\n  public static void main(String[] args) {\n    launch(args);\n  }\n\n  @Override\n  public void start(Stage primaryStage) {\n    \n    Workbench customWorkbench = Workbench.builder(\n        new CustomModule()\n    ).build();\n    \n    // Adding the stylesheet to the workbench to restyle it\n    customWorkbench.getStylesheets().add(CustomDemo.class.getResource(\"customTheme.css\").toExternalForm());\n    \n    Scene myScene = new Scene(customWorkbench);\n    primaryStage.setScene(myScene);\n    primaryStage.setWidth(700);\n    primaryStage.setHeight(450);\n    primaryStage.show();\n  }\n}\n``` \n\nChanging the colors in the `css` file to something like this:\n\n```css\n* {\n  -primary-color: #9db668;\n  -primary-variant-color: #7f975f;\n  -secondary-color: #9db668;\n  -secondary-variant-color: #7f975f;\n  -background-color: #FFFFFF;\n  -surface-color: #FFFFFF;\n  -error-color: #B00020;\n  -on-primary-color: #FFFFFF;\n  -on-secondary-color: #FFFFFF;\n  -on-background-color: #747474;\n  -on-surface-color: #747474;\n  -on-error-color: #FFFFFF;\n}\n```\n\nLeads to following design:\n\n![screenshot of the custom css](docs/images/custom_css_1.png)\n\n### Setting a Logo\n\nIn the upper section of the `NavigationDrawer`, there is a section for a logo.\nThe logo is defined in the custom stylesheet (how to create one and reference it is described in the previous chapter).\nAn example implementation of the logo can be found in the `customTheme.css` of the [Custom Demo](#demos):\n\n```css\n.logo {\n  /* Reference to the application's logo */\n  -fx-graphic: url(\"logo.png\");\n}\n```\n\nChanging the logo can easily be done by adding an image with the correct size in the `resources` folder and referring to its name in the stylesheet:\n\n```css\n.logo {\n  /* Reference to the applications logo */\n  -fx-graphic: url(\"myCustomLogo.png\"); /* Replacing logo.png with a different image. */\n}\n```\n\nNote:\n- WorkbenchFX does not resize the image.\nWe suggest a maximum image height of *250px*\n\n## Advanced Styling\nSometimes just changing the colors is not enough.\nEvery component in the workbench has its own `.class` or `#id`.\nThis way, the components can be restyled if needed.\n\nFor example, every generated `Tab` and `Tile` has its own unique `#id`.\nThe naming conventions for the `#id` are defined as:\n- Prefix: `tab` / `tile` (depending on the component)\n- Body: the name of the module\n  - all special characters besides hyphens are removed\n  - all spaces are replaced by hyphens (`-`)\n  - uppercase letters are converted to lowercase\n\nSetting the LOGGER level to debug will print each module's tab and tile id as soon as they are set:\n- Set Tab-ID of '(MODULE NAME)' to: '(TAB ID)'\n- Set Tile-ID of '(MODULE NAME)' to: '(TILE ID)'\n\nFor further information, refer to the Javadoc of `WorkbenchUtils.convertToId()`\n\n`#id` example:\n\n    Module name:\n        François' Module\n        \n    Results in:\n        tab-franois-module // Tab id\n        tile-franois-module // Tile id\n        \n    LOGGER output:\n        Set Tab-ID of 'François' Module' to: 'tab-franois-module'\n        Set Tile-ID of 'François' Module' to: 'tile-franois-module'\n\nStarting with the result after chapter [Getting Started](#getting-started):\n\nAssuming the `Tab` and `Tile` need to be restyled, add the following code snippet to the `customTheme.css` file:\n\n```css\n/* Styling the Tile */\n#tile-my-first-workbench-module .tile-box {\n  -fx-background-color: -primary-color !important; /* The background of the Tile */\n}\n\n#tile-my-first-workbench-module .tile-box .text, /* The icon and the text */\n#tile-my-first-workbench-module .tile-box .glyph-icon {\n  -fx-fill: -on-primary-color !important;\n}\n\n/* Styling the Tab */\n#tab-my-first-workbench-module:selected {\n  -fx-background-color: #747474 !important; /* The background of the Tab */\n  -fx-background-radius: 5px 5px 0 0 !important;\n}\n\n#tab-my-first-workbench-module:selected .text, /* The icon and the text */\n#tab-my-first-workbench-module:selected .glyph-icon {\n  -fx-fill: #ffffff !important;\n}\n\n#tab-my-first-workbench-module:selected .shape {\n  -fx-background-color: #ffffff !important; /* The close icon */\n}\n```\n\nLeads to following styling:\n\n![screenshot of the custom css](docs/images/custom_css_2.png)\n\nNote:\n- The css color variables can still be used in the `customTheme.css` file\n- Since the styling of the workbench is more specific, the `!important` tag is required when styling the workbench\n- A tool like [ScenicView](http://fxexperience.com/scenic-view/) helps to determine the style classes\n\n# Team\n- Marco Sanfratello\n  - marco.sanfratello@students.fhnw.ch\n  - Skype: sanfratello.m@gmail.com \n  - GitHub: Genron\n\n- François Martin\n  - francois.martin@students.fhnw.ch \n  - Skype: francoisamimartin\n  - GitHub: martinfrancois\n  \n- Dirk Lemmermann\n  - dlemmermann@gmail.com\n  - Skype: dlemmermann\n  - GitHub: dlemmermann\n  \n- Dieter Holz\n  - dieter.holz@fhnw.ch\n  - Skype: dieter.holz.canoo.com\n  - GitHub: DieterHolz\n\n\n## License\n[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fdlsc-software-consulting-gmbh%2FWorkbenchFX.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fdlsc-software-consulting-gmbh%2FWorkbenchFX?ref=badge_large)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdlsc-software-consulting-gmbh%2FWorkbenchFX","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdlsc-software-consulting-gmbh%2FWorkbenchFX","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdlsc-software-consulting-gmbh%2FWorkbenchFX/lists"}