diff --git a/README.md b/README.md index 24590a2..2affaa4 100644 --- a/README.md +++ b/README.md @@ -20,40 +20,31 @@ Context Circle Menu is a simple open-source tool for Unity. It lets users open a ### Feature - Create Context Circle Menu - -![alt text](docs/image.png) - - Easy to use Editor Icon - - Customized Menu - - Add from Attribute - - ![alt text](docs/image-1.png) - - Add Manual - - ![alt text](docs/image-2.png) - + - Button design + - Folder design - Open in Scene View - Customized Shortcut Key -![alt text](docs/image-3.png) - ## Table of Contents - [Context Circle Menu](#context-circle-menu) - - [Overview](#overview) - - [Feature](#feature) - - [Table of Contents](#table-of-contents) - - [Setup](#setup) - - [Requirements](#requirements) - - [Installation](#installation) - - [Demonstration](#demonstration) - - [Editor Icons](#editor-icons) - - [Customized Menu](#customized-menu) - - [Manual Add Method](#manual-add-method) - - [Customized Button](#customized-button) - - [Customized Shortcut Key](#customized-shortcut-key) + - [Overview](#Overview) + - [Features](#Features) + - [Table of Contents](#Table-of-Contents) + - [Setup](#Setup) + - [Requirements](#Requirements) + - [Installation](#Installation) + - [Demonstration](#Demonstration) + - [Editor Icons](#editor-icons) + - [Add Manual Method](#Add-Manual-Method) + - [Customize](#Customize) + - [Customize Buttons](#Customize-Buttons) + - [Customize Folder](#Customize-Folder) + - [Customize Shortcut](#Customized-Shortcut-Key) + - [API Documentation](#API-Documentation) - [LISENCE](#lisence) - [AUTHOR](#author) @@ -121,7 +112,7 @@ Then you will see below menu. [![Image from Gyazo](https://i.gyazo.com/39b665e8fdd473bb408102e1b5d5bf09.gif)](https://gyazo.com/39b665e8fdd473bb408102e1b5d5bf09) -## Editor Icons +### Editor Icons Icons can be attached to menu buttons. @@ -136,9 +127,6 @@ public static void TestMethod() Debug.Log("TestMethod"); } ``` - -## Customized Menu - ### Manual Add Method If you do not want to use the `Context Circle Menu` attribute, you can register the method manually. @@ -161,25 +149,94 @@ public class Menu } ``` +## Customization + ### Customized Button -If you don't like the button UI, you can replace it with your own. +If you don't like the UI of the button, you can replace it with your own button. -Use `builder.ConfigureFolder();` +Use `builder.ConfigureButton` in `ContextCircleMenuLoader.OnBuild`. -> [!CAUTION] -> It is an incomplete feature. -> -> Destructive changes may be made. +```cs +ContextCircleMenuLoader.OnBuild += (builder => +{ + ... + builder.ConfigureButton(FolderMenuFactory); +}); +``` + +To create your own button, you must create a class that extends CircleButton and a corresponding Factory class. -First, you need to create a FolderMenu that extends `CircleMenu`. -You can create buttons freely in `CreateButtons`. -Please refer to `FolderCircleMenu.cs` for detailed code. +Here is an example code similar to the one provided in Sample's Custom. -Next, create a FolderMenuFactory that implements `IFolderCircleMenuFactory`. -Please refer to `CircleMenuFactory` for detailed code. +Samples can be imported from Package Manager > ContextCircleMenu > Samples. -Finally, you can replace the UI by doing the below. +In this example, the button is replaced with a button that displays only an icon. + +Create the following class + +```cs +public class CustomButtonFactory : IButtonFactory +{ + public CircleButton Create(string path, GUIContent icon, Action onSelected, int section) + { + return new OnlyImageCircleButton(path, icon, section, onSelected); + } + + // Back button is needed when creating a folder structure menu. + // section should be -1 unless ConfigureFolder is used. + public CircleButton CreateBackButton(Action onBack) + { + return new OnlyImageCircleButton("Back", EditorGUIUtility.IconContent(EditorIcons.Back2x), + -1, onBack); + } +} + +public class OnlyImageCircleButton : CircleButton +{ + public OnlyImageCircleButton(string text, GUIContent icon, int section, Action onSelect) : base(text, icon, section, onSelect) + { + } + + // You can edit the generated buttons. + // Feel free to customize the buttons here. + protected override void ModifierButton(Button button, string text, GUIContent icon, int section) + { + var image = new Image + { + image = icon.image, + style = + { + width = 32f, + height = 32f, + flexShrink = 0 + }, + tooltip = text + }; + + button.Add(image); + } +} + +``` + +Set the created Factory class. + +```cs +ContextCircleMenuLoader.OnBuild += (builder => +{ + ... + builder.ConfigureButton(new CustomButtonFactory()); +}); +``` + +It will then be replaced by a button that displays only the icon as shown below. + +![alt text](docs/image-6.png) + +### Customize Folder + +If you don't like the folder UI, you can replace it with your own folder. ```cs ContextCircleMenuLoader.OnBuild += (builder => @@ -189,7 +246,93 @@ ContextCircleMenuLoader.OnBuild += (builder => }); ``` -## Customized Shortcut Key +> [!CAUTION] +> Destructive changes were made in v1.0.0. + +To create your own folder, you need to create a class that extends FolderCircleMenu and a corresponding Factory class. + + +Here is an example code similar to the one provided in Sample's Custom. + +Samples can be imported from Package Manager > ContextCircleMenu > Samples. + +In this example, we are replacing an existing UI with vector graphics. + +Create the following classes + +```cs +public class CustomFolderMenuFactory : IFolderCircleMenuFactory +{ + public FolderCircleMenu Create(string path, IMenuControllable menu, CircleMenu parent, IButtonFactory factory) + { + return new CustomFolderCircleMenu(path, menu, parent, factory); + } +} + +public class CustomFolderCircleMenu : FolderCircleMenu +{ + public CustomFolderCircleMenu(string path, IMenuControllable menu, CircleMenu parent, IButtonFactory factory) : + base(path, menu, EditorGUIUtility.IconContent(EditorIcons.FolderIcon), parent, factory) + { + } + + protected override VisualElement[] CreateUtilityElements(ref ContextCircleMenuOption menuOption) + { + var element = new VisualElement(); + var option = menuOption; + element.generateVisualContent += context => + { + var painter = context.painter2D; + var buttonCount = ButtonElements.Length; + for (var i = 0; i < buttonCount; i++) + { + var angle = (float)i / buttonCount * 360f; + if (buttonCount % 2 == 1) + angle += 180f; + else + angle += 180f - 360f / buttonCount / 2; + var vector = new Vector2( + Mathf.Sin(Mathf.Deg2Rad * angle), + Mathf.Cos(Mathf.Deg2Rad * angle)).normalized; + + var from = vector * 12f; + var to = vector * option.Radius * 1.5f; + painter.strokeColor = Color.black; + painter.lineWidth = 2f; + painter.BeginPath(); + painter.MoveTo(from); + painter.LineTo(to); + painter.Stroke(); + } + + painter.BeginPath(); + painter.Arc(Vector2.zero, option.Radius * 1.5f, 0, 360f); + painter.fillColor = new Color(0f, 0f, 0f, 0.2f); + painter.Fill(); + + painter.DrawCircle(Vector2.zero, option.Radius * 1.5f, 0, 360f, 5f, Color.gray); + }; + return new[] { element }; + } +} +``` + +Set the created Factory class. + +```cs +ContextCircleMenuLoader.OnBuild += (builder => +{ + ... + builder.ConfigureButton(new CustomButtonFactory()); + builder.ConfigureFolder(new CustomFolderMenuFactory()); +}); +``` + +Then it will be replaced by the following UI. + +! [alt text](docs/image-5.png) + +### Customized Shortcut Key The default menu open/close button is set to the `A` key, but can be changed freely. @@ -201,6 +344,63 @@ Search for `Context Circle Menu` and you will find the settings as shown in the Set the keys as you like. +## API Documentation + +This section describes the major APIs and can be used as a reference when customizing the UI. + +### ContextCircleMenu + +#### property +| property name | description | +| ---- | ---- | +| BlockMouseEvents | Disables mouse operations such as clicking if true. | + +#### method +| method name | description | +| ---- | ---- | +| Show() | Show menu. | +| Hide() | Hide menu. | +| Open(CircleMenu menu) | Opens the menu specified in the argument. | +| Back() | Open the previous menu. | +| TryForceSelect() | If there is a button in focus, it is forced to select it. | +| TryForceEnterByMousePosition() | Forces the button corresponding to the mouse position to focus. | +| CreateMenu(Action\ configureMenu) | Create the menu content using CircleMenuBuilder. | + +### CircleMenuBuilder +#### method +| method name | description | +| ---- | ---- | +| AddMenu(ICircleMenuFactory factory) | Add custom menu. | +| AddMenu(string path, GUIContent content, Action action) | Add a menu manually. | +| AddMenu(ContextCircleMenuAttribute attribute, MethodInfo method) | Add a menu from the attributes. | +| ConfigureFolder(IFolderCircleMenuFactory factory) | Replace with your custom folder menu. | +| ConfigureButton(IButtonFactory factory) | Replace with your custom button. | + +### CircleMenu +#### abstract method +| method name | description | +| ---- | ---- | +| CreateButtons(IButtonFactory factory, ref ContextCircleMenuOption menuOption) | Create a button to be displayed on the menu. The IButtonFactory passed here will be the one set in CircleMenuBuilder.ConfigureButton(). | + +#### virtual method +| method name | description | +| ---- | ---- | +| CreateUtilityElements(ref ContextCircleMenuOption menuOption) | Create elements other than buttons. | +| OnInitialized(ref ContextCircleMenuOption menuOption) | Called at initialization. | +| OnBuild() | Called when an element is created. Mainly when Show() or Open() is called. | + +### CircleButton +#### abstract method +| method name | description | +| ---- | ---- | +| ModifierButton(Button button, string text, GUIContent icon, int section) | Called when creating a button. Use it to modify the elements of the button. | + +#### virtual method +| method name | description | +| ---- | ---- | +| OnMouseEnter(Button button, MouseEnterEvent evt) | Called when the mouse enters an element.| +| OnMouseLeave(Button button, MouseLeaveEvent evt) | Called when the mouse leaves an element. | + ## LISENCE MIT diff --git a/README_JA.md b/README_JA.md index 11f3ea2..e1db9dc 100644 --- a/README_JA.md +++ b/README_JA.md @@ -9,7 +9,7 @@ Context Circle Menu は、Sceneビュー上で円形のメニューを開き、 ## 概要 -Context Circle Menu はUnity用のシンプルなツールです。円形のメニューを開くことができるVisualElementを提供します。また、Sceneビュー上でメニューを開くローダー機能を搭載しています。このメニューにより、任意のメソッドを素早く使用することができます。円形にボタンが広がるため少ないマウス操作と直観的な理解で開発効率を極限まで高めることが可能です。 +Context Circle Menu はUnity用のシンプルなツールです。円形のメニューを開くことができるVisualElementを提供します。また、Sceneビュー上でメニューを開くローダー機能を搭載しています。このメニューは円形にボタンが広がるため少ないマウス操作と直観的な理解で任意のメソッドを実行させることができます。開発効率を極限まで高まることが期待できます。 [![Image from Gyazo](https://i.gyazo.com/8124142a3643fb0d735f7dd66b068142.gif)](https://gyazo.com/8124142a3643fb0d735f7dd66b068142) @@ -18,26 +18,15 @@ Context Circle Menu はUnity用のシンプルなツールです。円形のメ ### 特徴 - 円形メニューの作成 - -![alt text](docs/image.png) - - 使いやすいエディターアイコン - - メニューのカスタマイズ - - アトリビュートからメソッドを追加 - - ![alt text](docs/image-1.png) - - 手動でメソッドを追加 - - ![alt text](docs/image-2.png) - + - ボタンのデザイン + - フォルダーのデザイン - Sceneビュー上でメニューを開く - ショートカットキーのカスタマイズ -![alt text](docs/image-3.png) - ## 目次 - [Context Circle Menu](#context-circle-menu) - [概要](#概要) @@ -47,11 +36,13 @@ Context Circle Menu はUnity用のシンプルなツールです。円形のメ - [要求](#要求) - [インストール](#インストール) - [デモ](#デモ) - - [Editor Icons](#editor-icons) - - [メニューのカスタマイズ](#メニューのカスタマイズ) + - [Editor Icons](#editor-icons) - [手動メソッド追加](#手動メソッド追加) + - [カスタマイズ](#カスタマイズ) - [ボタンのカスタマイズ](#ボタンのカスタマイズ) - - [ショートカットキーのカスタマイズ](#ショートカットキーのカスタマイズ) + - [フォルダのカスタマイズ](#フォルダのカスタマイズ) + - [ショートカットキーのカスタマイズ](#ショートカットキーのカスタマイズ) + - [API ドキュメント](#API-ドキュメント) - [LISENCE](#lisence) - [AUTHOR](#author) @@ -83,7 +74,7 @@ https://github.com/Garume/ContextCircleMenu.git?path=/Assets/ContextCircleMenu ## デモ -追加したい静的メソッドに `Context Circle Menu` 属性を適用するだけで追加できます。 +静的メソッドに `ContextCircleMenu` 属性を適用するだけで追加できます。 ```cs public class Menu @@ -96,9 +87,9 @@ public class Menu } ``` -すると下のようにメニューが作成されます。 +下図のようにメニューが作成されます。 -Sceneビュー上で`A`キーを押すことでメニューを開けます。 +メニューはSceneビュー上で`A`キーを押すことで開くことができます。 [![Image from Gyazo](https://i.gyazo.com/1ec027f73700f52c6b3cd9691647a8a1.gif)](https://gyazo.com/1ec027f73700f52c6b3cd9691647a8a1) @@ -115,11 +106,11 @@ public class Menu } ``` -すると下のようにメニューが作成されます。 +すると下図のようにフォルダ階層付きのメニューが作成されます。 [![Image from Gyazo](https://i.gyazo.com/39b665e8fdd473bb408102e1b5d5bf09.gif)](https://gyazo.com/39b665e8fdd473bb408102e1b5d5bf09) -## Editor Icons +### Editor Icons メニューボタンにアイコンを付けることができます。 @@ -135,13 +126,11 @@ public static void TestMethod() } ``` -## メニューのカスタマイズ - ### 手動メソッド追加 -もし `Context Circle Menu` 属性を使いたくない場合は、手動でメソッドを登録することができます。 +`Context Circle Menu` 属性を使用する以外に、手動で追加することもできます。 -内部的にメソッドを登録するプロセスを`ContextCircleMenuLoader.OnBuild`でフックすることができます。 +内部的にメソッドを登録するプロセスを`ContextCircleMenuLoader.OnBuild`でフックすることができます。これを利用して以下のように記述します。 ```cs public class Menu @@ -159,25 +148,98 @@ public class Menu } ``` +## カスタマイズ + + ### ボタンのカスタマイズ ボタンのUIが気に入らなければ、独自のボタンに置き換えることができます。 -`builder.ConfigureFolder();`を使ってください。 +`ContextCircleMenuLoader.OnBuild`内で`builder.ConfigureButton`を使用してください。 -> [!CAUTION] -> これは未完成の機能です。 -> -> 破壊的な変更が加えられる可能性があります。 +```cs +ContextCircleMenuLoader.OnBuild += (builder => +{ + ... + builder.ConfigureButton(FolderMenuFactory); +}); +``` + +独自のボタンを作成するためには、CircleButtonを継承したクラスとそれに対応したFactoryクラスを作成する必要があります。 + +ここでは、SampleのCustomで提供しているものと同様のコードを例として紹介します。 + +SampleはPackage Manager > ContextCircleMenu > Samplesからインポートすることができます。 -まず、`CircleMenu`を継承したFolderMenuを作成します。 -独自のボタンは `CreateButtons` を通して自由に作成してください。 -詳しいコードは `FolderCircleMenu.cs` を参照してください。 +この例では、アイコンのみ表示するボタンに置き換えています。 -次に `IFolderCircleMenuFactory` を実装した FolderMenuFactory を作成します。 -詳しいコードは `CircleMenuFactory` を参照してください。 +以下のクラスを作成してください。 -最後に、以下のようにすることでUIの置き換えが完了します。 +```cs +public class CustomButtonFactory : IButtonFactory +{ + public CircleButton Create(string path, GUIContent icon, Action onSelected, int section) + { + return new OnlyImageCircleButton(path, icon, section, onSelected); + } + + // フォルダ構造のメニューを作成する際に戻るボタンが必要です。 + // ConfigureFolderを利用しない限りsectionは-1にしてください。 + public CircleButton CreateBackButton(Action onBack) + { + return new OnlyImageCircleButton("Back", EditorGUIUtility.IconContent(EditorIcons.Back2x), + -1, onBack); + } +} + +public class OnlyImageCircleButton : CircleButton +{ + public OnlyImageCircleButton(string text, GUIContent icon, int section, Action onSelect) : base(text, icon, section, onSelect) + { + } + + // 生成したボタンを編集することができます。 + // ここで自由にボタンをカスタマイズしてください。 + protected override void ModifierButton(Button button, string text, GUIContent icon, int section) + { + var image = new Image + { + image = icon.image, + style = + { + width = 32f, + height = 32f, + flexShrink = 0 + }, + tooltip = text + }; + + button.Add(image); + } +} + +``` + +作成したFactoryクラスを設定します。 + +```cs +ContextCircleMenuLoader.OnBuild += (builder => +{ + ... + builder.ConfigureButton(new CustomButtonFactory()); +}); +``` + +すると以下のようにアイコンのみ表示するボタンに置き換わります。 + +![alt text](docs/image-6.png) + + +### フォルダのカスタマイズ + +フォルダのUIが気に入らなければ、独自のフォルダに置き換えることができます。 + +`ContextCircleMenuLoader.OnBuild`内で`builder.ConfigureFolder`を使用してください。 ```cs ContextCircleMenuLoader.OnBuild += (builder => @@ -187,7 +249,93 @@ ContextCircleMenuLoader.OnBuild += (builder => }); ``` -## ショートカットキーのカスタマイズ +> [!CAUTION] +> v1.0.0で破壊的な変更が加えられました。 + +独自のフォルダを作成するためには、FolderCircleMenuを継承したクラスとそれに対応したFactoryクラスを作成する必要があります。 + + +ここでは、SampleのCustomで提供しているものと同様のコードを例として紹介します。 + +SampleはPackage Manager > ContextCircleMenu > Samplesからインポートすることができます。 + +この例では、既存のUIにベクターグラフィックスを加えたものに置き換えています。 + +以下のクラスを作成してください。 + +```cs +public class CustomFolderMenuFactory : IFolderCircleMenuFactory +{ + public FolderCircleMenu Create(string path, IMenuControllable menu, CircleMenu parent, IButtonFactory factory) + { + return new CustomFolderCircleMenu(path, menu, parent, factory); + } +} + +public class CustomFolderCircleMenu : FolderCircleMenu +{ + public CustomFolderCircleMenu(string path, IMenuControllable menu, CircleMenu parent, IButtonFactory factory) : + base(path, menu, EditorGUIUtility.IconContent(EditorIcons.FolderIcon), parent, factory) + { + } + + protected override VisualElement[] CreateUtilityElements(ref ContextCircleMenuOption menuOption) + { + var element = new VisualElement(); + var option = menuOption; + element.generateVisualContent += context => + { + var painter = context.painter2D; + var buttonCount = ButtonElements.Length; + for (var i = 0; i < buttonCount; i++) + { + var angle = (float)i / buttonCount * 360f; + if (buttonCount % 2 == 1) + angle += 180f; + else + angle += 180f - 360f / buttonCount / 2; + var vector = new Vector2( + Mathf.Sin(Mathf.Deg2Rad * angle), + Mathf.Cos(Mathf.Deg2Rad * angle)).normalized; + + var from = vector * 12f; + var to = vector * option.Radius * 1.5f; + painter.strokeColor = Color.black; + painter.lineWidth = 2f; + painter.BeginPath(); + painter.MoveTo(from); + painter.LineTo(to); + painter.Stroke(); + } + + painter.BeginPath(); + painter.Arc(Vector2.zero, option.Radius * 1.5f, 0, 360f); + painter.fillColor = new Color(0f, 0f, 0f, 0.2f); + painter.Fill(); + + painter.DrawCircle(Vector2.zero, option.Radius * 1.5f, 0, 360f, 5f, Color.gray); + }; + return new[] { element }; + } +} +``` + +作成したFactoryクラスを設定します。 + +```cs +ContextCircleMenuLoader.OnBuild += (builder => +{ + ... + builder.ConfigureButton(new CustomButtonFactory()); + builder.ConfigureFolder(new CustomFolderMenuFactory()); +}); +``` + +すると以下のようなUIに置き換わります。 + +![alt text](docs/image-5.png) + +### ショートカットキーのカスタマイズ デフォルトのメニュー開閉ボタンは`A`キーに設定されていますが、自由に変更することができます。 @@ -198,6 +346,63 @@ ContextCircleMenuLoader.OnBuild += (builder => 好きなキーに設定してください。 +## API ドキュメント + +ここでは主要なAPIを説明します。UIをカスタマイズする際の参考にしてください。 + +### ContextCircleMenu + +#### プロパティ +| プロパティ名 | 説明 | +| ---- | ---- | +| BlockMouseEvents | trueの場合にクリック等のマウス操作を無効化します | + +#### メソッド +| メソッド名 | 説明 | +| ---- | ---- | +| Show() | メニューを表示します。 | +| Hide() | メニューを非表示にします。 | +| Open(CircleMenu menu) | 引数に指定したメニューを開きます。 | +| Back() | 前回のメニューを開きます。 | +| TryForceSelect() | フォーカスされているボタンがあれば強制的に選択します。 | +| TryForceEnterByMousePosition() | マウスの位置に対応するボタンを強制的にフォーカスします。 | +| CreateMenu(Action\ configureMenu) | メニュー内容をCircleMenuBuilderを用いて作成します。 | + +### CircleMenuBuilder +#### メソッド +| メソッド名 | 説明 | +| ---- | ---- | +| AddMenu(ICircleMenuFactory factory) | 独自のメニューを追加します。 | +| AddMenu(string path, GUIContent content, Action action) | 手動でメニューを追加します。 | +| AddMenu(ContextCircleMenuAttribute attribute, MethodInfo method) | 属性からメニューを追加します。 | +| ConfigureFolder(IFolderCircleMenuFactory factory) | 独自のフォルダーメニューに置き換えます。 | +| ConfigureButton(IButtonFactory factory) | 独自のボタンに置き換えます。 | + +### CircleMenu +#### 抽象メソッド +| メソッド名 | 説明 | +| ---- | ---- | +| CreateButtons(IButtonFactory factory, ref ContextCircleMenuOption menuOption) | メニューに表示するボタンを作成します。ここで渡されるIButtonFactoryはCircleMenuBuilder.ConfigureButton()で設定したものになります。 | + +#### 仮想メソッド +| メソッド名 | 説明 | +| ---- | ---- | +| CreateUtilityElements(ref ContextCircleMenuOption menuOption) | ボタン以外の要素を作成します。 | +| OnInitialized(ref ContextCircleMenuOption menuOption) | 初期化時に呼ばれます。 | +| OnBuild() | 要素作成時に呼ばれます。主にShow()やOpen()が呼ばれた時です。 | + +### CircleButton +#### 抽象メソッド +| メソッド名 | 説明 | +| ---- | ---- | +| ModifierButton(Button button, string text, GUIContent icon, int section) | ボタン作成時に呼ばれます。ボタンの要素を編集するために用いてください。 | + +#### 仮想メソッド +| メソッド名 | 説明 | +| ---- | ---- | +| OnMouseEnter(Button button, MouseEnterEvent evt) | マウスが要素内に入った際に呼ばれます。| +| OnMouseLeave(Button button, MouseLeaveEvent evt) | マウスが要素内から出た際に呼ばれます。 | + ## LISENCE MIT diff --git a/docs/image-5.png b/docs/image-5.png new file mode 100644 index 0000000..b69830e Binary files /dev/null and b/docs/image-5.png differ diff --git a/docs/image-6.png b/docs/image-6.png new file mode 100644 index 0000000..977da2e Binary files /dev/null and b/docs/image-6.png differ