Introduce new APIs for managing focus and using focus to inform delivery of keyboard events.
Use new APIs to implement the following behavior:
Focus:
- If the component browser is opened, its initial state is *focused*.
- If the node input area's text component is clicked, the component browser's state becomes *blurred*.
- If a click occurs anywhere in the component browser, the component browser's state becomes *focused*.
Event dispatch:
- When the component browser is in the *focused* state, it handles certain keyboard events (chiefly, arrow keys).
- If the component browser handles an event, the event is not received by other components.
- If an event occurs that the component browser doesn't handle, the node input area's text component receives the event.
[vokoscreenNG-2023-06-29_10-55-00.webm](https://github.com/enso-org/enso/assets/1047859/f1d9d07c-8c32-4482-ba32-15b6e4e20ae7)
# Important Notes
Changes to display object interface:
- **`display::Object` can now be derived.**
- Introduce display object *focus receiver* concept. Many components, when receiving focus, should actually be focused indirectly by focusing a descendant.
- For example, when the CB Panel receives focus, its descendant at `self.model().grid.model().grid` should be focused, because that's the underlying Grid View, which has its own event handlers. By allowing each level of the hierarchy to define a `focus_receiver`, focus can reach the right object without the CB panel having to know structural details of its descendants.
- When delegating to a field's `display::Object` implementation, the derived implementation uses the child's `focus_receiver`, which will normally be the correct behavior.
**Changes to `shortcut` API**:
- New `View::focused_shortcuts()` is a focus-aware alternative to `View::default_shortcuts()` (which should now only be used for global shortcuts, i.e. shortcuts that don't depend on whether the component is focused). It's based on the *Keyboard Event* API (see below), so events propagate up the focus hierarchy until a shortcut is executed and `stop_propagation()` is called; this allows sensible resolution of event targets when more than one component is capable of handling the same keypress.
Keypress dataflow overview:
DOM -> KeyboardManager -> FrpKeyboard -> KeyboardEvents -> Shortcut.
Low-level keyboard changes to support Focus:
- New `KeyboardManager`: Attaches DOM event handlers the same way as `MouseManager`.
- New *Keyboard Event* API: `on_event::<KeyDown>()`. Events propagate up the focus hierarchy. This API is used for low-level keyboard listeners such a `Text`, which may need complex logic to determine whether a key is handled (rather than having a closed set of bindings, which can be handled by `shortcut`).
- FRP keyboard: Now attaches to the `KeyboardManager` API. It now serves primarily to produce Keyboard Events (it still performs the role of making `KeyUp` events saner in a couple different ways). The FRP keyboard can also be used directly as a global keyboard, for such things as reacting to modifier state.
Misc:
- Updated the workspace `syn` to version 2. Crates still depending on legacy `syn` now do so through the workspace-level `syn_1` alias.
Adds a new bare-bones AI searcher that can be triggered with `cmd+tab`. It will interpret the searcher input as a prompt to an AI model and replace the created node with the suggestion that was computed.
https://github.com/enso-org/enso/assets/1428930/f8403533-54ba-4ea5-9d3c-6bdf3cf336b5
Implements the first step of #7099.
# Important Notes
Contains some refactoring that allows us to have multiple controllers side by side. So QA testing should make sure that the Component Browser Searcher is still working as before.
Implements #6544 (eliminates 10/42 of the constantly-displayed draw calls).
Fixes#6717. Improves startup CPU time by 5% (250ms, loading Orders on my dev box).
# Important Notes
- Edges: New implementation uses only Rectangle under most conditions.
- Node and action area: Replace some shapes with Rectangle.
- List view: Replace some shapes with Rectangle.
- Display object hierarchy: The lowest-level shape instance types no longer have their own display objects.
- Includes initial support for using `Rectangle` to display triangles.
* Remove unused code: project management in component browser
* Encapsulate internal FRP logic of project list
* Collapse some code paths
* Open project passed on command line through presenter
A project name or ID that is passed on the command line was initialised
in the controller setup, before the presenters and views are set up.
Now, we fully initialise the IDE before opening a project so we have
control over the view while a project is being opened.
* Show a spinner in all cases of opening a project
* Let root presenter open/close projects when switching projects
* Change spinner to make progress over a fixed period
* Resolve issues when Project Manager API isn't available
* Bump wasm size limit
Refactored the logic behind selecting appropriate widgets for span tree nodes. Now the bulk of it is moved into widget methods. When a given widget type is reporting to be not compatible with the expression, it will not be used even if the configuration was overriden using an method argument annotation. In that case, the usual logic for automatically selecting the appropriate widget will kick in.
![image](https://github.com/enso-org/enso/assets/919491/6316e21e-c509-4cc4-a3a6-c482798894d0)
# Important Notes
The mouse handling changes involve an unfortunate huge hack, where we enable mouse events on the mouse shape during box selection. That way we know for sure that no other shape will be able to receive mouse enter event. Then the list editor widget is modified to only actually respond to events when its background is hovered. We will definitely want a more proper way to handle mouse event contention, but it's definitely out of scope for current bugfixing.
Rewrites node input component. Now the input is composed of multiple widget components arranged in a tree of views with automatic layout. That allows creating complex UI elements on top of the node itself, and further widget positions will be automatically adapted to that. The tree roughly follow the span tree, as it is built by consuming its nodes and eagerly creating widgets from them. The tree is rebuilt every time the expression changes, but that rebuild process reuses as much previously created widgets as possible, and only updates their configuration as needed. Each widget type can have its own configuration options that can be passed to it from the parent, or assigned based on configuration received from the language server.
<img width="773" alt="image" src="https://user-images.githubusercontent.com/919491/233439310-9c39ea88-19bc-43da-9baf-1bb176e2724e.png">
# Important Notes
For now, all span-tree updates are sent over to the shared Frp endpoint of the whole tree, so there is no mechanism for intercepting them by the parent widgets. One idea would be to use existing bubbling/capturing events on widget display objects for that purpose, but I think existing implementation is simpler and more convenient, and we can always easily change that if we have a use for it.
There are some issues with performance due to much more display objects being created on the graph. Expect it to be a little worse, especially at initialization time.
Provides functionality necessary for:
- opening URLs in the system browser (so that we can handle OAuth flows outside of the app)
- handling deep links to the application (so that the OAuth flows can return the user to the app)
### Important Notes
- Modifies `preload.ts` to expose the ability to open the system browser to the sandboxed parts of the app.
- Modifies `election-builder-config.ts` to register a deep link URL protocol scheme with the OS.
- Modifies the client's `index.ts` to register a handler for Electron `open-url` events
Implements [#183453466](https://www.pivotaltracker.com/story/show/183453466).
https://user-images.githubusercontent.com/1428930/203870063-dd9c3941-ce79-4ce9-a772-a2014e900b20.mp4
# Important Notes
* the best laziness is used for `Text` type, which makes use of its internal representation to send data
* any type will first compute its default string representation and then send the content of that lazy to the IDE
* special handling of files and their content will be implemented in the future
* size of the displayed text can be updated dynamically based on best effort information: if the backend does not yet know the full width/height of the text, it can update the IDE at any time and this will be handled gracefully by updating the scrollbar position and sizes.
[Task link](https://www.pivotaltracker.com/story/show/184012434)
This PR implements Intermediate Representation for our documentation. Later these data structures would be used to generate HTML and CSS for the documentation panel. For now, we display it in the debug scene.
https://user-images.githubusercontent.com/6566674/210674850-480a3e6e-76c3-4f34-a235-15c44dc9ec01.mp4
# Important Notes
- `suggestion-database` now lives in a separate crate
- also, two utility crates were introduced for the `notification` and `executor` modules of enso-gui
- documentation debug scene is moved to a separate crate
- All refactorings are done in the last two commits
Implements https://www.pivotaltracker.com/n/projects/2539304/stories/184023445
Added a dropdown widget to graph node for all span tree nodes that have tag values present. When an option is selected, the controller receives a partial expression update, which targets specific crumbs of the expression (similar to how edge endpoint updates work).
https://user-images.githubusercontent.com/919491/210219931-8ae418fd-3ac4-44a5-abea-9e670f15cdf9.mp4
# Important Notes
Right now the dropdown widget is recreated every time the node is edited, including a dropdown option being selected. This causes it to close every time. I wanted to get around that by diffing span trees, but I wasn't able to do it in useful way. Additionally, current implementation of node input expression view heavily relies on being reinitialized from scratch every time. This led to more necessary changes than I was comfortable with for this task. I believe it will be easier to implement it as part of more complete widget support, especially after dynamic data support, as we will have proper widget type information.
Implements https://www.pivotaltracker.com/n/projects/2539304/stories/184023380
Dropdown component. Planned to be used in nodes as a single and multiple selection widget, both for static and dynamically loaded values. Initial support is focused on static data, with limited support for dynamic sources. Notably, loading states are not supported yet. Full support for that is planned to be added later with widget lazy-loading.
- Supports single and multiple selections.
- Dedicated API for providing a static list of all entries.
- Range-based query API for dynamically loading data as it is scrolled (only basic support - will need more work for proper async lazy-loading).
- Internal entry cache and query batching to avoid querying data one by one (the batching for now is very basic, will have to be improved for proper lazy-loading).
- Automatic dropdown width adjustment based on the entry label lengths, up to a set max allowed value.
- Open and close animation.
- Keyboard support for focusing and selecting entries.
![image](https://user-images.githubusercontent.com/919491/207866293-de2e3fef-c93b-48cc-8253-11c186d223fd.png)
# Important Notes
Implementing the dropdown on top of grid-view have uncovered some assumptions around grid-view layers. It was assumed to always be a part of the component browser. Removing that assumption required a mechanism for propagating camera update information through layer tree. This is now implemented using a `camera_parent` layer field. Ideally each layer should simply have at most a single parent, and camera inheritance would follow that. That refactor turned out to be quite involved, so right now the simpler temporary solution is introduced in order to not delay this PR further.
This is an enhancement of the `Slider` component implemented in #3852. It adds the following features:
* Tooltips and precision change hints
* Selectable slider limit behaviors
* Textual slider value editing
* Vertical slider layout
#### Tooltips
An information tooltip can now be added to a slider, it is shown when the mouse hovers over the component. Additionally, a pop-up indicating the slider's precision appears when the slider's precision has been adjusted.
https://user-images.githubusercontent.com/117099775/206148098-3b4dc059-18aa-4200-9ee0-5d4382363810.mp4
#### Slider limits
The previous slider implementation clamped the adjusted value to the slider's minimum/maximum limits. Now the following behaviors are available:
* Hard limits: Clamp the value to a range within the slider's limits.
* Soft limits: The value can extend beyond the slider's limits. When this occurs, an overflow indicator will be displayed on the side of the limit that is exceeded.
* Adaptive limits: The value can extend beyond the slider's limits. When this occurs, the exceeded limit will temporarily be adjusted to double the slider's range. This will be performed iteratively until the value falls within the extended limits. When a limit is extended and the value is adjusted to fit a smaller range, the extended limit will be iteratively halved until only the necessary range is covered. The slider's extended limits will never shrink to a range smaller than the original range.
These behaviors can be set to the lower and upper limits of a slider independently.
https://user-images.githubusercontent.com/117099775/206148139-6149c91d-ef49-4e2d-97f6-71084f52591c.mp4
#### Textual editing
The slider's value can now be entered through a text input field. Double-click to edit the slider's current value. To confirm the edit press `enter`, or press `escape` to cancel the edit. If an invalid value is entered on confirmation the slider will revert to its value before the edit. The slider's precision will be adjusted based on the number of decimal places of the value entered.
https://user-images.githubusercontent.com/117099775/206148170-d3fa4c82-6e73-4b1c-9be9-cb99979f7b70.mp4
#### Vertical layout
The slider component now supports a vertical layout. In this case value adjustment is performed by a vertical mouse movement, and a horizontal movement adjusts the slider's precision. The slider's track now fills the component in a vertical direction, and the slider's label is displayed near the top end of the component.
https://user-images.githubusercontent.com/117099775/206148211-0f176aaf-bc1b-45e2-afd7-0d28391aafcb.mp4
#### Scroll bar mode
The slider component supports two indicator modes:
* `Track`: The component is filled with a colored bar from the lower limit (empty) to the upper limit (full) dependent on the slider's value.
* `Thumb`: The component contains a rounded indicator that moves along the slider from one end to the other, indicating the slider's value proportionally to the slider's limits. The width of the indicator is configurable.
In addition, the value text, text entry, and precision adjustment can be turned off to provide a scroll bar appearance when used with the `Thumb` indicator.
https://user-images.githubusercontent.com/117099775/206148261-ae291073-85e9-4082-9f91-39b65fecdc0f.mp4
#### Example scene shortcuts
The example scene contains two shortcuts in order to evaluate the dynamic addition and removal of the slider components:
* `CTRL+D` drops all the slider components that are added to the scene.
* `CTRL+A` adds a new set of example slider components to the scene.
This PR is a draft PR while I learn EnsoGL. The eventual goal is to implement the projects list portion of the cloud dashboard in this PR. This PR will implement part of https://www.pivotaltracker.com/n/projects/2539513/stories/183557950
### Important Notes
This PR is still really rough and contains a lot of hacks & hard-coded values. The FRP usage is also likely to be suboptimal and need fixing.
This `Slider` component allows adjusting a numeric value with the mouse. The value is increased or decreased by clicking on the component and dragging it to the left or right.
The `Slider` has a configurable default value. `Ctrl`+clicking on the component resets its value to that default. When the value is moved away from the default, the value is printed in **bold**.
The `Slider` precision is increased or decreased by clicking the component and dragging upward or downward. This precision influences how quickly the value changes when the mouse moves horizontally, the steps in which the value is incremented or decremented, and the number of digits used to display the value. There is a margin around the component within which the precision is not changed. Beyond this margin, the precision is increased or decreased in powers of 10 (e.g. `0.1` -> `0.01` -> `0.001` when moving the mouse downwards, or `0.1` -> `1.0` -> `10.0` when moving the mouse upwards). The margin and distance between consecutive steps along the vertical axis are configurable.
The value of the `Slider` is limited to a configurable range, and cannot be adjusted beyond that range. A colored bar fills the component to indicate the current value within the range.
#### Video demonstration
https://user-images.githubusercontent.com/117099775/202244982-2f6f419d-7281-41f6-8607-7e492ad25b46.mp4
#### Future additions
This is the first iteration of the `Slider` component. Additional features are planned for the future:
* Textual editing of the value.
* Improved visual feedback on precision changes.
* Additional out-of-range behaviors.
Split `HasOutputTypeLabel::output_type_label` method implementation non-generic part into a separate function. This significantly reduces compile time without risking any performance regressions. That function is currently only used for debug network visualization using graphviz, but still contributed a significant compilation time.
I've made a single attempt to profile the compiler itself, and it turned out that the compiler spent a significant amount of time trying to resolve `Pattern` implementation for closures in that method. Each of those also had to separately go through codegen and optimization. That happened for each node type in the codebase, per crate. Moving that into separate non-inline function removed all those unnecessary duplicates from. I also took this opportunity to rewrite that small piece of parsing to make it a bit cleaner.
The method of measurement is explained here: https://blog.rust-lang.org/inside-rust/2020/02/25/intro-rustc-self-profile.html
In this specific case, I've used `self-profile` to generate a profile for all crates, then used `crox` and [perfetto](https://ui.perfetto.dev/) to analyze the output.
This also shows a pattern to be aware of - using closures in generic context forces the compiler to make a separate closure type per each instantiation, even if that closure doesn't close on anything and could be a static function. This in effect forces even more instantiations or unnecessary type resolutions for all code paths that touch that closure. Using static functions or separating the non-generic part away in those cases would likely continue to help with compile times and file size.
## Comparison
The measurement was done on same machine under same environment, cleaning the build artifacts inbetween runs.
| | before | after |
|-|-|-|
|total build time|5m 5.5s|3m 59.0s|
| `ide-view-graph-editor` crate build time| 85.68s | 49.73s |
| `ide-view-component-list-panel-grid` crate build time | 49.88s | 32.07s |
| `ide-view` crate build time | 29.05s | 17.5s |
| `enso_gui.wasm` file size before wasm-opt | 83.6 MB | 80.9 MB |
| `enso_gui.wasm` file size after wasm-opt | 67.3 MB | 65.3 MB |
![image](https://user-images.githubusercontent.com/919491/199633193-64dada16-eb22-4020-8d31-3f24661497aa.png)
[ci no changelog needed]
[Task link](https://www.pivotaltracker.com/story/show/182675703)
This PR implements the actual integration of the breadcrumbs panel with the component list panel. A special breadcrumbs controller (`controller::searcher::breadcrumbs`) is tracking the currently opened module with a list of its parents. The searcher presenter uses the API of the controller to sync the displayed list of breadcrumbs with the controller state.
https://user-images.githubusercontent.com/6566674/193064122-7d3fc4d6-9148-4ded-a73e-767ac9ac83f8.mp4
# Important Notes
- There is an `All` breadcrumb displayed at all times at the beginning of the list. It will be replaced with a section name as part of [Section Title on Component Browser's Breadcrumbs Panel](https://www.pivotaltracker.com/story/show/182610561) task.
- I changed the implementation of `project::main_module_id`, `project::QualifiedName::main_module`, and `API::main_module` so that they are logically connected which each other.
- I adjusted the Breadcrumbs View to avoid "appearance" animation glitches when opening new modules. `set_entries` was replaced with the `set_entries_from` endpoint.
When a GridView is navigated using the keyboard, scroll it to display the newly selected entry.
https://www.pivotaltracker.com/story/show/182593635
#### Visuals
See below for a video demonstrating automatic scrolling of the GridView when arrow keys are pressed on the keyboard. The first video below shows the default scrolling behavior in a GridView without headers.
Note:
- When the Grid View is scrolled, the mouse hover highlight moves away from the mouse position. This is not a new regression, the behavior is the same in the `develop` branch (e.g. when scrolling using the mouse).
https://user-images.githubusercontent.com/273837/190183984-91f7808c-3606-43f8-bcda-ac4d5f84e00f.mov
The video below shows the behavior in a GridView with headers when the GridView is first scrolled to its top-left corner. The following guidance from the Design Doc is enabled and showcased:
> Users can change the selected component by pressing the arrow keys. The Focus does not move up if it does not have to (in most cases, the focus is located in the second row from the bottom). Instead, the component list scrolls down if there are enough entries.
https://user-images.githubusercontent.com/273837/189151546-e50aaf22-6f4d-41cb-809f-60038305745f.mov
The next video shows the behavior in the same GridView as in the previous example when the GridView is first scrolled away from any of its boundary entries. Notably, scrolling happens only when the selection is moved using the keyboard arrow keys, not when changing the selection using the mouse. This behavior is based on a discussion with @wdanilo on Discord.
https://user-images.githubusercontent.com/273837/189151974-d992be93-f61f-4e9f-9f4c-dfe260bbec5b.mov
This PR contains an entry definition for Grid View to be used inside Component List Panel View. The Example grid view with the entry definition may be seen on new_component_list_panel_view debug scene.
https://user-images.githubusercontent.com/3919101/190663278-23c35ab0-f426-4001-8128-df7147aafb9e.mp4
# Important Notes
* The styling is not detailed yet due to time constraints (I want to move to integration this grid view to Component Panel List ASAP) and the fact that I could not get new mplus1 font working with text Area.
* Implementing this required adding a "contour offset" feature to the Grid View.
This PR reenables code signing and notarization on macOS.
[ci no changelog needed]
# Important Notes
* electron-builder has been bumped, mostly to avoid missing Python issue. A workaround for a regression with Windows installer is provided as a patch.
[ci no changelog needed]
[Task link](https://www.pivotaltracker.com/story/show/181445628).
This PR implements a Breadcrumbs panel for the new component browser.
The Breadcrumbs is a horizontal list of text labels separated by a special icon and has an optional ellipsis icon at the end.
It is implemented using the new GridView component.
Video:
Demo of adding new breadcrumbs, scrolling behavior, and selecting breadcrumbs with the mouse.
https://user-images.githubusercontent.com/6566674/189199432-77807cef-00dc-4abe-b95c-b17a536f59f6.mp4
Demo of selecting breadcrumbs with keyboard shortcuts:
https://user-images.githubusercontent.com/6566674/189199603-53e55335-73ba-4ed7-8291-4455144c06aa.mp4
# Important Notes
- This PR implements an old interaction of the design of the component browser. The new design of the breadcrumbs can not be easily integrated into the current look of the component browser, so we would need to update styles later. It should be a relatively simple task. *The implementation uses color from the new design though. (but not fonts and sizes)*
- I found a bug in the grid view implementation that causes panics at runtime in some conditions. The reason is triggering FRP endpoints while constructing new entries. This issue is fixed in the PR.
[ci no changelog needed]
[Task link](https://www.pivotaltracker.com/story/show/182955595)
This PR implements variable column widths in the new Grid View component. We need this feature to quickly implement various parts of the UI, including the breadcrumbs panel of the component browser.
There are two ways to change the width of the specific column:
1. "From the outside", using the `set_column_width` endpoint of the Grid View
2. "From the inside", using the `override_column_width` endpoint of the EntryFrp.
Both ways work similarly, but the latter is helpful for our breadcrumbs implementation, as it allows for entry to decide on the width of the column by its content.
See the screencast with three grid views. The top-left one has every even column shrunk by GridView API. Every grid view has a second column extended by EntryFrp API.
https://user-images.githubusercontent.com/6566674/185060985-7b7df076-c659-41fa-977a-22875493f8d4.mp4
Show custom icons in Component Browser for entries that have a non-empty `Icon` section in their docs with the section's body containing a name of a predefined icon.
https://www.pivotaltracker.com/story/show/182584336
#### Visuals
A screenshot of a couple custom icons in the Component Browser:
<img width="346" alt="Screenshot 2022-07-27 at 15 55 33" src="https://user-images.githubusercontent.com/273837/181265249-d57f861f-8095-4933-9ef6-e62644e11da3.png">
# Important Notes
- The PR assigns icon names to four items in the standard library, but only three of them are shown in the Component Browser because of [a parsing bug in the Engine](https://www.pivotaltracker.com/story/show/182781673).
- Icon names are assigned only to four items in the standard library because only two currently predefined icons match entries in the currently defined Virtual Component Groups. Adjusting the definitions of icons and Virtual Component Groups is covered by [a different task](https://www.pivotaltracker.com/story/show/182584311).
- A bug in the documentation of the Enso protocol message `DocSection` is fixed. A `text` field in the `Tag` interface is renamed to `body` (this is the field name used in Engine).
This PR adds a new variant of selection, where the mouse-hovered entry is highlighted and may be selected by clicking.
In the video below, we have three grid views with slightly different settings:
* In the left-top corner, both hover and selection highlight is just a shape under the label. Such a grid view does not require additional layers (when compared to non-selectable grid view).
* In the left-bottom corner the hover is normal shape, but selection is a _masked layer_ which allows us to have different text color. This setting requires three more layers to render.
* In the right-top corner, both hover and selection are displayed in the masked layer, creating 6 additional layers.
https://user-images.githubusercontent.com/3919101/181514178-f243bfeb-f2dd-4507-adc3-5344ae0579b7.mp4
**Note**: This PR also contains content of previous Grid View PR. We decided to discard the previous, because this one did some refactoring of old one, and it's not a big addition.
Added a scrollable::GridView component, which just embeds the GridView in ScrollArea. Also, re-worked the idea of text layers.
https://user-images.githubusercontent.com/3919101/179020359-512ee127-c333-4f86-bff5-f1cb4154e03c.mp4