Permits lesser-used settings (e.g. cutting/embankment) to be grouped under disclosure triangles.
This is pretty much the second part of the work to remove the clutter of having too many tabs.
Still needs some styling work!
--- /dev/null
+/*
+
+The MIT License
+
+Copyright (c) 2007-2008 Ali Rantakari of hasseg.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+package net.systemeD.controls {
+
+ import flash.events.*;
+ import mx.effects.AnimateProperty;
+ import mx.events.*;
+ import mx.containers.Panel;
+ import mx.core.ScrollPolicy;
+
+ /**
+ * The icon designating a "closed" state
+ */
+ [Style(name="closedIcon", property="closedIcon", type="Object")]
+
+ /**
+ * The icon designating an "open" state
+ */
+ [Style(name="openIcon", property="openIcon", type="Object")]
+
+ /**
+ * This is a Panel that can be collapsed and expanded by clicking on the header.
+ *
+ * @author Ali Rantakari
+ */
+ public class CollapsiblePanel extends Panel {
+
+
+
+ private var _creationComplete:Boolean = false;
+ private var _open:Boolean = true;
+ private var _openAnim:AnimateProperty;
+
+
+
+ /**
+ * Constructor
+ *
+ */
+ public function CollapsiblePanel(aOpen:Boolean = true):void
+ {
+ super();
+ open = aOpen;
+ this.addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);
+ }
+
+
+
+
+
+
+
+
+ // BEGIN: event handlers ------------------------------------------------------------
+
+ private function creationCompleteHandler(event:FlexEvent):void
+ {
+ this.horizontalScrollPolicy = ScrollPolicy.OFF;
+ this.verticalScrollPolicy = ScrollPolicy.OFF;
+
+ _openAnim = new AnimateProperty(this);
+ _openAnim.duration = 300;
+ _openAnim.property = "height";
+
+ titleBar.addEventListener(MouseEvent.CLICK, headerClickHandler);
+
+ _creationComplete = true;
+ }
+
+ private function headerClickHandler(event:MouseEvent):void { toggleOpen(); }
+
+ private function callUpdateOpenOnCreationComplete(event:FlexEvent):void { updateOpen(); }
+
+ // --end--: event handlers - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+
+
+
+
+
+
+ // BEGIN: private methods ------------------------------------------------------------
+
+ // sets the height of the component without animation, based
+ // on the _open variable
+ private function updateOpen():void
+ {
+ if (!_open) height = closedHeight;
+ else height = openHeight;
+ setTitleIcon();
+ }
+
+ // the height that the component should be when open
+ private function get openHeight():Number {
+ return measuredHeight;
+ }
+
+ // the height that the component should be when closed
+ private function get closedHeight():Number {
+ var hh:Number = getStyle("headerHeight");
+ if (hh <= 0 || isNaN(hh)) hh = titleBar.height;
+ return hh;
+ }
+
+ // sets the correct title icon
+ private function setTitleIcon():void
+ {
+ if (!_open) this.titleIcon = getStyle("closedIcon");
+ else this.titleIcon = getStyle("openIcon");
+ }
+
+ // --end--: private methods - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+
+
+
+
+
+
+ // BEGIN: public methods ------------------------------------------------------------
+
+
+
+ /**
+ * Collapses / expands this block (with animation)
+ */
+ public function toggleOpen():void
+ {
+ if (_creationComplete && !_openAnim.isPlaying) {
+
+ _openAnim.fromValue = _openAnim.target.height;
+ if (!_open) {
+ _openAnim.toValue = openHeight;
+ _open = true;
+ dispatchEvent(new Event(Event.OPEN));
+ }else{
+ _openAnim.toValue = _openAnim.target.closedHeight;
+ _open = false;
+ dispatchEvent(new Event(Event.CLOSE));
+ }
+ setTitleIcon();
+ _openAnim.play();
+
+ }
+
+ }
+
+
+ /**
+ * Whether the block is in a expanded (open) state or not
+ */
+ public function get open():Boolean {
+ return _open;
+ }
+ /**
+ * @private
+ */
+ public function set open(aValue:Boolean):void {
+ _open = aValue;
+ if (_creationComplete) updateOpen();
+ else this.addEventListener(FlexEvent.CREATION_COMPLETE, callUpdateOpenOnCreationComplete, false, 0, true);
+ }
+
+
+ /**
+ * @private
+ */
+ override public function invalidateSize():void {
+ super.invalidateSize();
+ if (_creationComplete)
+ if (_open && !_openAnim.isPlaying) this.height = openHeight;
+ }
+
+
+ // --end--: public methods - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+ }
+
+}
+
import net.systemeD.halcyon.MapPaint;
import net.systemeD.potlatch2.mapfeatures.*;
import net.systemeD.potlatch2.utils.*;
+ import net.systemeD.controls.CollapsiblePanel;
import mx.collections.*;
import mx.containers.*;
}
private var tabComponents:Object = {};
+ private var subpanelComponents:Object = {};
private function initialiseEditors():void {
- editorStack.removeAllChildren();
- if ( selectedEntity == null || feature == null )
- return;
-
- var editorBox:VBox = createEditorBox();
- editorBox.label = "Basic";
- editorBox.icon=tabIconBasic;
- editorStack.addChild(editorBox);
+ editorStack.removeAllChildren();
+ if ( selectedEntity == null || feature == null )
+ return;
+
+ var editorBox:VBox = createEditorBox();
+ editorBox.label = "Basic";
+ editorBox.icon=tabIconBasic;
+ editorStack.addChild(editorBox);
+
+ var tabs:Object = {};
+ tabComponents = {};
+ var subpanels:Object = {};
+ subpanelComponents = {};
+
+ // ** FIXME: we should do this so that the tabs are always in the same order
+ for each (var factory:EditorFactory in feature.editors) {
+ if ( factory.presence.isEditorPresent(factory, selectedEntity, null) ) {
+ var editor:DisplayObject = factory.createEditorInstance(selectedEntity);
+ if (editor) editorBox.addChild(editor);
+ }
- var tabs:Object = {};
- tabComponents = {};
+ var catEditor:DisplayObject = factory.createEditorInstance(selectedEntity);
+ if (!catEditor) continue;
+
+ // Create tab if it doesn't already exist
+ var category:String = factory.category;
+ if (category=='') continue;
+ var tab:VBox = tabs[category];
+ if (tab == null) {
+ tab = createEditorBox();
+ tab.label = category;
+ if (tabIcons[category]) tab.icon=tabIcons[category];
+ editorStack.addChild(tab);
+ tabs[category] = tab;
+ tabComponents[tab] = [];
+ }
- // ** FIXME: we should do this so that the tabs are always in the same order
- for each (var factory:EditorFactory in feature.editors) {
- if ( factory.presence.isEditorPresent(factory, selectedEntity, null) ) {
- var editor:DisplayObject = factory.createEditorInstance(selectedEntity);
- if ( editor != null )
- editorBox.addChild(editor);
- }
- var category:String = factory.category;
- if (category!='') {
- var tab:VBox = tabs[category];
- if ( tab == null) {
- tab = createEditorBox();
- tab.label = category;
- if (tabIcons[category]) tab.icon=tabIcons[category];
- editorStack.addChild(tab);
- tabs[category] = tab;
- tabComponents[tab] = [];
- }
- var catEditor:DisplayObject = factory.createEditorInstance(selectedEntity);
- if ( catEditor != null )
- tabComponents[tab].push(catEditor);
- // tab.addChild(catEditor);
- }
- }
+ // Create subcategory panel if needed
+ if (factory.subcategory) {
+ var subcategory:String = factory.subcategory;
+ if (!subpanels[category]) { subpanels[category]={}; }
+ var subpanel:CollapsiblePanel = subpanels[category][subcategory];
+ if (!subpanel) {
+ subpanel=new CollapsiblePanel(false);
+ subpanel.percentWidth=100;
+ subpanel.styleName="subcategoryPanel";
+ subpanel.title=subcategory;
+ subpanels[category][subcategory]=subpanel;
+ tabComponents[tab].push(subpanel);
+ }
+ subpanel.addChild(catEditor);
+ } else {
+ tabComponents[tab].push(catEditor);
+ }
+ }
}
private function createEditorBox():VBox {
}
/** Translates a priority string ("highest") to a const (PRIORITY_HIGHEST). */
- public static function getPriority(priority:String):uint {
+ public function getPriority(priority:String):uint {
+ var base:uint=subcategory ? 0 : 11;
switch ( priority ) {
- case "highest": return PRIORITY_HIGHEST;
- case "high": return PRIORITY_HIGHEST;
- case "normal": return PRIORITY_NORMAL;
- case "low": return PRIORITY_LOW;
- case "lowest": return PRIORITY_LOWEST;
- default: return PRIORITY_NORMAL;
+ case "highest": return PRIORITY_HIGHEST+base;
+ case "high": return PRIORITY_HIGH+base;
+ case "normal": return PRIORITY_NORMAL+base;
+ case "low": return PRIORITY_LOW+base;
+ case "lowest": return PRIORITY_LOWEST+base;
+ default: return PRIORITY_NORMAL+base;
}
}
/** Default category: "Standard" */
public var category:String = "Standard";
+ /** Optional subcategory (rendered as a collapsible panel) */
+ public var subcategory:String;
+
private var _name:String;
private var _description:String;
_name = String(inputXML.@name);
_description = String(inputXML.@description);
category = String(inputXML.@category);
+ subcategory = String(inputXML.@subcategory);
}
/** Whether the tags on an entity correspond to those for the edit control. By default, returns true - must be overriden by more useful behaviour. */
var editor:EditorFactory = EditorFactory.createFactory(inputType, inputXML);
if ( editor != null ) {
editor.presence = Presence.getPresence(presenceStr);
- editor.sortOrder = EditorFactory.getPriority(sortOrderStr);
+ editor.sortOrder = editor.getPriority(sortOrderStr);
_editors.push(editor);
}
}
public var choices:Array;
public function ChoiceEditorFactory(inputXML:XML) {
- super(inputXML);
+ super(inputXML,"horizontal");
choices = [];
for each( var choiceXML:XML in inputXML.choice ) {
private var _notPresentText:String;
public function FreeTextEditorFactory(inputXML:XML) {
- super(inputXML);
+ super(inputXML,"horizontal");
_notPresentText = inputXML.hasOwnProperty("@absenceText") ? String(inputXML.@absenceText) : "Unset";
}
private var tagKey:String;
private var boxDirection:String;
- public function SingleTagEditorFactory(inputXML:XML) {
+ public function SingleTagEditorFactory(inputXML:XML, defaultLayout:String="vertical") {
super(inputXML);
tagKey = inputXML.@key;
- boxDirection = inputXML.@layout=='horizontal' ? 'horizontal' : 'vertical';
+ boxDirection = inputXML.@layout;
+ if (!boxDirection) { boxDirection=defaultLayout; }
}
override public function areTagsMatching(entity:Entity):Boolean {
</inputSet>
<inputSet id="designation">
- <input type="freetext" category="Details" presence="onTagMatch" description="Official designation or classification" name="Designation" key="designation"/>
+ <input type="freetext" category="Details" presence="onTagMatch" description="Official designation or classification" name="Designation" key="designation" priority="lowest" />
</inputSet>
<inputSet id="common">
name="Name" category="Details" priority="highest"
key="name" description="The most common name"/>
<input type="freetext" presence="onTagMatch"
- name="International Name" category="Details"
+ name="International Name" category="Details" subcategory="Additional names"
key="int_name" description="The internationally recognised name"/>
<input type="freetext" presence="onTagMatch"
- name="Historical Name" category="Details" priority="low"
+ name="Historical Name" category="Details" subcategory="Additional names" priority="low"
key="old_name" description="The historic or previous name"/>
<input type="freetext" presence="onTagMatch"
- name="Alternative Name" category="Details" priority="low"
+ name="Alternative Name" category="Details" subcategory="Additional names" priority="low"
key="alt_name" description="An alternative, currently used, name"/>
</inputSet>
name="Reference" category="Details" priority="high"
key="ref" description="The official reference number"/>
<input type="freetext" presence="onTagMatch"
- name="International Reference" category="Details"
+ name="International Reference" category="Details" subcategory="Additional names"
key="int_ref" description="The official international reference number"/>
<input type="freetext" presence="onTagMatch"
- name="Old Reference" category="Details" priority="low"
+ name="Old Reference" category="Details" subcategory="Additional names" priority="low"
key="old_ref" description="The historic or previous reference number"/>
</inputSet>
<inputSet id="roadPhysical">
<input type="freetext" presence="onTagMatch"
- name="Width" category="Details"
+ name="Width" category="Details" subcategory="Physical"
key="width" description="Width of the road" layout="horizontal"/>
<input type="choice" presence="onTagMatch"
name="Surface" category="Details" description="Type of road surface"
<inputSet ref="tunnel"/>
<inputSet ref="embankment-cutting"/>
<!-- not sure which category best suits put area=yes -->
- <input type="checkbox" presence="onTagMatch" category="Restrictions" key="area" name="Open area" description="The way is a large open space, like at a dock, where vehicles can move anywhere within the space, rather than just along the edge." />
+ <input type="checkbox" presence="onTagMatch" category="Details" subcategory="Physical" key="area" name="Open area" description="The way is a large open space, like at a dock, where vehicles can move anywhere within the space, rather than just along the edge." />
</inputSet>
<inputSet id="roadLanes">
<inputSet id="tunnel">
<!-- Not ideal, used for non-roads too. -->
<input type="choice" presence="onTagMatch"
- name="Tunnel" category="Details" description="Road goes into a tunnel"
+ name="Tunnel" category="Details" subcategory="Physical" description="Road goes into a tunnel"
key="tunnel" layout="horizontal">
<choice value="yes" text="Tunnel" description="Generic tunnel"/>
</input>
<inputSet id="embankment-cutting">
<input type="choice"
- name="Embankment" category="Details" description="Road supported on a raised bed of earth and rock."
+ name="Embankment" category="Details" subcategory="Physical" description="Road supported on a raised bed of earth and rock."
key="embankment" layout="horizontal">
<choice value="yes" text="Embankment"/>
</input>
<input type="choice"
- name="Cutting" category="Details" description="Road carved out of hill on one or both sides."
+ name="Cutting" category="Details" subcategory="Physical" description="Road carved out of hill on one or both sides."
key="cutting" layout="horizontal">
<choice value="yes" text="Cutting"/>
</input>
</inputSet>
<inputSet id="rail-electrification">
- <input type="choice" name="Electrified" category="Details" description="Is the track electrified (whether by 3rd rail, overhead wires, etc)?"
+ <input type="choice" name="Electrified" category="Details" subcategory="Electrification" description="Is the track electrified (whether by 3rd rail, overhead wires, etc)?"
key="electrified">
<choice value="yes" text="Yes"/>
<choice value="no" text="No"/>
</input>
- <input type="choice" name="Voltage" category="Details" description="Nominal voltage of electric wires"
+ <input type="choice" name="Voltage" category="Details" subcategory="Electrification" description="Nominal voltage of electric wires"
key="voltage" presence="withCategory">
<choice value="600" text="600V"/>
<choice value="750" text="750V"/>
<choice value="15000" text="15kV"/>
<choice value="25000" text="25kV"/>
</input>
- <input type="choice" name="Frequency" category="Details" description="Frequency in Hertz of alternating current power supply"
+ <input type="choice" name="Frequency" category="Details" subcategory="Electrification" description="Frequency in Hertz of alternating current power supply"
key="frequency" presence="withCategory">
<choice value="0" text="DC"/>
<choice value="16.67" text="16.67 Hz"/>
</input>
</inputSet>
- <inputSet id="rail-usage">
- <input type="choice" name="Usage" category="Usage" description="Main use of the line" key="usage">
- <choice value="main" text="Main line" description="The principal artery of a rail system."/>
- <choice value="branch" text="Branch line" description="A secondary line, branching off a main line."/>
- <choice value="industrial" text="Industrial"/>
- <choice value="tourism" text="Tourism" />
- <choice value="military" text="Military"/>
- </input>
- </inputSet>
-
<inputSet id="fee">
<input type="freetext" presence="onTagMatch" category="Restrictions" description="The charge/cost of using this amenity" name="Fee" key="fee"/>
</inputSet>
<inputSet ref="tunnel"/>
<inputSet ref="embankment-cutting"/>
<inputSet ref="rail-electrification"/>
- <inputSet ref="rail-usage"/>
<inputSet ref="common"/>
</feature>
.accordionHeader { fillColors: #888888, #999999; fillAlphas: 1,1; color: white; fontWeight: bold; fontSize: 11; }
+/* Simple tag view - panels for subcategories */
+
+.subcategoryPanel {
+ borderThicknessLeft: 1;
+ borderThicknessTop: 1;
+ borderThicknessBottom: 1;
+ borderThicknessRight: 1;
+ borderColor: gray;
+ borderAlpha: 1;
+ dropShadowEnabled: false;
+
+ backgroundColor: gray;
+ backgroundAlpha: 0.2;
+
+ headerHeight: 20;
+ headerColors: #d9d9d9, #ffffff;
+
+ paddingTop: 4;
+ paddingLeft: 4;
+ paddingRight: 4;
+ paddingBottom: 4;
+ verticalGap: 4;
+
+ openIcon: Embed(source="../embedded/CollapsiblePanelAssets.swf", symbol="CollapseButtonDown");
+ closedIcon: Embed(source="../embedded/CollapsiblePanelAssets.swf", symbol="CollapseButtonOver");
+}
+
/* Toolbox */
.theToolBox {