{"id":13629478,"url":"https://github.com/tgjones/gemini","last_synced_at":"2025-04-17T09:34:06.936Z","repository":{"id":40267574,"uuid":"774097","full_name":"tgjones/gemini","owner":"tgjones","description":"Gemini is an IDE framework similar in concept to the Visual Studio Shell. It uses AvalonDock and has an MVVM architecture based on Caliburn Micro.","archived":false,"fork":false,"pushed_at":"2023-07-30T04:38:02.000Z","size":45535,"stargazers_count":1097,"open_issues_count":64,"forks_count":298,"subscribers_count":101,"default_branch":"master","last_synced_at":"2024-11-02T12:08:54.065Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tgjones.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null},"funding":{"github":["tgjones"]}},"created_at":"2010-07-14T07:50:55.000Z","updated_at":"2024-10-16T00:03:34.000Z","dependencies_parsed_at":"2023-01-23T07:45:41.122Z","dependency_job_id":"61fa1977-f87b-4f14-b089-61a789e660b7","html_url":"https://github.com/tgjones/gemini","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tgjones%2Fgemini","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tgjones%2Fgemini/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tgjones%2Fgemini/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tgjones%2Fgemini/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tgjones","download_url":"https://codeload.github.com/tgjones/gemini/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223285876,"owners_count":17119971,"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-01T22:01:11.682Z","updated_at":"2024-11-08T20:31:00.791Z","avatar_url":"https://github.com/tgjones.png","language":"C#","funding_links":["https://github.com/sponsors/tgjones"],"categories":["C#","C# #","MVVM"],"sub_categories":[],"readme":"# Gemini [![Build status](https://github.com/tgjones/gemini/workflows/CI/badge.svg)](https://github.com/tgjones/gemini/actions) [![NuGet](https://img.shields.io/nuget/v/GeminiWpf.svg)](https://www.nuget.org/packages/GeminiWpf/) [![Join the chat at https://gitter.im/tgjones/gemini](https://badges.gitter.im/tgjones/gemini.svg)](https://gitter.im/tgjones/gemini?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\nGemini is a WPF framework designed specifically for building IDE-like applications. It builds on some excellent libraries:\n\n* [AvalonDock](https://github.com/Dirkster99/AvalonDock) (specifically Dirkster99's fork, because it supports .NET Core)\n* [Caliburn Micro](https://github.com/Caliburn-Micro/Caliburn.Micro)\n\nGemini ships with two themes: a Light theme and a Blue theme. There is also an in-development Dark theme.\n\n![Screenshot - Light theme](https://raw.github.com/tgjones/gemini/master/doc/gemini-everything-light.png)\n\n![Screenshot - Blue theme](https://raw.github.com/tgjones/gemini/master/doc/gemini-everything-blue.png)\n\n## Getting started\n\nIf you are creating a new WPF application, follow these steps:\n\n* Install the [Gemini](http://nuget.org/packages/GeminiWpf/) NuGet package.\n* Delete `MainWindow.xaml` - you don't need it.\n* Open `App.xaml` and delete the `StartupUri=\"MainWindow.xaml\"` attribute.\n* Add `xmlns:gemini=\"http://schemas.timjones.tw/gemini\"` to `App.xaml`.\n* Add `\u003cgemini:AppBootstrapper x:Key=\"bootstrapper\" /\u003e` to a `ResourceDictionary` within `\u003cApplication.Resources\u003e`.\n\nSo the whole `App.xaml` should look something like this:\n\n```xml\n\u003cApplication x:Class=\"Gemini.Demo.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" \n             xmlns:gemini=\"http://schemas.timjones.tw/gemini\"\u003e\n    \u003cApplication.Resources\u003e\n        \u003cResourceDictionary\u003e\n            \u003cResourceDictionary.MergedDictionaries\u003e\n                \u003cResourceDictionary\u003e\n                    \u003cgemini:AppBootstrapper x:Key=\"bootstrapper\" /\u003e\n                \u003c/ResourceDictionary\u003e\n            \u003c/ResourceDictionary.MergedDictionaries\u003e\n        \u003c/ResourceDictionary\u003e\n    \u003c/Application.Resources\u003e\n\u003c/Application\u003e\n```\n\nNow hit F5 and see a very empty application!\n\nBy far the easiest way to get started with Gemini is to use the various NuGet packages.\nFirst, install the base Gemini package (note that the package ID is `GeminiWpf`, to\ndistinguish it from another NuGet package with the same name):\n\n* [Gemini](http://nuget.org/packages/GeminiWpf/)\n\nThen add any other modules you are interested in (note that some modules have dependencies\non other modules, but this is taken care of by the NuGet package dependency system):\n\n* [Gemini.Modules.CodeCompiler](http://nuget.org/packages/Gemini.Modules.CodeCompiler/)\n* [Gemini.Modules.CodeEditor](http://nuget.org/packages/Gemini.Modules.CodeEditor/)\n* [Gemini.Modules.ErrorList](http://nuget.org/packages/Gemini.Modules.ErrorList/)\n* [Gemini.Modules.GraphEditor](http://nuget.org/packages/Gemini.Modules.GraphEditor/)\n* [Gemini.Modules.Inspector](http://nuget.org/packages/Gemini.Modules.Inspector/)\n* [Gemini.Modules.Output](http://nuget.org/packages/Gemini.Modules.Output/)\n* [Gemini.Modules.PropertyGrid](http://nuget.org/packages/Gemini.Modules.PropertyGrid/)\n\n## Continuous builds\n\nWe use AppVeyor to build Gemini after every commit to the master branch,\nand also to generate pre-release NuGet packages so you can try out new features immediately.\n\nTo access the pre-release NuGet packages, you'll need to add a custom package source in Visual Studio,\npointing to this URL:\n\nhttps://ci.appveyor.com/nuget/gemini-g84phgw340sm\n\nMake sure you select \"Include Prerelease\" when searching for NuGet packages.\n\n## What does it do?\n\nGemini allows you to build your WPF application by composing separate modules. This provides a nice\nway of separating out the code for each part of your application. For example, here is a very simple\nmodule:\n\n```csharp\n[Export(typeof(IModule))]\npublic class Module : ModuleBase\n{\n\t[Import]\n\tprivate IPropertyGrid _propertyGrid;\n\n    public override IEnumerable\u003cType\u003e DefaultTools\n    {\n        get { yield return typeof(IInspectorTool); }\n    }\n\n\tpublic override void Initialize()\n\t{\n\t\tvar homeViewModel = IoC.Get\u003cHomeViewModel\u003e();\n\t\tShell.OpenDocument(homeViewModel);\n\n\t\t_propertyGrid.SelectedObject = homeViewModel;\n\t}\n\n\tprivate IEnumerable\u003cIResult\u003e OpenHome()\n\t{\n\t\tyield return Show.Document\u003cHomeViewModel\u003e();\n\t}\n}\n```\n\n### Documents\n\nDocuments are (usually) displayed in the main area in the middle of the window. To create a new document\ntype, simply inherit from the `Document` class:\n\n```csharp\npublic class SceneViewModel : Document\n{\n\tpublic override string DisplayName\n\t{\n\t\tget { return \"3D Scene\"; }\n\t}\n\n\tprivate Vector3 _position;\n\tpublic Vector3 Position\n\t{\n        get { return _position; }\n        set\n        {\n            _position = value;\n            NotifyOfPropertyChange(() =\u003e Position);\n        }\n\t}\n}\n```\n\nTo open a document, call `OpenDocument` on the shell (`Shell` is defined in `ModuleBase`, but you can also \nretrieve it from the IoC container with `IoC.Get\u003cIShell\u003e()`):\n\n```csharp\nShell.OpenDocument(new SceneViewModel());\n```\n\nYou can then create a `SceneView` view, and Caliburn Micro will use a convention-based lookup to find the correct view.\n\n### Persisted documents\n\nIf you have a document that needs to be loaded from, and saved to, a file, you can use the `PersistedDocument`\nbase class, to remove a lot of the boilerplate code that you would usually have to write. You only need to\nimplement the `DoNew`, `DoLoad`, and `DoSave` methods.\n\n``` csharp\npublic class EditorViewModel : PersistedDocument\n{\n\tprivate EditorView _view;\n\tprivate string _originalText;\n\n\tprotected override Task DoNew()\n\t{\n\t\t_originalText = string.Empty;\n\t\tApplyOriginalText();\n\t\treturn TaskUtility.Completed;\n\t}\n\n\tprotected override Task DoLoad(string filePath)\n\t{\n\t\t_originalText = File.ReadAllText(filePath);\n\t\tApplyOriginalText();\n\t\treturn TaskUtility.Completed;\n\t}\n\n\tprotected override Task DoSave(string filePath)\n\t{\n\t\tvar newText = _view.textBox.Text;\n\t\tFile.WriteAllText(filePath, newText);\n\t\t_originalText = newText;\n\t\treturn TaskUtility.Completed;\n\t}\n\n\tprivate void ApplyOriginalText()\n\t{\n\t\t_view.textBox.Text = _originalText;\n\n\t\t_view.textBox.TextChanged += delegate\n\t\t{\n\t\t\tIsDirty = string.Compare(_originalText, _view.textBox.Text) != 0;\n\t\t};\n\t}\n\n\tprotected override void OnViewLoaded(object view)\n\t{\n\t\t_view = (EditorView) view;\n\t}\n}\n```\n\n### Tools\n\nTools are usually docked to the sides of the window, although they can also be dragged\nfree to become floating windows. Most of the modules (ErrorList, Output, Toolbox, etc.) primarily provide tools.\nFor example, here is the property grid tool class:\n\n```csharp\n[Export(typeof(IPropertyGrid))]\npublic class PropertyGridViewModel : Tool, IPropertyGrid\n{\n\tpublic PropertyGridViewModel()\n\t{\n\t\tDisplayName = \"Properties\";\n\t}\n\n\tpublic override PaneLocation PreferredLocation\n\t{\n\t\tget { return PaneLocation.Right; }\n\t}\n\n\tprivate object _selectedObject;\n\tpublic object SelectedObject\n\t{\n\t\tget { return _selectedObject; }\n\t\tset\n\t\t{\n\t\t\t_selectedObject = value;\n\t\t\tNotifyOfPropertyChange(() =\u003e SelectedObject);\n\t\t}\n\t}\n}\n```\n\nFor more details on creating documents and tools, look at the \n[demo program](https://github.com/tgjones/gemini/tree/master/src/Gemini.Demo)\nand the source code for the built-in modules.\n\n### Commands\n\nCommands are one of the core concepts in Gemini. Commands help you to avoid duplicating code\nby letting you define command handlers in a single place, regardless of whether the command\nis invoked through a menu item, toolbar item, or other trigger.\nGemini's commands are conceptually similar to WPF commands, but they are more powerful.\n\nFirst, create a command definition. Here's Gemini [command definition for opening files](https://github.com/tgjones/gemini/blob/master/src/Gemini/Modules/Shell/Commands/OpenFileCommandDefinition.cs):\n\n``` csharp\n[CommandDefinition]\npublic class OpenFileCommandDefinition : CommandDefinition\n{\n\tpublic const string CommandName = \"File.OpenFile\";\n\n\tpublic override string Name\n\t{\n\t\tget { return CommandName; }\n\t}\n\n\tpublic override string Text\n\t{\n\t\tget { return \"_Open\"; }\n\t}\n\n\tpublic override string ToolTip\n\t{\n\t\tget { return \"Open\"; }\n\t}\n\n\tpublic override Uri IconSource\n\t{\n\t\tget { return new Uri(\"pack://application:,,,/Gemini;component/Resources/Icons/Open.png\"); }\n\t}\n\t\n\t[Export]\n    public static CommandKeyboardShortcut KeyGesture = new CommandKeyboardShortcut\u003cOpenFileCommandDefinition\u003e(new KeyGesture(Key.O, ModifierKeys.Control));\n}\n```\n\nThen, provide a command handler. You can do this in one of two ways. For global commands,\nthat don't depend on a document context, create a global handler:\n\n``` csharp\n[CommandHandler]\npublic class OpenFileCommandHandler : CommandHandlerBase\u003cOpenFileCommandDefinition\u003e\n{\n\tpublic override void Update(Command command)\n\t{\n\t\t// You can enable / disable the command here with:\n\t\t// command.Enabled = true;\n\t\t\n\t\t// You can also modify the command text / icon, which will affect\n\t\t// any menu items or toolbar items bound to this command.\n\t}\n\t\n\tpublic override async Task Run(Command command)\n\t{\n\t\t// ... implement command handling here\n\t}\n}\n```\n\nFor commands that depend on a document context, and should be disabled when there is no active document\nor the active document is not of the correct type, define the command in the document class:\n\n``` csharp\npublic class MyDocument : Document, ICommandHandler\u003cClearTextCommandDefinition\u003e\n{\n\tvoid ICommandHandler\u003cClearTextCommandDefinition\u003e.Update(Command command)\n\t{\n\t\tcommand.Enabled = this.Text.Any();\n\t}\n\n\tTask ICommandHandler\u003cClearTextCommandDefinition\u003e.Run(Command command)\n\t{\n\t\tthis.Text = string.Empty;\n\t\treturn TaskUtility.Completed;\n\t}\n}\n```\n\nTo remove built-in keyboard shortcuts, you can exclude them declaratively:\n\n``` csharp\n[Export]\npublic static ExcludeCommandKeyboardShortcut ExcludeFileOpenShortcut = new ExcludeCommandKeyboardShortcut(OpenFileCommandDefinition.KeyGesture);\n```\n\nTo find out how to bind commands to menus or toolbars, see the \"MainMenu\" and \"ToolBars\" modules below.\n\n## What modules are built-in?\n\nGemini itself is built out of seven core modules:\n\n* MainWindow\n* Shell\n* MainMenu\n* StatusBar\n* ToolBars\n* Toolbox\n* UndoRedo\n\nSeveral more modules ship with Gemini, and are available as \n[NuGet packages](http://nuget.org/packages?q=Gemini.Modules) as described above:\n\n* CodeCompiler\n* CodeEditor\n* ErrorList\n* GraphEditor\n* Inspector\n* Output\n* PropertyGrid\n\nFor more information about these modules, see below. In general, each module adds some combination\nof menu items, tool window, document types and services.\n\n### MainWindow module\n\nThe main window module:\n\n* manages the overall window\n\n#### Provides\n\n* `IMainWindow` interface\n\n#### NuGet package\n\n* [Gemini](http://nuget.org/packages/GeminiWpf/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\nThe `IMainWindow` interface exposes a number of useful properties to control\naspects of the main application window.\n\n```csharp\npublic interface IMainWindow\n{\n    WindowState WindowState { get; set; }\n    double Width { get; set; }\n    double Height { get; set; }\n\n    string Title { get; set; }\n    ImageSource Icon { get; set; } \n\n    IShell Shell { get; }\n}\n```\n\n### Shell module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-shell.png)\n\nThe shell module:\n\n* manages placement of the document and tool windows\n* persists and loads the size and position of tool windows\n* manages the links between AvalonDock and Caliburn.Micro\n\n#### Provides\n\n* `IShell` interface\n\n#### NuGet package\n\n* [Gemini](http://nuget.org/packages/GeminiWpf/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\nThe `IShell` interface exposes a number of useful properties and methods. It is the main way\nto control Gemini's behaviour.\n\n```csharp\npublic interface IShell\n{\n    event EventHandler ActiveDocumentChanging;\n    event EventHandler ActiveDocumentChanged;\n\n    bool ShowFloatingWindowsInTaskbar { get; set; }\n        \n\tIMenu MainMenu { get; }\n    IToolBars ToolBars { get; }\n\tIStatusBar StatusBar { get; }\n\n\tIDocument ActiveItem { get; }\n\n\tIObservableCollection\u003cIDocument\u003e Documents { get; }\n\tIObservableCollection\u003cITool\u003e Tools { get; }\n\n    void ShowTool\u003cTTool\u003e() where TTool : ITool;\n\tvoid ShowTool(ITool model);\n\n\tvoid OpenDocument(IDocument model);\n\tvoid CloseDocument(IDocument document);\n\n\tvoid Close();\n}\n```\n\n### MainMenu module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-mainmenu.png)\n\nAdds a main menu to the top of the window.\n\n#### NuGet package\n\n* [Gemini](http://nuget.org/packages/GeminiWpf/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\nFirst, create commands, as described above in the \"Commands\" section. Then declare menus, menu item groups,\nand menu items. This is how the built-in File menu and menu items are declared; you can create your own\nmenus in the same way.\n\n```csharp\npublic static class MenuDefinitions\n{\n    [Export]\n    public static readonly MenuDefinition FileMenu = new MenuDefinition(MainMenuBar, 0, Resources.FileMenuText);\n\t\n\t[Export]\n    public static readonly MenuItemGroupDefinition FileNewOpenMenuGroup = new MenuItemGroupDefinition(FileMenu, 0);\n\t\n\t[Export]\n    public static readonly MenuItemDefinition FileNewMenuItem = new TextMenuItemDefinition(\n        MenuDefinitions.FileNewOpenMenuGroup, 0, \"_New\");\n}\n```\n\nYou can either use an existing menu or menu item group as a parent for your menu items, or create your own.\n\nTo remove an existing menu item (such as a built-in menu item that you don't want), you can exclude it declaratively:\n\n``` csharp\n[Export]\npublic static readonly ExcludeMenuItemDefinition ExcludeOpenMenuItem = new ExcludeMenuItemDefinition(Gemini.Modules.Shell.MenuDefinitions.FileOpenMenuItem);\n\n[Export]\npublic static readonly ExcludeMenuItemGroupDefinition ExcludeWindowMenuItemGroup = new ExcludeMenuItemGroupDefinition(Gemini.Modules.MainMenu.MenuDefinitions.ViewToolsMenuGroup);\n\n[Export]\npublic static readonly ExcludeMenuDefinition ExcludeWindowMenuDefinition = new ExcludeMenuDefinition(Gemini.Modules.MainMenu.MenuDefinitions.WindowMenu);\n```\n\n### StatusBar module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-statusbar.png)\n\nAdds a status bar to the bottom of the window.\n\n#### Provides\n\n* `IStatusBar`\n* `StatusBarItemViewModel` class\n\n#### NuGet package\n\n* [Gemini](http://nuget.org/packages/GeminiWpf/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\n```csharp\nvar statusBar = IoC.Get\u003cIStatusBar\u003e();\nstatusBar.AddItem(\"Hello world!\", new GridLength(1, GridUnitType.Star));\nstatusBar.AddItem(\"Ln 44\", new GridLength(100));\nstatusBar.AddItem(\"Col 79\", new GridLength(100));\n```\n\n### ToolBars module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-toolbars.png)\n\nAdds a toolbar tray to the top of the window. By default, the toolbar tray is hidden - use\n`Shell.ToolBars.Visible = true` to show it.\n\n#### NuGet package\n\n* [Gemini](http://nuget.org/packages/GeminiWpf/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\nFirst, create commands, as described above in the \"Commands\" section. Then declare toolbars, toolbar item groups,\nand toolbar items. This is how the standard toolbar and toolbar items are declared; you can create your own\ntoolbars in the same way.\n\n```csharp\ninternal static class ToolBarDefinitions\n{\n\t[Export]\n\tpublic static ToolBarDefinition StandardToolBar = new ToolBarDefinition(0, \"Standard\");\n\t\n\t[Export]\n    public static ToolBarItemGroupDefinition StandardOpenSaveToolBarGroup = new ToolBarItemGroupDefinition(\n        ToolBars.ToolBarDefinitions.StandardToolBar, 8);\n\t\t\n\t[Export]\n    public static ToolBarItemDefinition OpenFileToolBarItem = new CommandToolBarItemDefinition\u003cOpenFileCommandDefinition\u003e(\n        StandardOpenSaveToolBarGroup, 0);\n}\n\n// ...\n\nShell.ToolBars.Visible = true;\n```\n\n### Toolbox module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-toolbox.png)\n\nReproduces the toolbox tool window from Visual Studio. Use the `[ToolboxItem]` attribute to provide\navailable items for listing in the toolbox. You specify the document type for each toolbox item.\nWhen the user switches to a different document, Gemini manages showing only the toolbox items that \nare supported for the active document type. Items are listed in categories. \nThe toolbox supports drag and drop.\n\n#### Provides\n\n* `IToolbox` tool window\n* `ToolboxItemAttribute` attribute\n* `ToolboxDragDrop` utility class\n\n#### NuGet package\n\n* [Gemini](http://nuget.org/packages/GeminiWpf/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\n```csharp\n[ToolboxItem(typeof(GraphViewModel), \"Image Source\", \"Generators\")]\npublic class ImageSource : ElementViewModel\n{\n\t// ...\n}\n```\n\nHandling dropping onto a document (this code is from [`GraphView.xaml.cs`](https://github.com/tgjones/gemini/blob/master/src/Gemini.Demo.FilterDesigner/Modules/FilterDesigner/Views/GraphView.xaml.cs)):\n\n```csharp\nprivate void OnGraphControlDragEnter(object sender, DragEventArgs e)\n{\n    if (!e.Data.GetDataPresent(ToolboxDragDrop.DataFormat))\n        e.Effects = DragDropEffects.None;\n}\n\nprivate void OnGraphControlDrop(object sender, DragEventArgs e)\n{\n    if (e.Data.GetDataPresent(ToolboxDragDrop.DataFormat))\n    {\n        var mousePosition = e.GetPosition(GraphControl);\n\n        var toolboxItem = (ToolboxItem) e.Data.GetData(ToolboxDragDrop.DataFormat);\n        var element = (ElementViewModel) Activator.CreateInstance(toolboxItem.ItemType);\n        element.X = mousePosition.X;\n        element.Y = mousePosition.Y;\n\n        ViewModel.Elements.Add(element);\n    }\n}\n```\n\n### UndoRedo module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-undoredo.png)\n\nProvides a framework for adding undo/redo support to your application. An undo/redo stack is maintained\nseparately for each document. The screenshot above shows the history tool window. You can drag the slider\nto move forward or backward in the document's history.\n\n#### Provides\n\n* `IHistoryTool` tool window\n* `IUndoableAction` interface\n* `UndoRedoToolbarItems` utility class\n\n#### NuGet package\n\n* [Gemini](http://nuget.org/packages/GeminiWpf/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\nFirst, define an action. The action needs to implement `IUndoableAction`:\n\n```csharp\npublic class MyAction : IUndoableAction\n{\n    public string Name\n    {\n        get { return \"My Action\"; }\n    }\n\n    public void Execute()\n    {\n        // Do something\n    }\n\n    public void Undo()\n    {\n        // Put it back\n    }\n}\n```\n\nThen execute the action:\n\n```csharp\nvar undoRedoManager = IoC.Get\u003cIShell\u003e().ActiveItem.UndoRedoManager;\nundoRedoManager.ExecuteAction(new MyAction());\n```\n\nNow the action will be shown in the history tool window. If you are using the Undo or Redo menu items or \ntoolbar buttons, they will also react appropriately to the action.\n\n### CodeCompiler module\n\nUses Roslyn to compile C# code. Currently, `ICodeCompiler` exposes a very simple interface:\n\n```csharp\npublic interface ICodeCompiler\n{\n    Assembly Compile(\n        IEnumerable\u003cSyntaxTree\u003e syntaxTrees, \n        IEnumerable\u003cMetadataReference\u003e references,\n        string outputName);\n}\n```\n\nAn interesting feature, made possible by Roslyn, is that the compiled assemblies are garbage-collectible.\nThis means that you can compile C# source code, run the resulting assembly in the same `AppDomain` as \nyour main application, and then unload the assembly from memory. This would be very useful, for example, in\na game editor where you want the game preview window to update as soon as the user modifies a script \nsource file.\n\n#### Provides\n\n* `ICodeCompiler` service\n\n#### NuGet package\n\n* [Gemini.Modules.CodeCompiler](http://nuget.org/packages/Gemini.Modules.CodeCompiler/)\n\n#### Dependencies\n\n* [Roslyn](http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx)\n\n#### Usage\n\nThis example is from [HelixViewModel](https://github.com/tgjones/gemini/blob/master/src/Gemini.Demo/Modules/Home/ViewModels/HelixViewModel.cs) in one of the sample applications.\n\n```csharp\nvar newAssembly = _codeCompiler.Compile(\n    new[] { SyntaxTree.ParseText(_helixView.TextEditor.Text) },\n    new[]\n    {\n        MetadataReference.CreateAssemblyReference(\"mscorlib\"),\n        MetadataReference.CreateAssemblyReference(\"System\"),\n        MetadataReference.CreateAssemblyReference(\"PresentationCore\"),\n        new MetadataFileReference(typeof(IResult).Assembly.Location),\n        new MetadataFileReference(typeof(AppBootstrapper).Assembly.Location),\n        new MetadataFileReference(GetType().Assembly.Location)\n    },\n    \"GeminiDemoScript\");\n```\n\nOnce there are no references to `newAssembly`, it will be eligible for garbage collection.\n\n### CodeEditor module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-codeeditor.png)\n\nUses AvalonEdit to provide syntax highlighting and other features for editing C# source files.\n\n#### Provides\n\n* `EditorProvider` for C# source files\n* `CodeEditor` control\n\n#### NuGet package\n\n* [Gemini.Modules.CodeEditor](http://nuget.org/packages/Gemini.Modules.CodeEditor/)\n\n#### Dependencies\n\n* [AvalonEdit](https://github.com/icsharpcode/SharpDevelop/wiki/AvalonEdit)\n\n#### Usage\n\nOpening a file with a `.cs` extension will automatically use the `CodeEditor` module to display\nthe document. You can also use the `CodeEditor` control in your own views:\n\n```xml\n\u003ccodeeditor:CodeEditor SyntaxHighlighting=\"C#\" /\u003e\n```\n\n### ErrorList module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-errorlist.png)\n\nReproduces the error list tool window from Visual Studio. Can be used to show errors, warning, or information.\n\n#### Provides\n\n* `IErrorList` tool window\n\n#### NuGet package\n\n* [Gemini.Modules.ErrorList](http://nuget.org/packages/Gemini.Modules.ErrorList/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\n```csharp\nvar errorList = IoC.Get\u003cIErrorList\u003e();\nerrorList.Clear();\nerrorList.AddItem(\n\tErrorListItemType.Error,\n\t\"Description of the error\",\n    @\"C:\\MyFile.txt\",\n    1,   // Line\n    20); // Column\n```\n\nYou can optionally provide a callback that will be executed when the user double-clicks on an item:\n\n```csharp\nerrorList.AddItem(\n\tErrorListItemType.Error,\n\t\"Description of the error\",\n    @\"C:\\MyFile.txt\",\n    1, // Line\n    20, // Character\n    () =\u003e\n    {\n        var openDocumentResult = new OpenDocumentResult(@\"C:\\MyFile.txt\");\n        IoC.BuildUp(openDocumentResult);\n        openDocumentResult.Execute(null);\n    });\n```\n\n### GraphEditor module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-grapheditor.png)\n\nImplements a general purpose graph / node editing UI. This module provides the UI controls - the logic\nand view models are usually specific to your application, and are left to you. The FilterDesigner sample\napplication (in the screenshot above) is one example of how it can be used.\n\nAlthough I implemented it slightly differently, I got a lot of inspiration and some ideas for the code \nfrom Ashley Davis's [CodeProject article](http://www.codeproject.com/Articles/182683/NetworkView-A-WPF-custom-control-for-visualizing-a).\n\n#### Provides\n\n* `GraphControl` control\n* `ConnectorItem` control\n* `BezierLine` control\n* `ZoomAndPanControl` control from [this CodeProject article](http://www.codeproject.com/Articles/85603/A-WPF-custom-control-for-zooming-and-panning)\n\n#### NuGet package\n\n* [Gemini.Modules.GraphEditor](http://nuget.org/packages/Gemini.Modules.GraphEditor/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\nYou'll need to create view models to represent:\n\n* the graph itself\n* elements\n* connectors\n* connections.\n\nI suggest looking at the \n[FilterDesigner sample application](https://github.com/tgjones/gemini/tree/master/src/Gemini.Demo.FilterDesigner)\nto get an idea of what's involved.\n\n### Inspector module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-inspector.png)\n\nSimilar in purpose to the property grid, but the Inspector module takes a more flexible approach.\nInstead of the strict \"two-column / property per row\" layout used in the standard PropertyGrid,\nthe Inspector module allows each editor to customise its own view.\n\nIt comes with the following editors:\n\n* BitmapSource\n* CheckBox\n* CollapsibleGroup\n* Color (WPF)\n* Enum\n* Point3D (WPF)\n* Range\n* TextBox\n\n#### Provides\n\n* `IInspectorTool` tool window\n* `InspectableObjectBuilder` class\n\n#### NuGet package\n\n* [Gemini.Modules.Inspector](http://nuget.org/packages/Gemini.Modules.Inspector/)\n\n#### Dependencies\n\n* [Extended WPF Toolkit](http://wpftoolkit.codeplex.com/) (for the colour picker)\n\n#### Usage\n\nYou can build up the inspector for an object in two ways:\n\n* Convention-based. The Inspector module can reflect over an object and create editors for the properties whose \n  types it recognises. It comes with built-in editors for `int`, `string`, `Enum`, etc.\n* Manually. Use the fluent interface on `InspectableObjectBuilder` to create editors.\n\nYou can also mix and match these approaches.\n\n```csharp\nvar inspectorTool = IoC.Get\u003cIInspectorTool\u003e();\ninspectorTool.SelectedObject = new InspectableObjectBuilder()\n\t.WithCollapsibleGroup(\"My Group\", b =\u003e b\n\t\t.WithColorEditor(myObject, x =\u003e x.Color))\n\t.WithObjectProperties(Shell.ActiveItem, pd =\u003e true) // Automatically adds browsable properties.\n\t.ToInspectableObject();\n```\n\n### Output module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-output.png)\n\nMuch like the output tool window from Visual Studio.\n\n#### Provides\n\n* `IOutput` tool window\n\n#### NuGet package\n\n* [Gemini.Modules.Output](http://nuget.org/packages/Gemini.Modules.Output/)\n\n#### Dependencies\n\n* None\n\n#### Usage\n\n```csharp\nvar output = IoC.Get\u003cIOutput\u003e();\noutput.AppendLine(\"Started up\");\n```\n\n### PropertyGrid module\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-module-propertygrid.png)\n\nPretty much does what it says on the tin. It uses the PropertyGrid control from the\nExtended WPF Toolkit.\n\n#### Provides\n\n* `IPropertyGrid` tool window\n\n#### NuGet package\n\n* [Gemini.Modules.PropertyGrid](http://nuget.org/packages/Gemini.Modules.PropertyGrid/)\n\n#### Dependencies\n\n* [Extended WPF Toolkit](http://wpftoolkit.codeplex.com/)\n\n#### Usage\n\n```csharp\nvar propertyGrid = IoC.Get\u003cIPropertyGrid\u003e();\npropertyGrid.SelectedObject = myObject;\n```\n\n## Sample application\n\nGemini.Demo showcases many of the available modules. The screenshot below shows the interactive script editor in action -\nas you type, the code will be compiled in real-time into a dynamic assembly and then executed in the same AppDomain.\n\nIt also includes a very basic example of a filter designer, built on the GraphEditor module.\n\n* [Source code](https://raw.github.com/tgjones/gemini/master/src/Gemini.Demo)\n\n![Screenshot](https://raw.github.com/tgjones/gemini/master/doc/gemini-demo.png)\n\n## What projects use Gemini?\n\nI've used Gemini on several of my own projects:\n\n* [Meshellator](http://github.com/tgjones/meshellator)\n* [Rasterizr](http://github.com/tgjones/rasterizr)\n* [SlimShader](http://github.com/tgjones/slimshader)\n* coming soon...\n\n## Acknowledgements\n\n* Many of the original ideas, and much of the early code came from [Rob Eisenberg](http://www.bluespire.com/), \n  creator of the [Caliburn Micro](http://caliburnmicro.codeplex.com/) framework. I have extended and modified \n  his code to integrate better with AvalonDock 2.0, which natively supports MVVM-style binding.\n* I used the VS2010 theme from [Edi](http://edi.codeplex.com/).\n\nGemini is not the only WPF framework for building IDE-like applications. Here are some others:\n\n* [SoapBox Core](http://soapboxautomation.com/products/soapbox-core-2/) - source [here](http://svn.soapboxcore.com/svn/),\n  but I think this project might be dead.\n* [Wide](https://github.com/chandramouleswaran/Wide/) - looks promising, and has a \n  [CodeProject article](http://www.codeproject.com/Articles/551885/How-to-create-a-VS-2012-like-application-Wide-IDE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftgjones%2Fgemini","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftgjones%2Fgemini","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftgjones%2Fgemini/lists"}