http://www.eclipse.org/articles/Article-UI-Workbench/workbench.html#mozTocId91
7303
Inside the Workbench
A guide to the workbench internals
Summary
This article describes how the Eclipse 3.1 workbench works, in particular the
infrastructure for views and editors. The goal is to teach you about important classes
in the workbench, and how they interact. A familiarity with the basic workbench APIs
for views, editors, action sets, and so forth is assumed.
Stefan Xenos, IBM
October 20, 2005
Table of Contents
• 1 Introduction
• 2 Inside a part
o 2.1 Part Lifecycle
o 2.2 Part Construction
• 3 Workbench Layout
o 3.1 An example layout
o 3.2 Zoom / Unzoom protocol
o 3.3 Layout protocol
o 3.4 PartStack: Communicating with the Presentation API
o 3.5 PartSashContainer: The main workbench layout
o 3.6 Drag / Drop
• 4 Action Bars
o 4.1 Editor Action Bars
o 4.2 Action Sets
o 4.3 View Actions
• 5 General Conventions
o 5.1 Objects must not be returned through API until they are fully
initialized
o 5.2 No method may open a modal dialog unless its JavaDoc says so
o 5.3 Lazy creation should happen as late as possible
o 5.4 getters should not modify the thing they are supposed to measure
1 Introduction
This document describes workbench internals and not API. The design of internals
changes frequently. For information on newer Eclipse versions, the latest version of
this document can be found on the UI development resources page .
Figure 1: Ownership of views and editors
Figure 1 shows how views and editors are owned by the workbench.
The Workbench contains one or more WorkbenchWindows, each of which contain
zero or more WorkbenchPages. The WorkbenchWindow supplies the trim widgets,
and the WorkbenchPage supplies the window contents. In theory, a
WorkbenchWindow can contain any number of pages, but in practice there is never
more than 1 page in a window.
Views and editors are owned by the page, through a ViewFactory and EditorManager
respectively. EditorManager stores the list of editors and their shared resources, and
ViewFactory stores a reference counted list of views. The workbench works in terms
of EditorReferences and ViewReferences, and in this article the terms "editor" or
"view" will refer to these classes specifically. In situations where the distinction
between editors and views is not important, we will simply use the term "part". The
implementation of the part (typically an IEditorPart or IViewPart) is created lazily
when it is first needed. As shown in Figure 2, a part reference exists for every tab but
the implementation is only created the first time it becomes visible.
The page owns a set of perspectives. Perspectives contain a layout and information
about what action sets to enable. Although perspectives appear to contain views and
the editor area, they only own a layout. The page itself maintains a reference count for
how many perspectives are using each view, and has complete ownership of the parts
and editor area.
Not shown in figure 1 are the classes PerspectiveHelper and EditorAreaHelper. These
classes exist largely for historic purposes, and in this article we will treat the former as
though it were part of the Perspective class and the latter as part of the
WorkbenchPage class.
Figure 2: Workbench objects and what they look like
2 Inside a part
Figure 3: Anatomy of a part
Internally, a part consists of several objects (Figure 3). WorkbenchPartReference is
the topmost representation of the part. Depending on where it is used, the part
reference is often exposed as IWorkbenchPartReference, IViewReference,
IEditorRefenece, or IPresentablePart, or PartPane. These are essentially different
interfaces to the same object. The I*Reference interfaces are implemented directly by
WorkbenchPartReference and its subclasses. IPresentablePart is a simple adapter that
redirects its methods directly to the part. PartPane implements the LayoutPart protocol
which is needed to insert the part into the workbench layout. PartPane also manages
the SWT resources (such as a top-level control) that are needed to include the control
in the workbench layout.
The part implementation (the IEditorPart or IViewPart) is owned by the reference.
When the implementation is created, it is given a PartSite. The PartSite (seen by client
code as an IWorkbenchPartSite, IEditorSite, or IViewSite) allows the client code to
communicate with the reference and manages services created for the implementation.
WorkbenchPartReferences allocates SWT resources lazily as needed. Once created,
the part reference must be explicitly disposed. Disposing the reference cleans up all of
its resources (including the part implementation itself) and guarantees that the
reference will never allocate additional resources. The workbench page disposes the
part reference once it is certain that it will never need to use that part again. Unlike
SWT controls, it is valid to continue using the reference after it has been disposed. A
disposed part reference is unlikely to do anything interesting besides returning its
name and cannot be used with any methods in the workbench page. Since it is hard
(or impossible) for clients to track the lifecycle of the reference, they are permitted to
continue using its public interface after disposal.
2.1 Part Lifecycle
Figure 4: WorkbenchPartReference states
Figure 4 shows the part lifecycle as a state machine. The part reference stores its
current state in the integer state field.
Notes:
• The part is in a distinct state while it is in the process of creating the
implementation. It cannot be recursively re-created or disposed while it is in
this state.
• The part implementation cannot be recreated once the reference has been
disposed.
• Parts cannot return to the lazy state once they have been created. This is a
limitation in the 3.1 implementation, not a functional requirement.
• It is valid to continue using the public interface of WorkbenchPartReference
once it has been disposed, however a disposed reference cannot be passed to
methods in workbench page (since it is, by definition, no longer part of any
page).
2.2 Part Construction
Figure 5: Message sequence for creating a part
Figure 5 shows the process for creating a part. The horizontal line separates the two
phases of creating a part. First the part is added to the layout, and then a real
implementation is attached. These are two distinct operations, and the part can exist as
a tab in the page with no implementation for some time before it becomes visible.
This diagram focuses on the interactions with the part reference, and skips the details
of adding the part to the presentation and creating the part site.
Suggestion: there are situations where it would be useful to only add the part to the
layout if it can be created successfully (this would be necessary to pass a
PartInitException thrown in the implementation's init method up through
IWorkbenchPage.openEditor). In these situations, it would be possible to merge both
operations into one atomic operation by creating the part before adding it to the layout.
It is unknown if this would create event ordering bugs in client code.
3 Workbench Layout
Figure 6: LayoutPart hierarchy
The workbench layout provides supports arranging parts using drag & drop, resizing
and detaching parts, fast views, etc. This section gives a quick overview of the layout
mechanism.
Anything in the workbench layout is a LayoutPart. A LayoutPart manages a set of
widgets in a rectangular region of the screen, can contain or arrange other layout parts,
returns size constraints, responds to drag events, etc. To this extent, a LayoutPart is
very similar to a custom SWT Control. However, LayoutPart differs from Control in
several important ways.
• The LayoutPart hierarchy is not the same as the widget hierarchy. Even
though one LayoutPart may contain another, their widgets may be peers. This
allows drag and drop to work on platforms where SWT doesn't support
reparenting, since a LayoutPart can be reparented without reparenting its
widgets.
• LayoutParts mainly perform layout-related tasks, unlike Controls which also
supply behavior and appearance. The behavior of a LayoutPart is supplied by
the widgets it arranges.
• LayoutParts know about higher-level concepts like zoom, and can specify
constraints about their own size.
Figure 6 shows the LayoutPart hierarchy. Notice the symmetry between the View*
classes and the Editor* classes. These classes exist largely for historical reasons, and
it should be possible to move all of the functionality into the Part* base classes or
other objects in the system. Since all of the interesting behavior comes from the Part*
classes, the remainder of this section will focus on them without describing the minor
differences between views and editors.
PartSashContainer arranges a set of child parts separated by a bunch of sashes. This is
the object that implements the most visible aspects of the workbench layout. It
arranges rectangular regions separated by sashes, and allows new regions to be
created by splitting old ones. It also supports the size constraints that make minimized
views possible, and determines what it means for one of these regions to be
maximized. Typically, a PartSashContainer contains a bunch of PartStacks, although
it is also possible for it to contain another PartSashContainer. The latter case occurs
since the editor area and the perspective both use a PartSashContainer for their layout
and one is embedded inside the other. PartSashContainer owns its stacks but does not
own an embedded PartSashContainer. In a workbench window, there is one
PartSashContainer for each perspective and one for the editor area itself.
PartStack arranges a stack of PartPanes. PartStack allows the presentation API to
participate in the workbench layout. The code for creating the tabs and arranging parts
is supplied by the active presentation.
PartPane allows views and editors to participate in the workbench layout. Although
PartPanes are arranged by PartStacks they belong to part reference, not the stack. The
same PartPane can exist in more than one PartStack at a time if that part appears in
more that one perspective.
3.1 An example layout
LayoutParts are best explained through example. Imagine a WorkbenchWindow that
contains custom Java and Java Browsing perspectives that look like Figure 7 and
Figure 8 respectively.
Figure 7: Example Java Perspective
Figure 8: Example Java Browsing perspective
Assume that the window resembles Figure 7. In this case, the Java perspective is
active, the Java Browsing perspective is hidden, and the objects are connected as
shown in Figure 9:
Figure 9: LayoutPart instances when two perspectives are open
All LayoutParts have a container pointer that points to the object that is currently
managing their position. Since the same LayoutPart instance may exist in more than
one perspective at once, this pointer points to the part's container in the
currently-active perspective. In the case of the projects view, above, the part is not in
the current perspective so its container pointer is null. When another perspective
becomes active, all the container pointers move to the new perspective. For historical
reasons, this is accomplished by setting and clearing the contianer pointer when the
container becomes visible or invisible. This works since only one perspective is
visible at a time, but it also means that perspectives cannot be manipulated when they
are invisible.
The diagram shows the internal objects that make up the only editor. Although this
detail has been omitted for the views, they would look similar. Each view's PartPane
is owned by a part reference which may have an associated part implementation.
3.2 Zoom / Unzoom protocol
The notion of "zoom" is defined locally between a part and its immediate container.
Zoom changes are triggered bottom-up. A part asks its parent to "zoom me", and the
parent either does something with the request or forwards the request up to its parent.
Each container determines what it means for a child to be zoomed. Once a part's zoom
state changes, its parent notifies it by calling setZoomed. The part may in turn zoom
or unzoom one or more of its children.
Anything that can contain LayoutParts must implement the following methods, to
support zooming:
/**
* Called by child parts to request a zoom in, given an immediate child
*
* @param toZoom part to zoom in on
*/
public void childRequestZoomIn(LayoutPart toZoom);
/**
* Called by child parts to request a zoom out
*/
public void childRequestZoomOut();
/**
* Returns true iff the given child is obscured due to the fact that the container is
zoomed into
* another part.
*
* @param toTest part to test
* @return true iff the part is currently obscured
*/
public boolean childObscuredByZoom(LayoutPart toTest);
/**
* Returns true iff we are zoomed into the given part, given an immediate child of
this container.
*
* @param toTest part to test
* @return true iff this contianer is currently zooming in on the given part
*/
public boolean childIsZoomed(LayoutPart toTest);
Consider again Figure 7. If we were to double-click on the java editor to zoom it, the
LayoutParts would send messages to one another in the following sequence. (In this
diagram, each cell represents a method call. Each column is an object. The reciever's
column shows the method name and the caller contains an arrow. Rows are in
ascending order of time.)
a Editor
tPane)
PartStack editor area
(EditorSashContainer)
ViewSashContainer
requestZoomIn
childRequestZoomIn(java
editor)
childRequestZoomIn(java
editor's part stack)
if there were other editor
stacks in the layout, we
would call
setVisible(false) on them
here
remember the partStack
as the zoomed part
setZoomed(true)
setZoomed(true)
childRequestZoomIn(editor
area)
call setVisible(false) on all
PartStacks for views in the
perspective
remember the editor area as
the zoomed part
setZoomed(true)
trigger a layout
setBounds(zoomed
bounds)
setBounds(zoomed
bounds)
setBounds(zoomed
bounds)
trigger a layout (nothing
to do)
3.3 Layout protocol
Every LayoutPart can specify constraints on their size. Parts specify constraints by
implementing the ISizeProvider interface. ISizeProvider serves a similar function as
the computeSize method on an SWT control, in that the parent layout uses it to
consult with the part when computing the part's size. ISizeProvider can provide a
variety of constraints:
Constraint
type
Meaning
Minimum
size
Given the available space along one dimension, the part returns the
minimum size that it can be compressed to along the other dimension.
For example, a stack would typically set its minimum size to be large
enough to fit its tabs. The information about available perpendicular
space could allow a stack to have wrapping tabs and still reserve enough
vertical space for the tabs once they are wrapped to fill the available
horizontal space.
Maximum
size
Given the available space along one dimension, the part returns the
maximum size that it can utilize along the other dimension. For
example, minimized stacks are implemented by setting their maximum
size to the minimized size. Non-minimized stacks typically have an
unbounded maximum size.
Preferred
size
Given the availble space along one dimension and a preferred size, this
returns the size closest to the preferred size at which the part would look
best. Parts can use this to quantize their size. For example, a part can
ensure that its size is always chosen such that it can be completely filled
with toolbar icons leaving no space left over.
Although there are three kinds of constraints, they are all returned using a single
method. See the JavaDoc on ISizeProvider for more information.
Whenever a size constraint changes, the part notifies its contianer by calling
resizeChild(part). This tells the container to respond to the new constraints, and to
resize the child if necessary (suggestion: if this is ever exposed as API, it would be
better to separate the concerns of notifying the parent of changes and triggering a
layout. In general, it may be possible for more than one part to change without
requiring a layout between each change).
3.4 PartStack: Communicating with the Presentation API
PartStack allows presentation to participate in the workbench layout. As shown in
Figure 10, it wraps a single instance of StackPresentation, and allows it the
presentation to manipulate parts by converting each visible part into an
IPresentablePart. The part stack outlives the StackPresentation. Whenever the part
stack needs widgets, it creates the StackPresentation. If the widgets are no longer
needed, it disposes the StackPresentation and remembers its persisted form. Whenever
the PartStack persists its StackPresenation, it remembers the presentation ID so that it
will not try to restore one StackPresentation from state saved by an incompatible
presentation.
Figure 10: Anatomy of PartStack
Suggestion: it would be useful to update this document with a state diagram for
PartStack, and message sequence charts for initializaiton and part activation.
3.5 PartSashContainer: The main workbench layout
Figure 11: PartSashContainer
PartSashContainer implements the main layout for a perspective and the editor area.
As shown in Figure 11, PartSashContainer contains a list of children, a (possibly null)
zoomed part, and a LayoutTree that manages the actual layout.
LayoutTree is a binary tree (technically, a KD tree). Each internal node is an instance
of LayoutTreeNode. It contains a draggable sash (LayoutPartSash) that divides its
area among its left and right children. LayoutTreeNodes can be horizontal or vertical
based on the orientation of the sash. For horizontal nodes, the "left" child is on top
and the "right" child is on the bottom. Leaf nodes are instances of LayoutTree (the
base class), and they point to a LayoutPart that occupies that region of the screen.
Normally, this is a PartStack, but in general it can be any LayoutPart.
Each LayoutTree occupies a recangular region of the screen that encompasses its
children. Internal nodes keep track of an integer size for each child (implementation
note: the sizes are stored in the associated LayoutPartSash, not the LayoutTreeNode
itself). Normally, the left and right sizes are used as a ratio for dividing up the
available space. However, when one child contains the editor area, the other becomes
"non-compressible". If one side in non-compressible, the size value is interpreted as a
fixed pixel size rather than a ratio. When the sash is moved, the size values are set to
the current pixel sizes of the left and right children.
Figure 12 shows an example LayoutTree. If this tree were rendered in a workbench
window, it would resemble Figure 3.5.3. The tall vertical sash separating the package
explorer from the editor area is the root node. The left child is the leaf node holding
the package explorer's stack. The right node is the horizontal sash separating the
problems view from the editor area. The editor area itself is a PartSashContainer,
which has its own LayoutTree. Note that the rounded rectangles in Figure 3.5.2 are
LayoutPar
本文档为【A guide to the workbench internals】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。