NAME

Prima::Docks - dockable widgets

DESCRIPTION

The module contains a set of classes and an implementation of dockable widgets interface. The interface assumes two parties, the dockable widget and the dock widget; the generic methods for the dock widget class are contained in Prima::AbstractDocker::Interface package.

USAGE

A dockable widget is required to take particular steps before it can dock to a dock widget. It needs to talk to the dock and find out if it is allowed to land, or if the dock contains lower-level dock widgets that might suit better for docking. If there's more than one dock widget in the program, the dockable widget can select between the targets; this is especially actual when a dockable widget is dragged by mouse and the arbitration is performed on geometrical distance basis.

The interface implies that there exists at least one tree-like hierarchy of dock widgets, linked up to a root dock widget. The hierarchy is not required to follow parent-child relationships, although this is the default behavior. All dockable widgets are expected to know explicitly what hierarchy tree they wish to dock to. Prima::InternalDockerShuttle introduces dockingRoot property for this purpose.

The conversation between parties starts when a dockable widget calls open_session method of the dock. The dockable widget passes set of parameters signaling if the widget is ready to change its size in case the dock widget requires so, and how. open_session method can either refuse or accept the widget. In case of the positive answer from open_session, the dockable widget calls query method, which either returns a new rectangle, or another dock widget. In the latter case, the caller can enumerate all available dock widgets by repetitive calls to next_docker method. The session is closed by close_session call; after that, the widget is allowed to dock by setting its owner to the dock widget, the rect property to the negotiated position and size, and calling dock method.

open_session/close_session brackets are used to cache all necessary calculations once, making query call as light as possible. This design allows a dockable widget, when dragged, repeatedly ask all reachable docks in an optimized way. The docking sessions are kept open until the drag session is finished.

The conversation can be schematized in the following code:

my $dock = $self-> dockingRoot;
my $session_id = $dock-> open_session({ self => $self });
return unless $session_id;
my @result = $dock-> query( $session_id, $self-> rect );
if ( 4 == scalar @result) {       # new rectangle is returned
	if ( ..... is new rectangle acceptable ? ... ) {
		$dock-> close_session( $session_id);
		$dock-> dock( $self);
		return;
	}
} elsif ( 1 == scalar @result) {  # another dock returned
	my $next = $result[0];
	while ( $next) {
		if ( ... is new docker acceptable? ....) {
			$dock-> close_session( $session_id);
			$next-> dock( $self);
			return;
		}
		$next = $dock-> next_docker( $session_id, $self-> origin );
	}
}
$dock-> close_session( $session_id);

Since even the simplified code is quite cumbersome, direct calls to open_session are rare. Instead, Prima::InternalDockerShuttle implements find_docking method which performs the arbitration automatically and returns the appropriate dock widget.

Prima::InternalDockerShuttle is a class that implements dockable widget functionality. It also employs a top-level window-like wrapper widget for the dockable widget when it is not docked. By default, Prima::ExternalDockerShuttle is used as the wrapper widget class.

It is not required, however, to use neither Prima::InternalDockerShuttle nor Prima::AbstractDocker::Interface to implement a dockable widget; the only requirements to one is to respect open_session/close_session protocol.

Prima::InternalDockerShuttle initiates a class hierarchy of dockable widgets. Its descendants are Prima::LinearWidgetDocker and, in turn, Prima::SingleLinearWidgetDocker. Prima::SimpleWidgetDocker and Prima::LinearWidgetDocker, derived from Prima::AbstractDocker::Interface, begin hierarchy of dock widgets. The full hierarchy is as follows:

Prima::AbstractDocker::Interface
	Prima::SimpleWidgetDocker
	Prima::ClientWidgetDocker
	Prima::LinearWidgetDocker
	Prima::FourPartDocker

Prima::InternalDockerShuttle
	Prima::LinearDockerShuttle
	Prima::SingleLinearWidgetDocker

Prima::ExternalDockerShuttle

All docker widget classes are derived from Prima::AbstractDocker::Interface. Depending on the specialization, they employ more or less sophisticated schemes for arranging dockable widgets inside. The most complicated scheme is implemented in Prima::LinearWidgetDocker; it does not allow children overlapping and is able to rearrange with children and resize itself when a widget is docked or undocked.

