FLEX-34476 Added RichTextEditor
Project: http://git-wip-us.apache.org/repos/asf/flex-sdk/repo Commit: http://git-wip-us.apache.org/repos/asf/flex-sdk/commit/d4eeb05f Tree: http://git-wip-us.apache.org/repos/asf/flex-sdk/tree/d4eeb05f Diff: http://git-wip-us.apache.org/repos/asf/flex-sdk/diff/d4eeb05f Branch: refs/heads/develop Commit: d4eeb05f553348f2ee246758f2e86aced7831d74 Parents: 524cdbe Author: Justin Mclean <jmcl...@apache.org> Authored: Tue Aug 26 23:16:17 2014 +1000 Committer: Justin Mclean <jmcl...@apache.org> Committed: Tue Aug 26 23:16:17 2014 +1000 ---------------------------------------------------------------------- .../projects/experimental/assets/tools/bold.png | Bin 0 -> 154 bytes .../experimental/assets/tools/bullet.png | Bin 0 -> 156 bytes .../experimental/assets/tools/center.png | Bin 0 -> 159 bytes .../experimental/assets/tools/italic.png | Bin 0 -> 165 bytes .../experimental/assets/tools/justify.png | Bin 0 -> 148 bytes .../projects/experimental/assets/tools/left.png | Bin 0 -> 163 bytes .../projects/experimental/assets/tools/link.png | Bin 0 -> 166 bytes .../experimental/assets/tools/right.png | Bin 0 -> 160 bytes .../experimental/assets/tools/underline.png | Bin 0 -> 167 bytes .../src/spark/components/RichTextEditor.as | 226 ++++++++++++++++ .../richTextEditorClasses/AlignTool.mxml | 128 ++++++++++ .../richTextEditorClasses/BoldTool.mxml | 89 +++++++ .../richTextEditorClasses/BulletTool.mxml | 193 ++++++++++++++ .../richTextEditorClasses/ColorTool.mxml | 84 ++++++ .../richTextEditorClasses/FontTool.mxml | 96 +++++++ .../richTextEditorClasses/ItalicTool.mxml | 89 +++++++ .../richTextEditorClasses/LinkTool.mxml | 255 +++++++++++++++++++ .../RichTextEditorToolBar.mxml | 105 ++++++++ .../richTextEditorClasses/SizeTool.mxml | 97 +++++++ .../richTextEditorClasses/UnderlineTool.mxml | 90 +++++++ .../src/spark/skins/AlignToolSkin.mxml | 86 +++++++ 21 files changed, 1538 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/bold.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/bold.png b/frameworks/projects/experimental/assets/tools/bold.png new file mode 100755 index 0000000..04abf6d Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/bold.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/bullet.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/bullet.png b/frameworks/projects/experimental/assets/tools/bullet.png new file mode 100755 index 0000000..aa107ba Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/bullet.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/center.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/center.png b/frameworks/projects/experimental/assets/tools/center.png new file mode 100755 index 0000000..68428a2 Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/center.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/italic.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/italic.png b/frameworks/projects/experimental/assets/tools/italic.png new file mode 100755 index 0000000..e89f768 Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/italic.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/justify.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/justify.png b/frameworks/projects/experimental/assets/tools/justify.png new file mode 100755 index 0000000..5ea6b2a Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/justify.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/left.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/left.png b/frameworks/projects/experimental/assets/tools/left.png new file mode 100755 index 0000000..91bfa69 Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/left.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/link.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/link.png b/frameworks/projects/experimental/assets/tools/link.png new file mode 100755 index 0000000..f771236 Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/link.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/right.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/right.png b/frameworks/projects/experimental/assets/tools/right.png new file mode 100755 index 0000000..455cc65 Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/right.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/assets/tools/underline.png ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/assets/tools/underline.png b/frameworks/projects/experimental/assets/tools/underline.png new file mode 100755 index 0000000..7d892b1 Binary files /dev/null and b/frameworks/projects/experimental/assets/tools/underline.png differ http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/RichTextEditor.as ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/RichTextEditor.as b/frameworks/projects/experimental/src/spark/components/RichTextEditor.as new file mode 100644 index 0000000..1507d9e --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/RichTextEditor.as @@ -0,0 +1,226 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You 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. +// +//////////////////////////////////////////////////////////////////////////////// + +package spark.components +{ + import flash.events.Event; + import flash.utils.Dictionary; + import flashx.textLayout.conversion.ConversionType; + import flashx.textLayout.conversion.TextConverter; + import flashx.textLayout.elements.TextFlow; + import spark.components.richTextEditorClasses.RichTextEditorToolBar; + import spark.events.TextOperationEvent; + + // for asdoc + [Experimental] + [Event(name = "change", type = "flash.events.Event")] + [Style(name = "borderColor", inherit = "no", type = "unit")] + [Style(name = "focusColor", inherit = "yes", type = "unit")] + public class RichTextEditor extends Group + { + private var _htmlText:String; + private var _htmlTextChanged:Boolean = false; + private var _prompt:String = ""; + private var _stylesChanged:Dictionary = new Dictionary; + private var _text:String; + private var _textArea:TextArea; + private var _textFlow:TextFlow; + + public function RichTextEditor() + { + super(); + this.textFlow = new TextFlow; //Prevents a stack trace that happends when you try to access the textflow on click. + } + + [Bindable("change")] + /** + * The htmlText property is here for convenience. It converts the textFlow to TextConverter.TEXT_FIELD_HTML_FORMAT. + */ + public function get htmlText():String + { + if (_htmlTextChanged) + { + if (text == "") + { + _htmlText = ""; + } + else + { + _htmlText = TextConverter.export(textFlow, TextConverter.TEXT_FIELD_HTML_FORMAT, ConversionType.STRING_TYPE) as String; + } + _htmlTextChanged = false; + } + return _htmlText; + } + + /** + * The htmlText property is here for convenience. It converts the textFlow to TextConverter.TEXT_FIELD_HTML_FORMAT. + */ + public function set htmlText(value:String):void + { + if (htmlText != value) + { + _htmlText = value; + if (textFlow) + { + textFlow = TextConverter.importToFlow(_htmlText, TextConverter.TEXT_FIELD_HTML_FORMAT); + } + } + } + + /** + * @private + */ + public function get prompt():String + { + return _prompt; + } + + /** + * @private + */ + public function set prompt(value:String):void + { + _prompt = value; + if (_textArea) + { + _textArea.prompt = _prompt; + } + } + + /** + * @private + */ + public override function styleChanged(styleProp:String):void + { + super.styleChanged(styleProp); + _stylesChanged[styleProp] = getStyle(styleProp); + this.invalidateDisplayList(); + } + + [Bindable("change")] + /** + * The text in the textArea + */ + public function get text():String + { + if (_textArea) + { + return _textArea.text; + } + else + { + return _text; + } + } + + /** + * @private + */ + public function set text(value:String):void + { + _text = value; + if (_textArea) + { + _textArea.text = value; + } + } + + [Bindable("change")] + /** + * The textFlow + */ + public function get textFlow():TextFlow + { + return _textFlow; + } + + /** + * @private + */ + public function set textFlow(value:TextFlow):void + { + _textFlow = value; + if (_textArea) + { + _textArea.textFlow = value; + } + } + + /** + * @private + */ + protected override function createChildren():void + { + super.createChildren(); + var container:VGroup = new VGroup; + container.percentHeight = 100; + container.percentWidth = 100; + this.addElement(container); + + var toolbar:RichTextEditorToolBar = new RichTextEditorToolBar(); + toolbar.percentWidth = 100; + toolbar.bottom = 6; + container.addElement(toolbar); + + _textArea = new TextArea(); + _textArea.percentHeight = 100; + _textArea.percentWidth = 100; + _textArea.addEventListener(TextOperationEvent.CHANGE, handleChange); + _textArea.prompt = prompt; + _textArea.textFlow = textFlow; + if (_htmlText) + { + textFlow = TextConverter.importToFlow(_htmlText, TextConverter.TEXT_FIELD_HTML_FORMAT); + } + else if (_text) + { + _textArea.text = _text; + } + container.addElement(_textArea); + + toolbar.textArea = _textArea; + } + + /** + * @private + */ + protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void + { + super.updateDisplayList(unscaledWidth, unscaledHeight); + if (_textArea) + { + for (var key:String in _stylesChanged) + { + _textArea.setStyle(key, _stylesChanged[key]); + } + _stylesChanged = new Dictionary; //Clear it out + } + } + + /** + * @private + */ + private function handleChange(e:Event):void + { + _htmlTextChanged = true; + this.dispatchEvent(e); + } + } +} http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml new file mode 100644 index 0000000..20f9c5f --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/AlignTool.mxml @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:ButtonBar width="80" arrowKeysWrapFocus="true" change="handleChange(event);" mouseFocusEnabled="false" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx" + xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Script> + <![CDATA[ + import flashx.textLayout.formats.TextAlign; + import flashx.textLayout.formats.TextLayoutFormat; + import mx.collections.ArrayList; + import mx.events.FlexEvent; + import spark.components.TextArea; + import spark.events.TextOperationEvent; + + [Embed(source = "../../../../assets/tools/center.png")] + private const CENTER:Class; + [Embed(source = "../../../../assets/tools/justify.png")] + private const JUSTIFY:Class; + [Embed(source = "../../../../assets/tools/left.png")] + private const LEFT:Class; + [Embed(source = "../../../../assets/tools/right.png")] + private const RIGHT:Class; + + private var _textArea:TextArea; + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + */ + protected override function createChildren():void + { + super.createChildren(); + + var dp:ArrayList = new ArrayList(); + dp.addItem({icon: LEFT, toolTip: "Left align text", value: TextAlign.LEFT}); + dp.addItem({icon: CENTER, toolTip: "Left align text", value: TextAlign.CENTER}); + dp.addItem({icon: RIGHT, toolTip: "Left align text", value: TextAlign.RIGHT}); + dp.addItem({icon: JUSTIFY, toolTip: "Left align text", value: TextAlign.JUSTIFY}); + this.dataProvider = dp; + } + + /** + * @private + */ + private function handleChange(e:Event):void + { + if (this.selectedItem) + { + var txtLayFmt:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + txtLayFmt.textAlign = this.selectedItem.value; + _textArea.setFormatOfRange(txtLayFmt, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + _textArea.setFocus(); + _textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE)); + } + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + + switch (format.textAlign) + { + case TextAlign.LEFT: + this.selectedIndex = 0; + break; + case TextAlign.CENTER: + this.selectedIndex = 1; + break; + case TextAlign.RIGHT: + this.selectedIndex = 2; + break; + case TextAlign.JUSTIFY: + this.selectedIndex = 3; + break; + default: + this.selectedIndex = -1; + break; + } + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</s:ButtonBar> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml new file mode 100644 index 0000000..8e1e89d --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BoldTool.mxml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:ToggleButton width="20" click="handleClick(event);" icon="@Embed('../../../../assets/tools/bold.png')" mouseFocusEnabled="false" toolTip="Bold text" xmlns:fx="http://ns.adobe.com/mxml/2009" + xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Script> + <![CDATA[ + import flash.text.engine.FontWeight; + import flashx.textLayout.formats.TextLayoutFormat; + import mx.events.FlexEvent; + import spark.components.TextArea; + import spark.events.TextOperationEvent; + + private var _textArea:TextArea; + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + */ + private function handleClick(e:MouseEvent):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + format.fontWeight = (format.fontWeight == FontWeight.BOLD) ? FontWeight.NORMAL : FontWeight.BOLD; + _textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + _textArea.setFocus(); + _textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE)); + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + if (format.fontWeight == FontWeight.BOLD) + { + this.selected = true; + } + else + { + this.selected = false; + } + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</s:ToggleButton> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml new file mode 100644 index 0000000..ca93cd8 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/BulletTool.mxml @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:ToggleButton width="20" click="handleClick(event);" icon="@Embed('../../../../assets/tools/bullet.png')" mouseFocusEnabled="false" toolTip="Bullet points" xmlns:fx="http://ns.adobe.com/mxml/2009" + xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Script> + <![CDATA[ + import flash.text.engine.FontWeight; + import flashx.textLayout.edit.IEditManager; + import flashx.textLayout.edit.ISelectionManager; + import flashx.textLayout.edit.SelectionState; + import flashx.textLayout.elements.FlowGroupElement; + import flashx.textLayout.elements.FlowLeafElement; + import flashx.textLayout.elements.ListElement; + import flashx.textLayout.formats.TextLayoutFormat; + import mx.events.FlexEvent; + import spark.components.TextArea; + import spark.events.TextOperationEvent; + + private var _textArea:TextArea; + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + */ + private function getSelectionState():SelectionState + { + if (_textArea.textFlow) + { + var selectionManager:ISelectionManager = _textArea.textFlow.interactionManager; + var selectionState:SelectionState = selectionManager.getSelectionState(); + var startleaf:FlowLeafElement = _textArea.textFlow.findLeaf(selectionState.absoluteStart); + var endleaf:FlowLeafElement = _textArea.textFlow.findLeaf(selectionState.absoluteEnd); + selectionState.absoluteStart = startleaf.getAbsoluteStart(); + selectionState.absoluteEnd = endleaf.getAbsoluteStart() + endleaf.parentRelativeEnd - endleaf.parentRelativeStart; + return selectionState; + } + return null; + } + + /** + * @private + */ + private function handleClick(e:MouseEvent):void + { + if (_textArea.textFlow && _textArea.textFlow.interactionManager is IEditManager) + { + var editManager:IEditManager = IEditManager(_textArea.textFlow.interactionManager); + var doCreate:Boolean = true; + var selectionState:SelectionState = getSelectionState(); + var listElements:Array = _textArea.textFlow.getElementsByTypeName("list"); + for each (var listElement:ListElement in listElements) + { + var start:int = listElement.getAbsoluteStart(); + var end:int = listElement.getAbsoluteStart() + listElement.parentRelativeEnd - listElement.parentRelativeStart; + if (selectionState.absoluteStart == start && selectionState.absoluteEnd == end) + { //Same + removeList(listElement); + doCreate = false; + break; + } + else if (selectionState.absoluteStart == start && selectionState.absoluteEnd <= end) + { //Inside touching start + selectionState = new SelectionState(_textArea.textFlow, end, selectionState.absoluteEnd); + removeList(listElement); + editManager.createList(null, null, selectionState); + doCreate = false; + break; + } + else if (selectionState.absoluteStart >= start && selectionState.absoluteEnd == end) + { //Inside touching end + selectionState = new SelectionState(_textArea.textFlow, selectionState.absoluteStart, start); + removeList(listElement); + editManager.createList(null, null, selectionState); + doCreate = false; + break; + } + else if (selectionState.absoluteStart >= start && selectionState.absoluteEnd <= end) + { //Inside + var firstRange:SelectionState = new SelectionState(_textArea.textFlow, selectionState.absoluteStart, start); + var secondRange:SelectionState = new SelectionState(_textArea.textFlow, end, selectionState.absoluteEnd); + removeList(listElement); + editManager.createList(null, null, firstRange); + editManager.createList(null, null, secondRange); + doCreate = false; + break; + } + else if ((selectionState.absoluteStart >= start && selectionState.absoluteStart <= end) || (selectionState.absoluteEnd >= start && selectionState.absoluteEnd <= end)) + { //Overlap. Include this list in the selection + selectionState = new SelectionState(_textArea.textFlow, Math.min(start, selectionState.absoluteStart), Math.max(end, selectionState.absoluteEnd)); + removeList(listElement); + } + else if (selectionState.absoluteStart <= start && selectionState.absoluteEnd >= end) + { //surround. Remove this list since it will get added back in, only expanded. + removeList(listElement); + } + } + if (doCreate) + { + IEditManager(_textArea.textFlow.interactionManager).createList(null, null, selectionState); + } + _textArea.textFlow.interactionManager.setFocus(); + } + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + if (_textArea.textFlow) + { + var willRemoveBulletsIfClicked:Boolean = false; + var selectionState:SelectionState = getSelectionState(); + var listElements:Array = _textArea.textFlow.getElementsByTypeName("list"); + for each (var listElement:ListElement in listElements) + { + var start:int = listElement.getAbsoluteStart(); + var end:int = listElement.getAbsoluteStart() + listElement.parentRelativeEnd - listElement.parentRelativeStart; + if (selectionState.absoluteStart == start && selectionState.absoluteEnd == end) + { //Same + willRemoveBulletsIfClicked = true; + break; + } + else if (selectionState.absoluteStart >= start && selectionState.absoluteEnd <= end) + { //Inside + willRemoveBulletsIfClicked = true; + break; + } + } + this.selected = willRemoveBulletsIfClicked; + + } + + } + + /** + * @private + */ + private function removeList(listElement:ListElement):void + { + var editManager:IEditManager = IEditManager(_textArea.textFlow.interactionManager); + + var target:FlowGroupElement = listElement.parent; + var targetIndex:int = target.getChildIndex(listElement); + editManager.moveChildren(listElement, 0, listElement.numChildren, target, targetIndex); + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</s:ToggleButton> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml new file mode 100644 index 0000000..86d3006 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ColorTool.mxml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<ns:ColorPicker height="21" width="20" choose="{handleChoose(event)}" mouseFocusEnabled="false" toolTip="Color text" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx" + xmlns:ns="http://flex.apache.org/experimental/ns" xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Script> + <![CDATA[ + import mx.events.FlexEvent; + + import spark.components.TextArea; + import spark.events.ColorChangeEvent; + import spark.events.TextOperationEvent; + + import flashx.textLayout.formats.TextLayoutFormat; + + private var _textArea:TextArea; + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + */ + private function handleChoose(e:ColorChangeEvent):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + format.color = e.color + _textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + _textArea.setFocus(); + _textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE)); + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + this.selectedColor = format.color; + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</ns:ColorPicker> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml new file mode 100644 index 0000000..f0eba89 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/FontTool.mxml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:DropDownList width="100%" change="callLater(handleChange, [event]);" dataProvider="{null}" maxWidth="120" minWidth="80" mouseFocusEnabled="false" xmlns:fx="http://ns.adobe.com/mxml/2009" + xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Script> + <![CDATA[ + import flashx.textLayout.formats.TextLayoutFormat; + import mx.collections.ArrayList; + import mx.collections.IList; + import mx.events.FlexEvent; + import spark.components.TextArea; + import spark.events.TextOperationEvent; + + private const _defaultDataProvider:ArrayList = new ArrayList(["_sans", "_serif", "_typewriter", "Arial", "Calibri", "Courier", "Courier New", "Geneva,Georgia", "Helvetica", "Times New Roman", "Times", "Trebuchet MS", "Verdana"]); + private var _textArea:TextArea; + + public override function set dataProvider(value:IList):void + { + if (value == null) + { + super.dataProvider = _defaultDataProvider; + } + else + { + super.dataProvider = value; + } + } + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + */ + private function handleChange(e:Event):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + format.fontFamily = this.selectedItem; + _textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + _textArea.setFocus(); + _textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE)); + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + this.selectedItem = format.fontFamily; + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</s:DropDownList> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml new file mode 100644 index 0000000..9c54159 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/ItalicTool.mxml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:ToggleButton width="20" click="handleClick(event);" icon="@Embed('../../../../assets/tools/italic.png')" mouseFocusEnabled="false" toolTip="Italic text" xmlns:fx="http://ns.adobe.com/mxml/2009" + xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Script> + <![CDATA[ + import flash.text.engine.FontPosture; + import flashx.textLayout.formats.TextLayoutFormat; + import mx.events.FlexEvent; + import spark.components.TextArea; + import spark.events.TextOperationEvent; + + private var _textArea:TextArea; + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + */ + private function handleClick(e:MouseEvent):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + format.fontStyle = (format.fontStyle == FontPosture.ITALIC) ? FontPosture.NORMAL : FontPosture.ITALIC; + _textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + _textArea.setFocus(); + _textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE)); + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + if (format.fontStyle == FontPosture.ITALIC) + { + this.selected = true; + } + else + { + this.selected = false; + } + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</s:ToggleButton> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml new file mode 100644 index 0000000..49d9f04 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/LinkTool.mxml @@ -0,0 +1,255 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:TextInput width="100%" text="" enabled="false" enter="apply(event)" keyDown="handleLinkKeydown(event)" minWidth="100" mouseFocusChange="apply(event)" mouseFocusEnabled="false" + toolTip="Hyperlink text" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Metadata> + [Event(name="linkSelectedChange", type="flash.events.Event")] + </fx:Metadata> + <fx:Script> + <![CDATA[ + import flashx.textLayout.edit.ElementRange; + import flashx.textLayout.edit.IEditManager; + import flashx.textLayout.edit.SelectionState; + import flashx.textLayout.elements.LinkElement; + import flashx.textLayout.elements.ParagraphElement; + import flashx.textLayout.formats.TextLayoutFormat; + import mx.events.FlexEvent; + import spark.components.TextArea; + + private var _linkSelected:Boolean = false; + + private var _textArea:TextArea; + private var _urlRegExpression:RegExp = new RegExp("^(https?://(www\\.)?|www\\.)[-._~:/?#\\[\\]@!$&'()*+,;=a-z0-9]+$", 'i'); + private const defaultLinkText:String = "http://"; + private var lastRange:ElementRange; + private var _linkEl:LinkElement + + [Bindable("linkSelectedChange")] + public function get linkSelected():Boolean + { + return _linkSelected; + } + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + _textArea.removeEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + _textArea.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + * Apply the link to the selected text + */ + private function apply(e:Event = null):void + { + var urlText:String = this.text == defaultLinkText ? '' : this.text; + applyLink(urlText, "_blank", true); + //Set focus to textFlow + textArea.textFlow.interactionManager.setFocus(); + } + + /** + * @private + * Actually apply the link to the selection. Repair the formating in the process. + */ + private function applyLink(href:String, target:String = null, extendToLinkBoundary:Boolean = false, operationState:SelectionState = null):void + { + if (textArea && textArea.textFlow && textArea.textFlow.interactionManager is IEditManager) + { + //Get the current format + var txtLayFmt:TextLayoutFormat = textArea.textFlow.interactionManager.getCommonCharacterFormat(); + //Set the link + var operationState:SelectionState = null; + if (_linkEl != null) + { + operationState = new SelectionState(textArea.textFlow, _linkEl.getAbsoluteStart(), _linkEl.getAbsoluteStart() + _linkEl.textLength); + } + var linkElement:LinkElement = IEditManager(textArea.textFlow.interactionManager).applyLink(href, target, extendToLinkBoundary, operationState); + //Fix the formatting + if(linkElement) + { + IEditManager(textArea.textFlow.interactionManager).clearFormatOnElement(linkElement.getChildAt(0), txtLayFmt); + } + var selectionEnd:int = Math.max(textArea.selectionActivePosition, textArea.selectionAnchorPosition); + textArea.selectRange(selectionEnd, selectionEnd); + IEditManager(textArea.textFlow.interactionManager).applyLeafFormat(txtLayFmt); + } + } + + /** + * @private + * Automatically add a link if the previous text looks like a link + */ + private function checkLinks():void + { + var position:int = _textArea.selectionActivePosition; + //Find the firt non-whitespace character + while (position > 0) + { + if (!isWhitespace(_textArea.textFlow.getCharCodeAtPosition(position))) + { + break; + } + position--; + } + //Find the next whitespace character + while (position > 0) + { + if (isWhitespace(_textArea.textFlow.getCharCodeAtPosition(position))) + { + position++; //Back up one character + break; + } + position--; + } + var testText:String = _textArea.textFlow.getText(position, _textArea.selectionActivePosition); + var result:Array = testText.match(_urlRegExpression); + if (result != null && result.length > 0) + { + if (_textArea.textFlow.interactionManager is IEditManager) + { + var selectionState:SelectionState = new SelectionState(_textArea.textFlow, position, _textArea.selectionActivePosition); + if (testText.substr(0, 3) == "www") + { + testText = "http://" + testText; //Add a missing 'http://' if needed + } + applyLink(testText, "_blank", true, selectionState); + _textArea.setFocus(); + } + } + } + + /** + * @private + */ + private function handleKeyDown(e:KeyboardEvent):void + { + if (e.keyCode == Keyboard.ENTER || e.keyCode == Keyboard.SPACE || e.keyCode == Keyboard.TAB) + { + checkLinks(); + } + } + + /** + * @private + */ + private function handleLinkKeydown(e:KeyboardEvent):void + { + e.stopImmediatePropagation(); + if (e.keyCode == Keyboard.ENTER) + { + _textArea.setFocus(); + } + } + + /** + * @private + * Update the text display based on the selected range + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var selectionState:SelectionState = _textArea.textFlow.interactionManager.getSelectionState(); + if (selectionState.absoluteStart != -1 && selectionState.absoluteEnd != -1) + { + var range:ElementRange = ElementRange.createElementRange(selectionState.textFlow, selectionState.absoluteStart, selectionState.absoluteEnd); + if (range) + { + var linkString:String = defaultLinkText; + _linkEl = range.firstLeaf.getParentByType(LinkElement) as LinkElement; + if (_linkEl != null) + { + var linkElStart:int = _linkEl.getAbsoluteStart(); + var linkElEnd:int = linkElStart + _linkEl.textLength; + if (linkElEnd < linkElStart) + { + var temp:int = linkElStart; + linkElStart = linkElEnd; + linkElEnd = temp; + } + + var beginRange:int = range.absoluteStart; + var endRange:int = range.absoluteEnd; + + var beginPara:ParagraphElement = range.firstParagraph; + if (endRange == (beginPara.getAbsoluteStart() + beginPara.textLength)) + { + endRange--; + } + + if ((beginRange == endRange) || (endRange <= linkElEnd)) + { + linkString = LinkElement(_linkEl).href; + } + } + var newLinkSelected:Boolean = _linkEl != null; + if (_linkSelected != newLinkSelected) + { + _linkSelected = newLinkSelected; + this.dispatchEvent(new Event("linkSelectedChange")); + } + + this.text = linkString; + + lastRange = range; + } + else + { + lastRange = null; + } + } + + enabled = _textArea.selectionAnchorPosition != _textArea.selectionActivePosition || _linkSelected; + } + + /** + * @private + * Return true if the character is a whitespace character + */ + private function isWhitespace(charCode:uint):Boolean + { + return charCode === 0x0009 || charCode === 0x000A || charCode === 0x000B || charCode === 0x000C || charCode === 0x000D || charCode === 0x0020 || charCode === 0x0085 || charCode === 0x00A0 || charCode === 0x1680 || charCode === 0x180E || charCode === 0x2000 || charCode === 0x2001 || charCode === 0x2002 || charCode === 0x2003 || charCode === 0x2004 || charCode === 0x2005 || charCode === 0x2006 || charCode === 0x2007 || charCode === 0x2008 || charCode === 0x2009 || charCode === 0x200A || charCode === 0x2028 || charCode === 0x2029 || charCode === 0x202F || charCode === 0x205F || charCode === 0x3000; + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</s:TextInput> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml new file mode 100755 index 0000000..d7ef167 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/RichTextEditorToolBar.mxml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:HGroup gap="6" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:richTextEditorClasses="spark.components.richTextEditorClasses.*" + xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Metadata> + [Event(name="linkSelectedChange", type="flash.events.Event")] + </fx:Metadata> + <fx:Script> + <![CDATA[ + import mx.collections.IList; + import spark.components.TextArea; + + [Bindable] + /** + * A list of fonts for the font dropdown + */ + public var fonts:IList = null; + + [Bindable] + /** + * A list of sizes for the size dropdown + */ + public var sizes:IList = null; + + private var _linkSelected:Boolean = false; + private var _textArea:TextArea; + + [Bindable("linkSelectedChange")] + /** + * True if a link is currently selected + */ + public function get linkSelected():Boolean + { + return _linkSelected; + } + + /** + * The textArea that this toolbar is controlling + */ + [Bindable] + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + _textArea = value; + } + + /** + * @private + */ + private function handleLinkSelectedChange(e:Event):void + { + _linkSelected = (e.currentTarget as LinkTool).linkSelected; + this.dispatchEvent(new Event("linkSelectedChange")); + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> + <richTextEditorClasses:FontTool dataProvider="{fonts}" textArea="{textArea}"/> + <richTextEditorClasses:SizeTool dataProvider="{sizes}" textArea="{textArea}"/> + <s:HGroup gap="0"> + <richTextEditorClasses:BoldTool textArea="{textArea}"/> + <richTextEditorClasses:ItalicTool textArea="{textArea}"/> + <richTextEditorClasses:UnderlineTool textArea="{textArea}"/> + </s:HGroup> + <richTextEditorClasses:ColorTool textArea="{textArea}"/> + <s:Line height="100%"> + <s:stroke> + <s:SolidColorStroke color="#B3C2B8"/> + </s:stroke> + </s:Line> + <richTextEditorClasses:AlignTool textArea="{textArea}"/> + <richTextEditorClasses:BulletTool textArea="{textArea}"/> + <s:Line height="100%"> + <s:stroke> + <s:SolidColorStroke color="#B3C2B8"/> + </s:stroke> + </s:Line> + <richTextEditorClasses:LinkTool linkSelectedChange="handleLinkSelectedChange(event)" textArea="{textArea}"/> +</s:HGroup> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml new file mode 100644 index 0000000..0f9b6e6 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/SizeTool.mxml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:DropDownList width="60" change="callLater(handleChange, [event]);" dataProvider="{null}" mouseFocusEnabled="false" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx" + xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Script> + <![CDATA[ + import flashx.textLayout.formats.TextLayoutFormat; + import mx.collections.ArrayList; + import mx.collections.IList; + import mx.events.FlexEvent; + import mx.utils.ArrayUtil; + import spark.components.TextArea; + import spark.events.TextOperationEvent; + + private const _defaultDataProvider:ArrayList = new ArrayList([8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]); + private var _textArea:TextArea; + + public override function set dataProvider(value:IList):void + { + if (value == null) + { + super.dataProvider = _defaultDataProvider; + } + else + { + super.dataProvider = value; + } + } + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + */ + private function handleChange(e:Event):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + format.fontSize = this.selectedItem; + _textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + _textArea.setFocus(); + _textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE)); + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + this.selectedItem = format.fontSize; + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</s:DropDownList> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml new file mode 100644 index 0000000..1ae0663 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/components/richTextEditorClasses/UnderlineTool.mxml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +--> +<s:ToggleButton width="20" click="handleClick(event);" icon="@Embed('../../../../assets/tools/underline.png')" mouseFocusEnabled="false" toolTip="Underline text" + xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark"> + <fx:Script> + <![CDATA[ + import flash.text.engine.FontPosture; + import flashx.textLayout.formats.TextDecoration; + import flashx.textLayout.formats.TextLayoutFormat; + import mx.events.FlexEvent; + import spark.components.TextArea; + import spark.events.TextOperationEvent; + + private var _textArea:TextArea; + + /** + * The textArea that this component interacts with + */ + public function get textArea():TextArea + { + return _textArea; + } + + /** + * @private + */ + public function set textArea(value:TextArea):void + { + if (_textArea) + { + _textArea.removeEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange); + } + _textArea = value; + if (_textArea) + { + _textArea.addEventListener(FlexEvent.SELECTION_CHANGE, handleSelectionChange, false, 0, true); + handleSelectionChange(); + } + } + + /** + * @private + */ + private function handleClick(e:MouseEvent):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + format.textDecoration = (format.textDecoration == TextDecoration.UNDERLINE) ? TextDecoration.NONE : TextDecoration.UNDERLINE; + _textArea.setFormatOfRange(format, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + _textArea.setFocus(); + _textArea.dispatchEvent(new TextOperationEvent(TextOperationEvent.CHANGE)); + } + + /** + * @private + */ + private function handleSelectionChange(e:FlexEvent = null):void + { + var format:TextLayoutFormat = _textArea.getFormatOfRange(null, _textArea.selectionAnchorPosition, _textArea.selectionActivePosition); + if (format.textDecoration == TextDecoration.UNDERLINE) + { + this.selected = true; + } + else + { + this.selected = false; + } + } + ]]> + </fx:Script> + <fx:Declarations> + <!-- Place non-visual elements (e.g., services, value objects) here --> + </fx:Declarations> +</s:ToggleButton> http://git-wip-us.apache.org/repos/asf/flex-sdk/blob/d4eeb05f/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml ---------------------------------------------------------------------- diff --git a/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml b/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml new file mode 100644 index 0000000..36e58b3 --- /dev/null +++ b/frameworks/projects/experimental/src/spark/skins/AlignToolSkin.mxml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. + +--> + + +<!--- The default skin class for the Spark ButtonBar component. The buttons on the ButtonBar component + use the ButtonBarLastButtonSkin, ButtonBarFirstButtonSkin and ButtonBarMiddleButtonSkin classes. + + @see spark.components.ButtonBar + @see spark.components.ButtonBarButton + + @langversion 3.0 + @playerversion Flash 10 + @playerversion AIR 1.5 + @productversion Flex 4 +--> +<s:Skin alpha.disabled="0.5" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"> + + <fx:Metadata> + <![CDATA[ + /** + * @copy spark.skins.spark.ApplicationSkin#hostComponent + */ + [HostComponent("spark.components.ButtonBar")] + ]]> + </fx:Metadata> + + <s:states> + <s:State name="normal"/> + <s:State name="disabled"/> + </s:states> + + <fx:Declarations> + <!--- + @copy spark.components.ButtonBar#firstButton + @default spark.skins.spark.ButtonBarFirstButtonSkin + @see spark.skins.spark.ButtonBarFirstButtonSkin + --> + <fx:Component id="firstButton"> + <s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarFirstButtonSkin" toolTip="{data.toolTip}"/> + </fx:Component> + + <!--- + @copy spark.components.ButtonBar#middleButton + @default spark.skins.spark.ButtonBarMiddleButtonSkin + @see spark.skins.spark.ButtonBarMiddleButtonSkin + --> + <fx:Component id="middleButton"> + <s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarMiddleButtonSkin" toolTip="{data.toolTip}"/> + </fx:Component> + + <!--- + @copy spark.components.ButtonBar#lastButton + @default spark.skins.spark.ButtonBarLastButtonSkin + @see spark.skins.spark.ButtonBarLastButtonSkin + --> + <fx:Component id="lastButton"> + <s:ButtonBarButton skinClass="spark.skins.spark.ButtonBarLastButtonSkin" toolTip="{data.toolTip}"/> + </fx:Component> + + </fx:Declarations> + + <!--- @copy spark.components.SkinnableDataContainer#dataGroup --> + <s:DataGroup id="dataGroup" height="100%" width="100%"> + <s:layout> + <s:ButtonBarHorizontalLayout gap="-1"/> + </s:layout> + </s:DataGroup> + +</s:Skin>