blob: 383b9047782c0713b6381e480367cef6faef1851 [file] [log] [blame]
<!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!-- For better readability, Please open this file in the browser -->
**Design Doc of Placeholder**
*Last updated: Jan 17th 2019*
Overview
=====
`Placeholder` is designed for drag-and-drop interaction in Layout Editor. It is a replacement of all `DragTarget`s.
`Placeholder` describes the area for dropping mouse, how component snaps it, and the callback function when dropping the component on it.
The callback function is used for setting the attributes after dropping the Widget.
We also has `CommonDragTarget` which takes responsibilities to update component position and interact with
`Placeholder`s.
How It Works
=====
When user starts dragging a component, we create a `CommonDragTarget` which captures the mouse position and collect `Placeholder`s from
`Scene`. `CommonDragTarget` snaps to the collected `Placeholder`s and updates the positions of the dragged component. Once the mouse is
released, `CommonDragTarget` finds the chosen `Placeholder` and applies the callback of it to update the attributes.
Implement a `Placeholder`
=====
For creating a new Placeholder, simply extends the `Placeholder` interface and custom the part you need.
This interface contains 6 overridable functions:
(optional) `val associatedComponent: SceneComponent = host`
----
When the mouse is hovered on a Placeholder, all other Placeholder which have the same container as hovered Placeholder would be
highlighted.
By default the associatedComponent is same as the host. One exception is the CoordinatorLayout, where anchor[1] Placeholders are created by
CoordinatorLayout but their associatedComponent is the anchor. The reason why this exception exists is because when dragging a widget to an
anchor Placeholder, the dragged component should be added to the CoordinatorLayout, but the user will interact with the anchor thus the
visual effects should displayed at the anchor position.<br>
(Note: Anchor is used to place floating views relative to other arbitrary content panes in Coordinator Layout.)
(optional) `val isLiveUpdatable = false`
---
Determine if this Placeholder updates the attributes during dragging. The updated attributes are pending in the `AttributesTransaction`
which is committed after mouse released.
(optional) `val dominate = true`
----
Determine if this Placeholder is visual visible and has higher priority than other Placeholder which are in the same level but not visible.
For example, all anchor Placeholder are dominate in Coordinator Layout. But the non-anchor Placeholder which add component to Coordinator
Layout without anchor is not dominate.
Non-dominated Placeholder can be treated as an background placeholder.
`val region: Region`
-----
For describing the receivable area. `CommonDragTarget` renders this region as well.
It also has `level` property, which is used to determine the priority when regions are overlap each other. The higher level has higher
priority. For now all region is a rectangle.
(optional) `fun findNextSibling(appliedComponent: SceneComponent, newParent: SceneComponent): SceneComponent?`
-----
If the dragged widget has to be inserted to specific position in Xml tree, return the next sibling of the inserted component.
By default the component would keep the same position as before, or append to the end of file if it is a new Widget in such View Group.
For example, LinearPlaceholder overrides this value since Linear Layout needs to change the order of its Widgets.
`fun snap(info: SnappingInfo, retPoint: Point): Boolean`
-----
The arguments describe the expected area of dragged widget. `snap` function checks if the dragged widget should effect to this `Placeholder`
(optional) `fun updateLiveAttribute(SceneComponent, AttributeHolder, Int, Int)`
-----
Callback for updating specified attributes when this `Placeholder` is applied during dragging.
`fun updateAttributes(SceneComponent, AttributeHolder, AttributeHolder)`
-----
Callback for updating specified attributes when this `Placeholder` is applied after releasing the mouse.
After having a custom `Placeholder`, override `getPlaceholders(SceneComponent)` function in associated `ViewHandler` to provide them.<br>
The `CommonDragTarget` will collect and interact with them automatically.
References
=====
[DesignDoc](https://docs.google.com/document/d/1HbxrqHMkdzjFAYhVJikp6bQVpBbaUsjmALtLpg-g_iw/edit?usp=sharing)
<!-- Markdeep: --><style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="markdeep.min.js"></script><script src="https://casual-effects.com/markdeep/latest/markdeep.min.js"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>