The package provides only basic functionality. Module Prima::DockManager provides common dockable controls, - toolbars, panels, speed buttons etc. based on Prima::Docks module. See Prima::DockManager.

Prima::AbstractDocker::Interface

Implements generic functionality of a docket widget. The class is not derived from Prima::Widget; is used as a secondary ascendant class for dock widget classes.

Properties

Since the class is not Prima::Object descendant, it provides only run-time implementation of its properties. It is up to the descendant object whether the properties are recognized on the creation stage or not.

fingerprint INTEGER

A custom bit mask, to be used by docking widgets to reject inappropriate dock widgets on early stage. The fingerprint property is not part of the protocol, and is not required to be present in a dockable widget implementation.

Default value: 0x0000FFFF

dockup DOCK_WIDGET

Selects the upper link in dock widgets hierarchy tree. The upper link is required to be a dock widget, but is not required to be a direct or an indirect parent. In this case, however, the maintenance of the link must be implemented separately, for example:

$self-> dockup( $upper_dock_not_parent );

$upper_dock_not_parent-> add_notification( 'Destroy', sub {
	return unless $_[0] == $self-> dockup;
	undef $self-> {dockup_event_id};
	$self-> dockup( undef );
}, $self);

$self-> {destroy_id} = $self-> add_notification( 'Destroy', sub {
	$self-> dockup( undef );
} unless $self-> {destroy_id};

Methods

add_subdocker SUBDOCK

Appends SUBDOCK to the list of lower-level docker widgets. The items of the list are returned by next_docker method.

check_session SESSION

Debugging procedure; checks SESSION hash, warns if its members are invalid or incomplete. Returns 1 if no fatal errors were encountered; 0 otherwise.

close_session SESSION

Closes docking SESSION and frees the associated resources.

dock WIDGET

Called after WIDGET is successfully finished negotiation with the dock widget and changed its owner property. The method adapts the dock widget layout and lists WIDGET into list of docked widgets. The method does not change owner property of WIDGET.

The method must not be called directly.

dock_bunch @WIDGETS

Effectively docks set of WIDGETS by updating internal structures and calling rearrange.

docklings

Returns array of docked widgets.

next_docker SESSION, [ X, Y ]

Enumerates lower-level docker widgets within SESSION; returns one docker widget at a time. After the last widget returns undef.

The enumeration pointer is reset by query call.

X and Y are coordinates of the point of interest.

open_session PROFILE

Opens docking session with parameters stored in PROFILE and returns session ID scalar in case of success, or undef otherwise. The following keys must be set in PROFILE:

position ARRAY

Contains two integer coordinates of the desired position of a widget in (X,Y) format in screen coordinate system.

self WIDGET

Widget that is about to dock.

sizeable ARRAY

Contains two boolean flags, representing if the widget can be resized to an arbitrary size, horizontally and vertically. The arbitrary resize option used as last resort if sizes key does not contain the desired size.

sizeMin ARRAY

Two integers; minimal size that the widget can accept.

sizes ARRAY

Contains arrays of points in (X,Y) format; each point represents an acceptable size of the widget. If sizeable flags are set to 0, and none of sizes can be accepted by the dock widget, open_session fails.

query SESSION [ X1, Y1, X2, Y2 ]

Checks if a dockable widget can be landed into the dock. If it can, returns a rectangle that the widget must be set to. If coordinates ( X1 .. Y2 ) are specified, returns the rectangle closest to these. If sizes or sizeable keys of open_session profile were set, the returned size might be different from the current docking widget size.

Once the caller finds the result appropriate, it is allowed to change its owner to the dock; after that, it must change its origin and size correspondingly to the result, and then call dock.

If the dock cannot accept the widget, but contains lower-lever dock widgets, returns the first lower-lever widget. The caller can use subsequent calls to next_docker to enumerate all lower-level dock widgets. A call to query resets the internal enumeration pointer.

If the widget cannot be landed, an empty array is returned.

rearrange

Effectively re-docks all the docked widgets. The effect is as same as of

$self-> redock_widget($_) for $self-> docklings;

but usually rearrange is faster.

redock_widget WIDGET

Effectively re-docks the docked WIDGET. If WIDGET has redock method in its namespace, it is called instead.

remove_subdocker SUBDOCK

Removes SUBDOCK from the list of lower-level docker widgets. See also add_subdocker.

replace FROM, TO

Assigns widget TO same owner and rectangle as FROM. The FROM widget must be a docked widget.

undock WIDGET

Removes WIDGET from list of docked widgets. The layout of the dock widget can be changed after execution of this method. The method does not change owner property of WIDGET.

The method must not be called directly.

Prima::SimpleWidgetDocker

A simple dock widget; accepts any widget that geometrically fits into. Allows overlapping of the docked widgets.

Prima::ClientWidgetDocker

A simple dock widget; accepts any widget that can be fit to cover all dock's interior.

Prima::LinearWidgetDocker

A toolbar-like docking widget class. The implementation does not allow tiling, and can reshape the dock widget and rearrange the docked widgets if necessary.

Prima::LinearWidgetDocker is orientation-dependent; its main axis, governed by vertical property, is used to align docked widgets in 'lines', which in turn are aligned by the opposite axis ( 'major' and 'minor' terms are used in the code for the axes ).

Properties

growable INTEGER

A combination of grow::XXX constants, that describes how the dock widget can be resized. The constants are divided in two sets, direct and indirect, or, vertical property independent and dependent.

The first set contains explicitly named constants:

grow::Left       grow::ForwardLeft       grow::BackLeft
grow::Down       grow::ForwardDown       grow::BackDown
grow::Right      grow::ForwardRight      grow::BackRight
grow::Up         grow::ForwardUp         grow::BackUp

that select if the widget can be grown to the direction shown. These do not change meaning when vertical changes, though they do change the dock widget behavior. The second set does not affect dock widget behavior when vertical changes, however the names are not that illustrative:

grow::MajorLess  grow::ForwardMajorLess  grow::BackMajorLess
grow::MajorMore  grow::ForwardMajorMore  grow::BackMajorMore
grow::MinorLess  grow::ForwardMinorLess  grow::BackMinorLess
grow::MinorMore  grow::ForwardMinorMore  grow::BackMinorMore

Forward and Back prefixes select if the dock widget can be respectively expanded or shrunk in the given direction. Less and More are equivalent to Left and Right when vertical is 0, and to Up and Down otherwise.

The use of constants from the second set is preferred.

Default value: 0

hasPocket BOOLEAN

A boolean flag, affects the possibility of a docked widget to reside outside the dock widget inferior. If 1, a docked wigdet is allowed to stay docked ( or dock into a position ) further on the major axis ( to the right when vertical is 0, up otherwise ), as if there's a 'pocket'. If 0, a widget is neither allowed to dock outside the inferior, nor is allowed to stay docked ( and is undocked automatically ) when the dock widget shrinks so that the docked widget cannot stay in the dock boundaries.

Default value: 1

vertical BOOLEAN

Selects the major axis of the dock widget. If 1, it is vertical, horizontal otherwise.

Default value: 0

Events

Dock

Called when dock is successfully finished.

DockError WIDGET

Called when dock is unsuccessfully finished. This only happens if WIDGET does not follow the docking protocol, and inserts itself into a non-approved area.

Undock

Called when undock is finished.

Prima::SingleLinearWidgetDocker

Descendant of Prima::LinearWidgetDocker. In addition to the constraints, introduced by the ascendant class, Prima::SingleLinearWidgetDocker allows only one line ( or row, depending on vertical property value ) of docked widgets.

Prima::FourPartDocker

Implementation of a docking widget, with its four sides acting as 'rubber' docking areas.

Properties

indents ARRAY

Contains four integers, specifying the breadth of offset for each side. The first integer is width of the left side, the second - height of the bottom side, the third - width of the right side, the fourth - height of the top side.

dockerClassLeft STRING

Assigns class of left-side dock window.

Default value: Prima::LinearWidgetDocker. Create-only property.

dockerClassRight STRING

Assigns class of right-side dock window.

Default value: Prima::LinearWidgetDocker. Create-only property.

dockerClassTop STRING

Assigns class of top-side dock window.

Default value: Prima::LinearWidgetDocker. Create-only property.

dockerClassBottom STRING

Assigns class of bottom-side dock window.

Default value: Prima::LinearWidgetDocker. Create-only property.

dockerClassClient STRING

Assigns class of center dock window.

Default value: Prima::ClientWidgetDocker. Create-only property.

dockerProfileLeft HASH

Assigns hash of properties, passed to the left-side dock widget during the creation.

Create-only property.

dockerProfileRight HASH

Assigns hash of properties, passed to the right-side dock widget during the creation.

Create-only property.

dockerProfileTop HASH

Assigns hash of properties, passed to the top-side dock widget during the creation.

Create-only property.

dockerProfileBottom HASH

Assigns hash of properties, passed to the bottom-side dock widget during the creation.

Create-only property.

dockerProfileClient HASH

Assigns hash of properties, passed to the center dock widget during the creation.

Create-only property.

dockerDelegationsLeft ARRAY

Assigns the left-side dock list of delegated notifications.

Create-only property.

dockerDelegationsRight ARRAY

Assigns the right-side dock list of delegated notifications.

Create-only property.

dockerDelegationsTop ARRAY

Assigns the top-side dock list of delegated notifications.

Create-only property.

dockerDelegationsBottom ARRAY

Assigns the bottom-side dock list of delegated notifications.

Create-only property.

dockerDelegationsClient ARRAY

Assigns the center dock list of delegated notifications.

Create-only property.

dockerCommonProfile HASH

Assigns hash of properties, passed to all five dock widgets during the creation.

Create-only property.

Prima::InternalDockerShuttle

The class provides a container, or a 'shuttle', for a client widget, while is docked to an Prima::AbstractDocker::Interface descendant instance. The functionality includes communicating with dock widgets, the user interface for dragging and interactive dock selection, and a client widget container for non-docked state. The latter is implemented by reparenting of the client widget to an external shuttle widget, selected by externalDockerClass property. Both user interfaces for the docked and the non-docked shuttle states are minimal.

The class implements dockable widget functionality, served by Prima::AbstractDocker::Interface, while itself it is derived from Prima::Widget only.

See also: "Prima::ExternalDockerShuttle".

Properties

client WIDGET

Provides access to the client widget, which always resides either in the internal or the external shuttle. By default there is no client, and any widget capable of changing its parent can be set as one. After a widget is assigned as a client, its owner and clipOwner properties must not be used.

Run-time only property.

dock WIDGET

Selects the dock widget that the shuttle is landed on. If undef, the shuttle is in the non-docked state.

Default value: undef

dockingRoot WIDGET

Selects the root of dock widgets hierarchy. If undef, the shuttle can only exist in the non-docked state.

Default value: undef

See "USAGE" for reference.

externalDockerClass STRING

Assigns class of external shuttle widget.

Default value: Prima::ExternalDockerShuttle

externalDockerModule STRING

Assigns module that contains the external shuttle widget class.

Default value: Prima::MDI ( Prima::ExternalDockerShuttle is derived from Prima::MDI ).

externalDockerProfile HASH

Assigns hash of properties, passed to the external shuttle widget during the creation.

fingerprint INTEGER

A custom bit mask, used to reject inappropriate dock widgets on early stage.

Default value: 0x0000FFFF

indents ARRAY

Contains four integers, specifying the breadth of offset in pixels for each widget side in the docked state.

Default value: 5,5,5,5.

snapDistance INTEGER

A maximum offset, in pixels, between the actual shuttle coordinates and the coordinates proposed by the dock widget, where the shuttle is allowed to land. In other words, it is the distance between the dock and the shuttle when the latter 'snaps' to the dock during the dragging session.

Default value: 10

x_sizeable BOOLEAN

Selects whether the shuttle can change its width in case the dock widget suggests so.

Default value: 0

y_sizeable BOOLEAN

Selects whether the shuttle can change its height in case the dock widget suggests so.

Default value: 0

Methods

client2frame X1, Y1, X2, Y2

Returns a rectangle that the shuttle would occupy if its client rectangle is assigned to X1, Y1, X2, Y2 rectangle.

dock_back

Docks to the recent dock widget, if it is still available.

drag STATE, RECT, ANCHOR_X, ANCHOR_Y

Initiates or aborts the dragging session, depending on STATE boolean flag.

If it is 1, RECT is an array with the coordinates of the shuttle rectangle before the drag has started; ANCHOR_X and ANCHOR_Y are coordinates of the aperture point where the mouse event occurred that has initiated the drag. Depending on how the drag session ended, the shuttle can be relocated to another dock, undocked, or left intact. Also, Dock, Undock, or FailDock notifications can be triggered.

If STATE is 0, RECT, ANCHOR_X ,and ANCHOR_Y parameters are not used.

find_docking DOCK, [ POSITION ]

Opens a session with DOCK, unless it is already opened, and negotiates about the possibility of landing ( at particular POSITION, if this parameter is present ).

find_docking caches the dock widget sessions, and provides a possibility to select different parameters passed to open_session for different dock widgets. To achieve this, GetCaps request notification is triggered, which fills the parameters. The default action sets sizeable options according to x_sizeable and y_sizeable properties.

In case an appropriate landing area is found, Landing notification is triggered with the proposed dock widget and the target rectangle. The area can be rejected on this stage if Landing returns negative answer.

On success, returns a dock widget found and the target rectangle; the widget is never docked though. On failure returns an empty array.

This method is used by the dragging routine to provide a visual feedback to the user, to indicate that a shuttle may or may not land in a particular area.

frame2client X1, Y1, X2, Y2

Returns a rectangle that the client would occupy if the shuttle rectangle is assigned to X1, Y1, X2, Y2 rectangle.

redock

If docked, undocks form the dock widget and docks back. If not docked, does not perform anything.

Events

Dock

Called when shuttle is docked.

EDSClose

Triggered when the user presses close button or otherwise activates the close function of the EDS ( external docker shuttle ). To cancel the closing, clear_event must be called inside the event handler.

FailDock X, Y

Called after the dragging session in the non-docked stage is finished, but did not result in docking. X and Y are the coordinates of the new external shuttle position.

GetCaps DOCK, PROFILE

Called before the shuttle opens a docking session with DOCK widget. PROFILE is a hash reference, which is to be filled inside the event handler. After that PROFILE is passed to open_session call.

The default action sets sizeable options according to x_sizeable and y_sizeable properties.

Landing DOCK, X1, Y1, X2, Y2

Called inside the docking session, after an appropriate dock widget is selected and the landing area is defined as X1, Y1, X2, Y2. To reject the landing on either DOCK or area, clear_event must be called.

Undock

Called when shuttle is switched to the non-docked state.

Prima::ExternalDockerShuttle

A shuttle class, used to host a client of Prima::InternalDockerShuttle widget when it is in the non-docked state. The class represents an emulation of a top-level window, which can be moved, resized ( this feature is not on by default though ), and closed.

Prima::ExternalDockerShuttle is inherited from Prima::MDI class, and its window emulating functionality is a subset of its ascendant. See also Prima::MDI.

Properties

shuttle WIDGET

Contains reference to the dockable WIDGET

Prima::LinearDockerShuttle

A simple descendant of Prima::InternalDockerShuttle, used for toolbars. Introduces orientation and draws a tiny header along the minor shuttle axis. All its properties concern only the way the shuttle draws itself.

Properties

headerBreadth INTEGER

Breadth of the header in pixels.

Default value: 8

indent INTEGER

Provides a wrapper to indents property; besides the space for the header, all indents are assigned to indent property value.

vertical BOOLEAN

If 1, the shuttle is drawn as a vertical bar. If 0, the shuttle is drawn as a horizontal bar.

Default value: 0

AUTHOR

Dmitry Karasik, <dmitry@karasik.eu.org>.

SEE ALSO

Prima, Prima::Widget, Prima::MDI, Prima::DockManager, examples/dock.pl