From: Paul Wisbey Date: Tue, 7 Apr 2015 10:34:34 +0000 (+0100) Subject: Merge remote-tracking branch 'origin/tizen' into new_text X-Git-Tag: dali_1.0.38~11^2~13 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=56d412791a44c2a79135d2293c13fddb135c9d54 Merge remote-tracking branch 'origin/tizen' into new_text Conflicts: automated-tests/src/dali-toolkit-internal/CMakeLists.txt dali-toolkit/internal/controls/buttons/push-button-impl.cpp dali-toolkit/internal/controls/text-input/text-input-handles-impl.cpp dali-toolkit/internal/controls/text-input/text-input-impl.cpp Change-Id: Icb029929ab92e9d4c606f47c50d8d213759f09c5 --- 56d412791a44c2a79135d2293c13fddb135c9d54 diff --cc dali-toolkit/internal/controls/buttons/button-impl.cpp index 0f7a0a5,0d3fcd5..c42531f --- a/dali-toolkit/internal/controls/buttons/button-impl.cpp +++ b/dali-toolkit/internal/controls/buttons/button-impl.cpp @@@ -26,8 -26,32 +26,32 @@@ #include // INTERNAL INCLUDES -#include +#include + /** + * Button states and contents + * (3) mSelectedContent + * (2) mButtonContent (2) mSelectedBackgroundContent + * (1) mBackgroundContent (1) mBackgroundContent + * < unselected > ----------------------- < selected > + * | OnSelect() | + * | OnDisabled() | OnDisabled() + * | | + * < disabled > < disabled-selected > + * (2) mDisabledContent (2) mDisabledSelectedContent + * (1) mDisabledBackgroundContent (1) mDisabledBackgroundContent + * + * The drawing order of child actors is as follows. + * + * Top mLabel + * | mButtonContent / mSelectedContent / mDisabledContent / mDisabledSelectedContent + * | mSelectedBackgroundContent + * Bottom mBackgroundContent / mDisabledBackgroundContent + * + * Some of contents may be missed. + * And 2 images - fade-in image and fade-out image - in the same layer can be shown during the transition animation. Fade-in image should be above fade-out image. + */ + namespace Dali { diff --cc dali-toolkit/internal/controls/buttons/push-button-impl.cpp index 1a5c3a4,a2cb95d..24fa230 --- a/dali-toolkit/internal/controls/buttons/push-button-impl.cpp +++ b/dali-toolkit/internal/controls/buttons/push-button-impl.cpp @@@ -1033,121 -578,42 +578,42 @@@ void PushButton::OnControlSizeSet( cons Vector3 PushButton::GetNaturalSize() { - Vector3 size = Control::GetNaturalSize(); - - const bool widthIsZero = EqualsZero( size.width ); - const bool heightIsZero = EqualsZero( size.height ); - - if( widthIsZero || heightIsZero ) - { - // If background and background not scale9 try get size from that - ImageActor imageActor = FindImageActor( GetButtonImage() ); - if( imageActor && imageActor.GetStyle() != ImageActor::STYLE_NINE_PATCH ) - { - Vector3 imageSize = imageActor.GetNaturalSize(); - - if( widthIsZero ) - { - size.width = imageSize.width; - } - - if( heightIsZero ) - { - size.height = imageSize.height; - } - } - - ImageActor backgroundImageActor = FindImageActor( GetBackgroundImage() ); - if( backgroundImageActor && backgroundImageActor.GetStyle() != ImageActor::STYLE_NINE_PATCH ) - { - Vector3 imageSize = backgroundImageActor.GetNaturalSize(); - - if( widthIsZero ) - { - size.width = std::max( size.width, imageSize.width ); - } - - if( heightIsZero ) - { - size.height = std::max( size.height, imageSize.height ); - } - } - - // If label, test against it's size - Toolkit::TextLabel textLabel = Toolkit::TextLabel::DownCast( GetLabel() ); - if( textLabel ) - { - Vector3 textLabelSize = textLabel.GetNaturalSize(); - - if( widthIsZero ) - { - size.width = std::max( size.width, textLabelSize.width + TEXT_PADDING * 2.0f ); - } - - if( heightIsZero ) - { - size.height = std::max( size.height, textLabelSize.height + TEXT_PADDING * 2.0f ); - } - } - } - - return size; - } - - Actor& PushButton::GetFadeOutButtonImage() - { - return mFadeOutButtonContent; - } + Vector3 size; - Actor& PushButton::GetFadeOutBackgroundImage() - { - return mFadeOutBackgroundContent; - } - - void PushButton::AddToFadeInAnimation( const Actor& actor ) - { - if( !mFadeInAnimation ) + // If background and background not scale9 try get size from that + ImageActor imageActor = FindImageActor( GetButtonImage() ); + if( imageActor && imageActor.GetStyle() != ImageActor::STYLE_NINE_PATCH ) { - mFadeInAnimation = Dali::Animation::New( GetAnimationTime() ); + size.width = imageActor.GetRelayoutSize( WIDTH ); + size.height = imageActor.GetRelayoutSize( HEIGHT ); } - mFadeInAnimation.OpacityTo( actor, 1.f ); - } - - void PushButton::StartFadeInAnimation() - { - if( mFadeInAnimation ) + ImageActor backgroundImageActor = FindImageActor( GetBackgroundImage() ); + if( backgroundImageActor && backgroundImageActor.GetStyle() != ImageActor::STYLE_NINE_PATCH ) { - mFadeInAnimation.FinishedSignal().Connect( this, &PushButton::FadeInAnimationFinished ); - mFadeInAnimation.Play(); + size.width = std::max( size.width, backgroundImageActor.GetRelayoutSize( WIDTH ) ); + size.height = std::max( size.height, backgroundImageActor.GetRelayoutSize( HEIGHT ) ); } - } - void PushButton::StopFadeInAnimation() - { - if( mFadeInAnimation ) + // If label, test against it's size - Toolkit::TextView textView = Toolkit::TextView::DownCast( GetLabel() ); - if( textView ) ++ Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( GetLabel() ); ++ if( label ) { - mFadeInAnimation.Clear(); - mFadeInAnimation.Reset(); - } - } - Vector3 textViewSize = textView.GetNaturalSize(); ++ Vector3 labelSize = label.GetNaturalSize(); - void PushButton::AddToFadeOutAnimation( const Actor& actor ) - { - if( !mFadeOutAnimation ) - { - mFadeOutAnimation = Dali::Animation::New( GetAnimationTime() ); - size.width = std::max( size.width, textViewSize.width + TEXT_PADDING * 2.0f ); - size.height = std::max( size.height, textViewSize.height + TEXT_PADDING * 2.0f ); ++ size.width = std::max( size.width, labelSize.width + TEXT_PADDING * 2.0f ); ++ size.height = std::max( size.height, labelSize.height + TEXT_PADDING * 2.0f ); } - mFadeOutAnimation.OpacityTo( actor, 0.f ); + return size; } - void PushButton::StartFadeOutAnimation() + void PushButton::StartTransitionAnimation() { - if( mFadeOutAnimation ) + if( mTransitionAnimation ) { - mFadeOutAnimation.FinishedSignal().Connect( this, &PushButton::FadeOutAnimationFinished ); - mFadeOutAnimation.Play(); + mTransitionAnimation.FinishedSignal().Connect( this, &PushButton::TransitionAnimationFinished ); + mTransitionAnimation.Play(); } } diff --cc dali-toolkit/internal/controls/text-controls/text-label-impl.cpp index 6149e29,0000000..73f40e9 mode 100644,000000..100644 --- a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp @@@ -1,474 -1,0 +1,474 @@@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include + +using Dali::Toolkit::Text::LayoutEngine; +using Dali::Toolkit::Text::Backend; + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ + const unsigned int DEFAULT_RENDERING_BACKEND = Dali::Toolkit::Text::DEFAULT_RENDERING_BACKEND; +} + +namespace +{ + +const Scripting::StringEnum< Toolkit::Text::LayoutEngine::HorizontalAlignment > HORIZONTAL_ALIGNMENT_STRING_TABLE[] = +{ + { "BEGIN", Toolkit::Text::LayoutEngine::HORIZONTAL_ALIGN_BEGIN }, + { "CENTER", Toolkit::Text::LayoutEngine::HORIZONTAL_ALIGN_CENTER }, + { "END", Toolkit::Text::LayoutEngine::HORIZONTAL_ALIGN_END }, +}; +const unsigned int HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE ) / sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE[0] ); + +const Scripting::StringEnum< Toolkit::Text::LayoutEngine::VerticalAlignment > VERTICAL_ALIGNMENT_STRING_TABLE[] = +{ + { "TOP", Toolkit::Text::LayoutEngine::VERTICAL_ALIGN_TOP }, + { "CENTER", Toolkit::Text::LayoutEngine::VERTICAL_ALIGN_CENTER }, + { "BOTTOM", Toolkit::Text::LayoutEngine::VERTICAL_ALIGN_BOTTOM }, +}; +const unsigned int VERTICAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof( VERTICAL_ALIGNMENT_STRING_TABLE ) / sizeof( VERTICAL_ALIGNMENT_STRING_TABLE[0] ); + +// Type registration +BaseHandle Create() +{ + return Toolkit::TextLabel::New(); +} + +// Setup properties, signals and actions using the type-registry. +DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TextLabel, Toolkit::Control, Create ); + +DALI_PROPERTY_REGISTRATION( TextLabel, "rendering-backend", INTEGER, RENDERING_BACKEND ) +DALI_PROPERTY_REGISTRATION( TextLabel, "text", STRING, TEXT ) +DALI_PROPERTY_REGISTRATION( TextLabel, "font-family", STRING, FONT_FAMILY ) +DALI_PROPERTY_REGISTRATION( TextLabel, "font-style", STRING, FONT_STYLE ) +DALI_PROPERTY_REGISTRATION( TextLabel, "point-size", FLOAT, POINT_SIZE ) +DALI_PROPERTY_REGISTRATION( TextLabel, "multi-line", BOOLEAN, MULTI_LINE ) +DALI_PROPERTY_REGISTRATION( TextLabel, "horizontal-alignment", STRING, HORIZONTAL_ALIGNMENT ) +DALI_PROPERTY_REGISTRATION( TextLabel, "vertical-alignment", STRING, VERTICAL_ALIGNMENT ) +DALI_PROPERTY_REGISTRATION( TextLabel, "text-color", VECTOR4, TEXT_COLOR ) +DALI_PROPERTY_REGISTRATION( TextLabel, "shadow-offset", VECTOR2, SHADOW_OFFSET ) +DALI_PROPERTY_REGISTRATION( TextLabel, "shadow-color", VECTOR4, SHADOW_COLOR ) +DALI_PROPERTY_REGISTRATION( TextLabel, "underline-enabled", BOOLEAN, UNDERLINE_ENABLED ) +DALI_PROPERTY_REGISTRATION( TextLabel, "underline-color", VECTOR4, UNDERLINE_COLOR ) +DALI_TYPE_REGISTRATION_END() + +} // namespace + +Toolkit::TextLabel TextLabel::New() +{ + // Create the implementation, temporarily owned by this handle on stack + IntrusivePtr< TextLabel > impl = new TextLabel(); + + // Pass ownership to CustomActor handle + Toolkit::TextLabel handle( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value ) +{ + Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( Dali::BaseHandle( object ) ); + + if( label ) + { + TextLabel& impl( GetImpl( label ) ); + switch( index ) + { + case Toolkit::TextLabel::Property::RENDERING_BACKEND: + { + int backend = value.Get< int >(); + + if( impl.mRenderingBackend != backend ) + { + impl.mRenderingBackend = backend; + impl.mRenderer.Reset(); + impl.RequestTextRelayout(); + } + break; + } + case Toolkit::TextLabel::Property::TEXT: + { + if( impl.mController ) + { + impl.mController->SetText( value.Get< std::string >() ); + impl.RequestTextRelayout(); + } + break; + } + case Toolkit::TextLabel::Property::FONT_FAMILY: + { + if( impl.mController ) + { + std::string fontFamily = value.Get< std::string >(); + + if( impl.mController->GetDefaultFontFamily() != fontFamily ) + { + impl.mController->SetDefaultFontFamily( fontFamily ); + impl.RequestTextRelayout(); + } + } + break; + } + case Toolkit::TextLabel::Property::FONT_STYLE: + { + if( impl.mController ) + { + std::string fontStyle = value.Get< std::string >(); + + if( impl.mController->GetDefaultFontStyle() != fontStyle ) + { + impl.mController->SetDefaultFontStyle( fontStyle ); + impl.RequestTextRelayout(); + } + } + break; + } + case Toolkit::TextLabel::Property::POINT_SIZE: + { + if( impl.mController ) + { + float pointSize = value.Get< float >(); + + if( impl.mController->GetDefaultPointSize() != pointSize /*TODO - epsilon*/ ) + { + impl.mController->SetDefaultPointSize( pointSize ); + impl.RequestTextRelayout(); + } + } + break; + } + case Toolkit::TextLabel::Property::MULTI_LINE: + { + if( impl.mController ) + { + LayoutEngine& engine = impl.mController->GetLayoutEngine(); + LayoutEngine::Layout layout = value.Get< bool >() ? LayoutEngine::MULTI_LINE_BOX : LayoutEngine::SINGLE_LINE_BOX; + + if( engine.GetLayout() != layout ) + { + engine.SetLayout( layout ); + impl.RequestTextRelayout(); + } + } + break; + } + case Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT: + { + LayoutEngine& engine = impl.mController->GetLayoutEngine(); + const LayoutEngine::HorizontalAlignment alignment = Scripting::GetEnumeration< Toolkit::Text::LayoutEngine::HorizontalAlignment >( value.Get< std::string >().c_str(), + HORIZONTAL_ALIGNMENT_STRING_TABLE, + HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT ); + + if( engine.GetHorizontalAlignment() != alignment ) + { + engine.SetHorizontalAlignment( alignment ); + impl.RequestTextRelayout(); + } + break; + } + case Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT: + { + LayoutEngine& engine = impl.mController->GetLayoutEngine(); + const LayoutEngine::VerticalAlignment alignment = Scripting::GetEnumeration< Toolkit::Text::LayoutEngine::VerticalAlignment >( value.Get< std::string >().c_str(), + VERTICAL_ALIGNMENT_STRING_TABLE, + VERTICAL_ALIGNMENT_STRING_TABLE_COUNT ); + + if( engine.GetVerticalAlignment() != alignment ) + { + engine.SetVerticalAlignment( alignment ); + impl.RequestTextRelayout(); + } + break; + } + + case Toolkit::TextLabel::Property::TEXT_COLOR: + { + if ( impl.mController ) + { + Vector4 textColor = value.Get< Vector4 >(); + if ( impl.mController->GetTextColor() != textColor ) + { + impl.mController->SetTextColor( textColor ); + impl.RequestTextRelayout(); + } + } + break; + } + + case Toolkit::TextLabel::Property::SHADOW_OFFSET: + { + if( impl.mController ) + { + Vector2 shadowOffset = value.Get< Vector2 >(); + if ( impl.mController->GetShadowOffset() != shadowOffset ) + { + impl.mController->SetShadowOffset( shadowOffset ); + impl.RequestTextRelayout(); + } + } + break; + } + case Toolkit::TextLabel::Property::SHADOW_COLOR: + { + if( impl.mController ) + { + Vector4 shadowColor = value.Get< Vector4 >(); + if ( impl.mController->GetShadowColor() != shadowColor ) + { + impl.mController->SetShadowColor( shadowColor ); + impl.RequestTextRelayout(); + } + } + break; + } + case Toolkit::TextLabel::Property::UNDERLINE_COLOR: + { + if( impl.mController ) + { + Vector4 color = value.Get< Vector4 >(); + if ( impl.mController->GetUnderlineColor() != color ) + { + impl.mController->SetUnderlineColor( color ); + impl.RequestTextRelayout(); + } + } + break; + } + case Toolkit::TextLabel::Property::UNDERLINE_ENABLED: + { + if( impl.mController ) + { + bool enabled = value.Get< bool >(); + if ( impl.mController->IsUnderlineEnabled() != enabled ) + { + impl.mController->SetUnderlineEnabled( enabled ); + impl.RequestTextRelayout(); + } + } + break; + } + } + } +} + +Property::Value TextLabel::GetProperty( BaseObject* object, Property::Index index ) +{ + Property::Value value; + + Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( Dali::BaseHandle( object ) ); + + if( label ) + { + TextLabel& impl( GetImpl( label ) ); + switch( index ) + { + case Toolkit::TextLabel::Property::RENDERING_BACKEND: + { + value = impl.mRenderingBackend; + break; + } + case Toolkit::TextLabel::Property::TEXT: + { + if( impl.mController ) + { + std::string text; + impl.mController->GetText( text ); + value = text; + } + break; + } + case Toolkit::TextLabel::Property::MULTI_LINE: + { + if( impl.mController ) + { + value = static_cast( LayoutEngine::MULTI_LINE_BOX == impl.mController->GetLayoutEngine().GetLayout() ); + } + break; + } + case Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT: + { + if( impl.mController ) + { + value = std::string( Scripting::GetEnumerationName< Toolkit::Text::LayoutEngine::HorizontalAlignment >( impl.mController->GetLayoutEngine().GetHorizontalAlignment(), + HORIZONTAL_ALIGNMENT_STRING_TABLE, + HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT ) ); + } + break; + } + case Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT: + { + if( impl.mController ) + { + value = std::string( Scripting::GetEnumerationName< Toolkit::Text::LayoutEngine::VerticalAlignment >( impl.mController->GetLayoutEngine().GetVerticalAlignment(), + VERTICAL_ALIGNMENT_STRING_TABLE, + VERTICAL_ALIGNMENT_STRING_TABLE_COUNT ) ); + } + break; + } + case Toolkit::TextLabel::Property::TEXT_COLOR: + { + if ( impl.mController ) + { + value = impl.mController->GetTextColor(); + } + break; + } + case Toolkit::TextLabel::Property::SHADOW_OFFSET: + { + if ( impl.mController ) + { + value = impl.mController->GetShadowOffset(); + } + break; + } + case Toolkit::TextLabel::Property::SHADOW_COLOR: + { + if ( impl.mController ) + { + value = impl.mController->GetShadowColor(); + } + break; + } + case Toolkit::TextLabel::Property::UNDERLINE_COLOR: + { + if ( impl.mController ) + { + value = impl.mController->GetUnderlineColor(); + } + break; + } + case Toolkit::TextLabel::Property::UNDERLINE_ENABLED: + { + if ( impl.mController ) + { + value = impl.mController->IsUnderlineEnabled(); + } + break; + } + } + } + + return value; +} + +void TextLabel::OnInitialize() +{ + Actor self = Self(); + + mController = Text::Controller::New( *this ); + + // Use height-for-width negotiation by default + self.SetResizePolicy( FILL_TO_PARENT, WIDTH ); - self.SetDimensionDependency( HEIGHT, WIDTH ); ++ self.SetResizePolicy( DIMENSION_DEPENDENCY, HEIGHT ); +} + +void TextLabel::OnStyleChange( Toolkit::StyleManager styleManager, StyleChange change ) +{ + GetImpl( styleManager ).ApplyThemeStyle( Toolkit::Control( GetOwner() ) ); +} + +Vector3 TextLabel::GetNaturalSize() +{ + return mController->GetNaturalSize(); +} + +float TextLabel::GetHeightForWidth( float width ) +{ + return mController->GetHeightForWidth( width ); +} + +void TextLabel::OnRelayout( const Vector2& size, RelayoutContainer& container ) +{ + if( mController->Relayout( size ) || + !mRenderer ) + { + if( !mRenderer ) + { + mRenderer = Backend::Get().NewRenderer( mRenderingBackend ); + } + + RenderableActor renderableActor; + if( mRenderer ) + { + renderableActor = mRenderer->Render( mController->GetView() ); + } + + if( renderableActor != mRenderableActor ) + { + UnparentAndReset( mRenderableActor ); + + if( renderableActor ) + { + const Vector2& alignmentOffset = mController->GetAlignmentOffset(); + renderableActor.SetPosition( alignmentOffset.x, alignmentOffset.y ); + + Self().Add( renderableActor ); + } + + mRenderableActor = renderableActor; + } + } +} + +void TextLabel::RequestTextRelayout() +{ + RelayoutRequest(); +} + +TextLabel::TextLabel() +: Control( ControlBehaviour( REQUIRES_STYLE_CHANGE_SIGNALS ) ), + mRenderingBackend( DEFAULT_RENDERING_BACKEND ) +{ +} + +TextLabel::~TextLabel() +{ +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --cc dali-toolkit/internal/controls/text-controls/text-selection-popup-impl.cpp index 6f3f565,0000000..6db6845 mode 100644,000000..100644 --- a/dali-toolkit/internal/controls/text-controls/text-selection-popup-impl.cpp +++ b/dali-toolkit/internal/controls/text-controls/text-selection-popup-impl.cpp @@@ -1,675 -1,0 +1,675 @@@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include +#include +#include + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +// todo Move this to adaptor?? +#define GET_LOCALE_TEXT(string) dgettext("elementary", string) + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +namespace +{ +const Dali::Vector4 DEFAULT_POPUP_BACKGROUND( Dali::Vector4( .20f, 0.29f, 0.44f, 1.0f ) ); +const Dali::Vector4 DEFAULT_POPUP_BACKGROUND_PRESSED( Dali::Vector4( 0.07f, 0.10f, 0.17f, 1.0f ) ); +const Dali::Vector4 DEFAULT_POPUP_LINE_COLOR( Dali::Vector4( 0.36f, 0.45f, 0.59f, 1.0f ) ); +const Dali::Vector4 DEFAULT_OPTION_ICON( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); +const Dali::Vector4 DEFAULT_OPTION_ICON_PRESSED( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); +const Dali::Vector4 DEFAULT_OPTION_TEXT( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); +const Dali::Vector4 DEFAULT_OPTION_TEXT_PRESSED( Dali::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); + +const std::string DEFAULT_POPUP_BACKGROUND_IMAGE( DALI_IMAGE_DIR "popup_bubble_bg.#.png" ); +const std::string OPTION_ICON_CLIPBOARD( DALI_IMAGE_DIR "copy_paste_icon_clipboard.png" ); +const std::string OPTION_ICON_COPY( DALI_IMAGE_DIR "copy_paste_icon_copy.png" ); +const std::string OPTION_ICON_CUT( DALI_IMAGE_DIR "copy_paste_icon_cut.png" ); +const std::string OPTION_ICON_PASTE( DALI_IMAGE_DIR "copy_paste_icon_paste.png" ); +const std::string OPTION_ICON_SELECT( DALI_IMAGE_DIR "copy_paste_icon_select.png" ); +const std::string OPTION_ICON_SELECT_ALL( DALI_IMAGE_DIR "copy_paste_icon_select_all.png" ); + +const Dali::Vector2 DEFAULT_POPUP_MAX_SIZE( 470.0f, 120.0f ); ///< The maximum size of the popup. + +const float OPTION_TEXT_LINE_HEIGHT( 32.0f ); ///< The line height of the text. +const Dali::Vector2 OPTION_ICON_SIZE( 0.f, 0.f ); ///< The size of the icon. +const float OPTION_GAP_ICON_TEXT( 6.f ); ///< The gap between the icon and the text +const float OPTION_MARGIN_WIDTH( 10.f ); ///< The margin between the right or lefts edge and the text or icon. +const float OPTION_MAX_WIDTH( DEFAULT_POPUP_MAX_SIZE.width / 6 ); ///< The maximum width of the option (currently set to the max) +const float OPTION_MIN_WIDTH( 86.0f ); ///< The minimum width of the option. + +const float POPUP_DIVIDER_WIDTH( 1.f ); ///< The size of the divider. + +const Dali::Vector2 POPUP_TAIL_SIZE( 20.0f, 16.0f ); ///< The size of the tail. +const float POPUP_TAIL_Y_OFFSET( 5.f ); ///< The y offset of the tail (when its position is on the bottom). +const float POPUP_TAIL_TOP_Y_OFFSET( 3.f ); ///< The y offset of the tail (when its position is on the top). + +const float HIDE_POPUP_ANIMATION_DURATION( 0.2f ); ///< Duration of popup hide animation in seconds. +const float SHOW_POPUP_ANIMATION_DURATION( 0.2f ); ///< Duration of popup show animation in seconds. + +const char* const OPTION_SELECT_WORD = "option-select_word"; // "Select Word" popup option. +const char* const OPTION_SELECT_ALL("option-select_all"); // "Select All" popup option. +const char* const OPTION_CUT("option-cut"); // "Cut" popup option. +const char* const OPTION_COPY("option-copy"); // "Copy" popup option. +const char* const OPTION_PASTE("option-paste"); // "Paste" popup option. +const char* const OPTION_CLIPBOARD("option-clipboard"); // "Clipboard" popup option. + +} // namespace + +//// Comparison function for ButtonRequirement Priority +//bool TextSelectionPopup::PriorityCompare( ButtonRequirement const& a, ButtonRequirement const& b ) +//{ +// return a.priority < b.priority; +//} + + +Dali::Toolkit::TextSelectionPopup TextSelectionPopup::New() +{ + // Create the implementation, temporarily owned by this handle on stack + IntrusivePtr< TextSelectionPopup > impl = new TextSelectionPopup(); + + // Pass ownership to CustomActor handle + Dali::Toolkit::TextSelectionPopup handle( *impl ); + + // Second-phase init of the implementation + // This can only be done after the CustomActor connection has been made... + impl->Initialize(); + + return handle; +} + +void TextSelectionPopup::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value ) +{ + Toolkit::TextSelectionPopup selectionPopup = Toolkit::TextSelectionPopup::DownCast( Dali::BaseHandle( object ) ); + + if( selectionPopup ) + { + TextSelectionPopup& impl( GetImpl( selectionPopup ) ); + + switch( index ) + { + case Toolkit::TextSelectionPopup::Property::POPUP_MAX_SIZE: + { + impl.SetPopupMaxSize( value.Get< Vector2 >() ); + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_BACKGROUND_IMAGE: + { + ResourceImage image = ResourceImage::New( value.Get< std::string >() ); + impl.SetPopupImage( POPUP_BACKGROUND, image ); + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_CLIPBOARD_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::New( value.Get< std::string >() ); + impl.SetPopupImage( POPUP_CLIPBOARD_BUTTON, image ); + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_CUT_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::New( value.Get< std::string >() ); + impl.SetPopupImage( POPUP_CUT_BUTTON_ICON, image ); + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_COPY_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::New( value.Get< std::string >() ); + impl.SetPopupImage( POPUP_COPY_BUTTON_ICON, image ); + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_PASTE_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::New( value.Get< std::string >() ); + impl.SetPopupImage( POPUP_PASTE_BUTTON_ICON, image ); + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::New( value.Get< std::string >() ); + impl.SetPopupImage( POPUP_SELECT_BUTTON_ICON, image ); + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_ALL_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::New( value.Get< std::string >() ); + impl.SetPopupImage( POPUP_SELECT_ALL_BUTTON_ICON, image ); + break; + } + } // switch + } // TextSelectionPopup +} + +Property::Value TextSelectionPopup::GetProperty( BaseObject* object, Property::Index index ) +{ + Property::Value value; + + Toolkit::TextSelectionPopup selectionPopup = Toolkit::TextSelectionPopup::DownCast( Dali::BaseHandle( object ) ); + + if( selectionPopup ) + { + TextSelectionPopup& impl( GetImpl( selectionPopup ) ); + + switch( index ) + { + case Toolkit::TextSelectionPopup::Property::POPUP_MAX_SIZE: + { + value = impl.GetPopupMaxSize(); + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_BACKGROUND_IMAGE: + { + ResourceImage image = ResourceImage::DownCast( impl.GetPopupImage( POPUP_BACKGROUND ) ); + if( image ) + { + value = image.GetUrl(); + } + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_CLIPBOARD_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::DownCast( impl.GetPopupImage( POPUP_CLIPBOARD_BUTTON ) ); + if( image ) + { + value = image.GetUrl(); + } + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_CUT_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::DownCast( impl.GetPopupImage( POPUP_CUT_BUTTON_ICON ) ); + if( image ) + { + value = image.GetUrl(); + } + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_COPY_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::DownCast( impl.GetPopupImage( POPUP_COPY_BUTTON_ICON ) ); + if( image ) + { + value = image.GetUrl(); + } + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_PASTE_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::DownCast( impl.GetPopupImage( POPUP_PASTE_BUTTON_ICON ) ); + if( image ) + { + value = image.GetUrl(); + } + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::DownCast( impl.GetPopupImage( POPUP_SELECT_BUTTON_ICON ) ); + if( image ) + { + value = image.GetUrl(); + } + break; + } + case Toolkit::TextSelectionPopup::Property::POPUP_SELECT_ALL_BUTTON_ICON_IMAGE: + { + ResourceImage image = ResourceImage::DownCast( impl.GetPopupImage( POPUP_SELECT_ALL_BUTTON_ICON ) ); + if( image ) + { + value = image.GetUrl(); + } + break; + } + } // switch + } + return value; +} + +void TextSelectionPopup::OnInitialize() +{ + CreatePopup(); +} + +void TextSelectionPopup::OnRelayout( const Vector2& size, RelayoutContainer& container ) +{ + +} + +void TextSelectionPopup::SetPopupMaxSize( const Size& maxSize ) +{ + mMaxSize = maxSize; +} + +const Dali::Vector2& TextSelectionPopup::GetPopupMaxSize() const +{ + return mMaxSize; +} + +void TextSelectionPopup::SetPopupImage( PopupParts part, Dali::Image image ) +{ + switch ( part ) + { + case POPUP_BACKGROUND : + { + mBackgroundImage = image; + } + break; + case POPUP_CLIPBOARD_BUTTON : + { + mClipboardIconImage = image; + } + break; + case POPUP_CUT_BUTTON_ICON : + { + mCutIconImage = image; + } + break; + case POPUP_COPY_BUTTON_ICON : + { + mCopyIconImage = image; + } + break; + case POPUP_PASTE_BUTTON_ICON : + { + mPasteIconImage = image; + } + break; + case POPUP_SELECT_BUTTON_ICON : + { + mSelectIconImage = image; + } + break; + case POPUP_SELECT_ALL_BUTTON_ICON : + { + mSelectAllIconImage = image; + } + break; + + } // switch +} + +Dali::Image TextSelectionPopup::GetPopupImage( PopupParts part ) +{ + switch ( part ) + { + case POPUP_BACKGROUND : + { + return mBackgroundImage; + } + break; + case POPUP_CLIPBOARD_BUTTON : + { + return mClipboardIconImage; + } + break; + case POPUP_CUT_BUTTON_ICON : + { + return mCutIconImage; + } + break; + case POPUP_COPY_BUTTON_ICON : + { + return mCopyIconImage; + } + break; + case POPUP_PASTE_BUTTON_ICON : + { + return mPasteIconImage; + } + break; + case POPUP_SELECT_BUTTON_ICON : + { + return mSelectIconImage; + } + break; + case POPUP_SELECT_ALL_BUTTON_ICON : + { + return mSelectAllIconImage; + } + break; + default : + { + DALI_ASSERT_DEBUG( "Unknown Popup Part" ); + } + } // switch + + return Dali::Image(); +} + + void TextSelectionPopup::CreateOrderedListOfPopupOptions() + { + mOrderListOfButtons.clear(); + + // Create button for each possible option using Option priority + if ( !mCutIconImage ) + { + mCutIconImage = ResourceImage::New( OPTION_ICON_CUT ); + } + mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCut, mCutOptionPriority, OPTION_CUT, GET_LOCALE_TEXT("IDS_COM_BODY_CUT"), mCutIconImage, true ) ); + + if ( !mCopyIconImage ) + { + mCopyIconImage = ResourceImage::New( OPTION_ICON_COPY ); + } + mOrderListOfButtons.push_back( ButtonRequirement( ButtonsCopy, mCopyOptionPriority, OPTION_COPY, GET_LOCALE_TEXT("IDS_COM_BODY_COPY"), mCopyIconImage, true ) ); + + if ( !mPasteIconImage ) + { + mPasteIconImage = ResourceImage::New( OPTION_ICON_PASTE ); + } + mOrderListOfButtons.push_back( ButtonRequirement( ButtonsPaste, mPasteOptionPriority, OPTION_PASTE, GET_LOCALE_TEXT("IDS_COM_BODY_PASTE"), mPasteIconImage, true ) ); + + if ( !mSelectIconImage ) + mSelectIconImage = ResourceImage::New( OPTION_ICON_SELECT ); + mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelect, mSelectOptionPriority, OPTION_SELECT_WORD, GET_LOCALE_TEXT("IDS_COM_SK_SELECT"), mSelectIconImage, true ) ); + + if ( !mSelectAllIconImage ) + { + mSelectAllIconImage = ResourceImage::New( OPTION_ICON_SELECT_ALL ); + } + mOrderListOfButtons.push_back( ButtonRequirement( ButtonsSelectAll, mSelectAllOptionPriority, OPTION_SELECT_ALL, GET_LOCALE_TEXT("IDS_COM_BODY_SELECT_ALL"), mSelectAllIconImage, true ) ); + + if ( !mClipboardIconImage ) + { + mClipboardIconImage = ResourceImage::New( OPTION_ICON_CLIPBOARD ); + } + mOrderListOfButtons.push_back( ButtonRequirement( ButtonsClipboard, mClipboardOptionPriority, OPTION_CLIPBOARD, GET_LOCALE_TEXT("IDS_COM_BODY_CLIPBOARD"), mClipboardIconImage, true ) ); + + // Sort the buttons according their priorities. + std::sort( mOrderListOfButtons.begin(), mOrderListOfButtons.end(), TextSelectionPopup::ButtonPriorityCompare() ); + } + + void TextSelectionPopup::CreateBackground() + { + if ( !mBackgroundImage ) + { + mBackgroundImage = ResourceImage::New( DEFAULT_POPUP_BACKGROUND_IMAGE ); + } + + NinePatchImage backgroundImageNinePatch = NinePatchImage::DownCast( mBackgroundImage ); + if( backgroundImageNinePatch ) + { + const Size ninePatchImageSize = Size( static_cast( mBackgroundImage.GetWidth() ), static_cast( mBackgroundImage.GetHeight() ) ); + Rect childRect = backgroundImageNinePatch.GetChildRectangle(); + + // -1u because of the cropping. + mNinePatchMargins.x = childRect.x - 1u; + mNinePatchMargins.y = ninePatchImageSize.width - ( childRect.x + childRect.width ) - 1u; + mNinePatchMargins.z = childRect.y - 1u; + mNinePatchMargins.w = ninePatchImageSize.height - ( childRect.y + childRect.height ) - 1u; + } + + SetBackgroundImage( mBackgroundImage ); + SetBackgroundColor( mBackgroundColor ); + } + + void TextSelectionPopup::AddOption( Actor& parent, const std::string& name, const std::string& caption, const Image iconImage, bool finalOption, bool showIcons ) + { + // 1. Create the backgrounds for the popup option both normal and pressed. + // Both containers will be added to a button. + Actor optionContainer = Actor::New(); + optionContainer.SetRelayoutEnabled( true ); + optionContainer.SetResizePolicy( FIXED, ALL_DIMENSIONS ); + optionContainer.SetDrawMode( DrawMode::OVERLAY ); + optionContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + + ImageActor optionPressedContainer = Toolkit::CreateSolidColorActor( mBackgroundPressedColor ); + optionPressedContainer.SetResizePolicy( FIXED, ALL_DIMENSIONS ); + optionPressedContainer.SetDrawMode( DrawMode::OVERLAY ); + optionPressedContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + + // 2. Add text. + Toolkit::TextLabel captionTextLabel = Toolkit::TextLabel::New(); + captionTextLabel.SetResizePolicy( FIXED, ALL_DIMENSIONS ); + captionTextLabel.SetProperty( Toolkit::TextLabel::Property::TEXT, caption ); + // optionContainer.Add( captionTextLabel ); Temporary removed. + + Toolkit::TextLabel pressedCaptionTextLabel = Toolkit::TextLabel::New(); + pressedCaptionTextLabel.SetResizePolicy( FIXED, ALL_DIMENSIONS ); + pressedCaptionTextLabel.SetProperty( Toolkit::TextLabel::Property::TEXT, caption ); + // optionPressedContainer.Add( pressedCaptionTextLabel ); Temporary removed. + + // Calculates the icon/text position. + float iconTextOffsetY = 0.0f; + + if ( showIcons ) + { + // 3. Create the icons + ImageActor pressedIcon = ImageActor::New( iconImage ); + ImageActor icon = ImageActor::New( iconImage ); + + optionContainer.Add( icon ); + optionPressedContainer.Add( pressedIcon ); + + iconTextOffsetY = 0.5f * ( ( DEFAULT_POPUP_MAX_SIZE.height - mNinePatchMargins.z - mNinePatchMargins.w ) - ( OPTION_ICON_SIZE.height + OPTION_GAP_ICON_TEXT + OPTION_TEXT_LINE_HEIGHT ) ); + + icon.SetParentOrigin( ParentOrigin::TOP_CENTER ); + icon.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + icon.SetY( iconTextOffsetY ); + + pressedIcon.SetParentOrigin( ParentOrigin::TOP_CENTER ); + pressedIcon.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + pressedIcon.SetY( iconTextOffsetY ); + + // Layout icon + gap + text + captionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + captionTextLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + pressedCaptionTextLabel.SetY( -iconTextOffsetY ); + captionTextLabel.SetY( -iconTextOffsetY ); + } + else + { + // Centre option text + captionTextLabel.SetAnchorPoint( AnchorPoint::CENTER ); + captionTextLabel.SetParentOrigin( ParentOrigin::CENTER ); + pressedCaptionTextLabel.SetAnchorPoint( AnchorPoint::CENTER ); + pressedCaptionTextLabel.SetParentOrigin( ParentOrigin::CENTER ); + } + + // Calculate the size of the text. + Vector3 textSize = captionTextLabel.GetNaturalSize(); + textSize.width = std::min( textSize.width, OPTION_MAX_WIDTH - 2.f * OPTION_MARGIN_WIDTH ); + + // Set the size to the text. Text will be ellipsized if exceeds the max width. - captionTextLabel.SetPreferredSize( textSize.GetVectorXY() ); - pressedCaptionTextLabel.SetPreferredSize( textSize.GetVectorXY() ); ++ captionTextLabel.SetSize( textSize.GetVectorXY() ); ++ pressedCaptionTextLabel.SetSize( textSize.GetVectorXY() ); + + // 4. Calculate the size of option. + + // The width is the max size of the text or the icon plus the margins clamped between the option min and max size. + // The height is the whole popup height minus the ninepatch margins. + const Vector2 optionSize( std::min( OPTION_MAX_WIDTH, std::max( OPTION_MIN_WIDTH, std::max( textSize.width, OPTION_ICON_SIZE.width ) + 2.f * OPTION_MARGIN_WIDTH ) ), + DEFAULT_POPUP_MAX_SIZE.height - mNinePatchMargins.z - mNinePatchMargins.w ); + - optionContainer.SetPreferredSize( optionSize ); - optionPressedContainer.SetPreferredSize( optionSize ); ++ optionContainer.SetSize( optionSize ); ++ optionPressedContainer.SetSize( optionSize ); + + // 5. Create a option. + Toolkit::PushButton option = Toolkit::PushButton::New(); + option.SetResizePolicy( FIXED, ALL_DIMENSIONS ); - option.SetPreferredSize( optionSize ); ++ option.SetSize( optionSize ); + option.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + option.SetX( mContentSize.width ); + option.SetName( name ); + option.SetAnimationTime( 0.0f ); + //option.ClickedSignal().Connect( this, &TextInputPopup::OnButtonPressed ); + + parent.Add( option ); + + // 6. Set the normal option image. + option.SetButtonImage( optionContainer ); + + // 7. Set the pressed option image + option.SetSelectedImage( optionPressedContainer ); + + // 8. Update the content size. + mContentSize.width += optionSize.width; + mContentSize.height = std::max ( optionSize.height, mContentSize.height ); + + // 9. Add the divider + if( !finalOption ) + { + const Size size( POPUP_DIVIDER_WIDTH, mContentSize.height ); + + ImageActor divider = Toolkit::CreateSolidColorActor( Color::WHITE ); + divider.SetResizePolicy( FIXED, ALL_DIMENSIONS ); - divider.SetPreferredSize( size ); ++ divider.SetSize( size ); + divider.SetParentOrigin( ParentOrigin::TOP_LEFT ); + divider.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + divider.SetPosition( mContentSize.width - POPUP_DIVIDER_WIDTH, 0.0f ); + parent.Add( divider ); + } + } + + void TextSelectionPopup::SetUpPopup( Size& size ) + { + Actor self = Self(); + + // Create Layer and Stencil. + mStencilLayer = Layer::New(); + ImageActor stencil = CreateSolidColorActor( Color::RED ); + stencil.SetDrawMode( DrawMode::STENCIL ); + stencil.SetVisible( true ); + Actor scrollview = Actor::New(); //todo make a scrollview + stencil.SetRelayoutEnabled( true ); + + self.SetResizePolicy( FIXED, ALL_DIMENSIONS ); - self.SetPreferredSize( mRequiredPopUpSize ); // control matches stencil size ++ self.SetSize( mRequiredPopUpSize ); // control matches stencil size + + mStencilLayer.SetResizePolicy( FIXED, ALL_DIMENSIONS ); - mStencilLayer.SetPreferredSize( size ); // matches stencil size ++ mStencilLayer.SetSize( size ); // matches stencil size + + stencil.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS ); + scrollview.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS ); + mButtons.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS ); + + mStencilLayer.SetAnchorPoint(AnchorPoint::TOP_LEFT); + scrollview.SetAnchorPoint(AnchorPoint::TOP_LEFT); + mButtons.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + + mStencilLayer.SetPosition( mNinePatchMargins.x, mNinePatchMargins.y ); + + self.Add( mStencilLayer ); + mStencilLayer.Add( stencil ); + mStencilLayer.Add( scrollview ); + scrollview.Add( mButtons ); + } + + void TextSelectionPopup::AddPopupOptions( bool createTail, bool showIcons ) + { + mShowIcons = showIcons; + + mContentSize = Vector2::ZERO; + + mButtons = Actor::New(); + mButtons.SetRelayoutEnabled( true ); + + // Add the options into the buttons container. + + // 1. Determine how many buttons are active and should be added to container. + std::size_t numberOfOptions = 0u; + for( std::vector::const_iterator it = mOrderListOfButtons.begin(), endIt = mOrderListOfButtons.end(); ( it != endIt ); ++it ) + { + const ButtonRequirement& button( *it ); + if( button.enabled ) + { + ++numberOfOptions; + } + } + + // 2. Iterate list of buttons and add active ones. + std::size_t optionsAdded = 0u; + for( std::vector::const_iterator it = mOrderListOfButtons.begin(), endIt = mOrderListOfButtons.end(); ( it != endIt ); ++it ) + { + const ButtonRequirement& button( *it ); + if ( button.enabled ) + { + ++optionsAdded; + AddOption( mButtons, button.name, button.caption, button.icon, optionsAdded == numberOfOptions, mShowIcons ); + } + } + + // Calculate the size of the whole popup which may not be all visible. + mRequiredPopUpSize = Size( std::min( mMaxSize.width, mContentSize.width + mNinePatchMargins.x + mNinePatchMargins.y ), DEFAULT_POPUP_MAX_SIZE.height ); + + // Size of the contents within the popup + mVisiblePopUpSize = Size( mRequiredPopUpSize.width - mNinePatchMargins.x - mNinePatchMargins.y, mRequiredPopUpSize.height - mNinePatchMargins.z - mNinePatchMargins.w ); + } + + void TextSelectionPopup::CreatePopup() + { + if ( !mStencilLayer ) + { + CreateOrderedListOfPopupOptions(); //todo Currently causes all options to be shown + CreateBackground(); + AddPopupOptions( true, true ); + SetUpPopup( mVisiblePopUpSize ); + } + + mStencilLayer.RaiseToTop(); + } + +TextSelectionPopup::TextSelectionPopup() +: Control( ControlBehaviour( CONTROL_BEHAVIOUR_NONE ) ), + mMaxSize ( DEFAULT_POPUP_MAX_SIZE ), + mVisiblePopUpSize( DEFAULT_POPUP_MAX_SIZE ), + mRequiredPopUpSize( DEFAULT_POPUP_MAX_SIZE ), + mBackgroundColor( DEFAULT_POPUP_BACKGROUND ), + mBackgroundPressedColor( DEFAULT_POPUP_BACKGROUND_PRESSED ), + mLineColor( DEFAULT_POPUP_LINE_COLOR ), + mIconColor( DEFAULT_OPTION_ICON ), + mIconPressedColor( DEFAULT_OPTION_ICON_PRESSED ), + mTextColor( DEFAULT_OPTION_TEXT ), + mTextPressedColor( DEFAULT_OPTION_TEXT_PRESSED ), + mSelectOptionPriority( 1 ), + mSelectAllOptionPriority ( 2 ), + mCutOptionPriority ( 3 ), + mCopyOptionPriority ( 4 ), + mPasteOptionPriority ( 5 ), + mClipboardOptionPriority( 6 ), + mShowIcons( true ) +{ +} + +TextSelectionPopup::~TextSelectionPopup() +{ +} + + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --cc dali-toolkit/internal/text/decorator/text-decorator.cpp index 97aa0c8,0000000..b4a0705 mode 100644,000000..100644 --- a/dali-toolkit/internal/text/decorator/text-decorator.cpp +++ b/dali-toolkit/internal/text/decorator/text-decorator.cpp @@@ -1,876 -1,0 +1,875 @@@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_ENABLED +#define DECORATOR_DEBUG +#endif + +// Local Data +namespace +{ + +const char* DEFAULT_GRAB_HANDLE_IMAGE( DALI_IMAGE_DIR "insertpoint-icon.png" ); +const char* DEFAULT_SELECTION_HANDLE_ONE( DALI_IMAGE_DIR "text-input-selection-handle-left.png" ); +const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-handle-right.png" ); +//const char* DEFAULT_SELECTION_HANDLE_ONE_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-left-press.png" ); +//const char* DEFAULT_SELECTION_HANDLE_TWO_PRESSED( DALI_IMAGE_DIR "text-input-selection-handle-right-press.png" ); + +const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f ); +const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f ); + +const std::size_t CURSOR_BLINK_INTERVAL = 500; // Cursor blink interval +const std::size_t MILLISECONDS = 1000; + +const float DISPLAYED_HIGHLIGHT_Z_OFFSET( -0.05f ); + +/** + * structure to hold coordinates of each quad, which will make up the mesh. + */ +struct QuadCoordinates +{ + /** + * Default constructor + */ + QuadCoordinates() + { + } + + /** + * Constructor + * @param[in] x1 left co-ordinate + * @param[in] y1 top co-ordinate + * @param[in] x2 right co-ordinate + * @param[in] y2 bottom co-ordinate + */ + QuadCoordinates(float x1, float y1, float x2, float y2) + : min(x1, y1), + max(x2, y2) + { + } + + Dali::Vector2 min; ///< top-left (minimum) position of quad + Dali::Vector2 max; ///< bottom-right (maximum) position of quad +}; + +typedef std::vector QuadContainer; + +} // end of namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Text +{ + +struct Decorator::Impl : public ConnectionTracker +{ + struct CursorImpl + { + CursorImpl() + : x(0.0f), + y(0.0f), + height(0.0f), + color(Dali::Color::WHITE) + { + } + + float x; + float y; + float height; + + Vector4 color; + }; + + struct SelectionHandleImpl + { + SelectionHandleImpl() + : x(0.0f), + y(0.0f), + cursorHeight(0.0f), + flipped(false) + { + } + + float x; + float y; + float cursorHeight; ///< Not the handle height + bool flipped; + + ImageActor actor; + Actor grabArea; + + Image pressedImage; + Image releasedImage; + }; + + Impl( Dali::Toolkit::Internal::Control& parent, Observer& observer ) + : mTextControlParent(parent), + mObserver(observer), + mActiveCursor(ACTIVE_CURSOR_NONE), + mActiveGrabHandle(false), + mActiveSelection( false ), + mActiveCopyPastePopup( false ), + mCursorBlinkInterval( CURSOR_BLINK_INTERVAL ), + mCursorBlinkDuration( 0.0f ), + mCursorBlinkStatus( true ), + mGrabDisplacementX( 0.0f ), + mGrabDisplacementY( 0.0f ), + mHighlightColor( 0.07f, 0.41f, 0.59f, 1.0f ), // light blue + mBoundingBox( Rect() ) + { + } + + /** + * Relayout of the decorations owned by the decorator. + * @param[in] size The Size of the UI control the decorater is adding it's decorations to. + */ + void Relayout( const Vector2& size, const Vector2& scrollPosition ) + { + // TODO - Remove this if nothing is active + CreateActiveLayer(); + + // Show or hide the cursors + CreateCursors(); + if( mPrimaryCursor ) + { + mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].x + scrollPosition.x, + mCursor[PRIMARY_CURSOR].y + scrollPosition.y ); + mPrimaryCursor.SetSize( Vector2( 1.0f, mCursor[PRIMARY_CURSOR].height ) ); + } + if( mSecondaryCursor ) + { + mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].x + scrollPosition.x, + mCursor[SECONDARY_CURSOR].y + scrollPosition.y ); + mSecondaryCursor.SetSize( Vector2( 1.0f, mCursor[SECONDARY_CURSOR].height ) ); + } + + // Show or hide the grab handle + if( mActiveGrabHandle ) + { + SetupTouchEvents(); + + CreateGrabHandle(); + + mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].x + scrollPosition.x, + mCursor[PRIMARY_CURSOR].y + scrollPosition.y + mCursor[PRIMARY_CURSOR].height ); + } + else if( mGrabHandle ) + { + UnparentAndReset( mGrabHandle ); + } + + // Show or hide the selection handles/highlight + if( mActiveSelection ) + { + SetupTouchEvents(); + + CreateSelectionHandles(); + + SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ]; + primary.actor.SetPosition( primary.x + scrollPosition.x, + primary.y + scrollPosition.y + primary.cursorHeight ); + + SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ]; + secondary.actor.SetPosition( secondary.x + scrollPosition.x, + secondary.y + scrollPosition.y + secondary.cursorHeight ); + + CreateHighlight(); + UpdateHighlight(); + } + else + { + UnparentAndReset( mSelectionHandle[ PRIMARY_SELECTION_HANDLE ].actor ); + UnparentAndReset( mSelectionHandle[ SECONDARY_SELECTION_HANDLE ].actor ); + UnparentAndReset( mHighlightMeshActor ); + } + + if ( mActiveCopyPastePopup ) + { + if ( !mCopyPastePopup ) + { + mCopyPastePopup = TextSelectionPopup::New(); + mActiveLayer.Add ( mCopyPastePopup ); + } + mCopyPastePopup.SetPosition( Vector3( 200.0f, -100.0f, 0.0f ) ); //todo grabhandle or selection handle positions to be used + } + else + { + if ( mCopyPastePopup ) + { + UnparentAndReset( mCopyPastePopup ); + } + } + } + + void CreateCursor( ImageActor& cursor ) + { + cursor = CreateSolidColorActor( Color::WHITE ); + cursor.SetParentOrigin( ParentOrigin::TOP_LEFT ); // Need to set the default parent origin as CreateSolidColorActor() sets a different one. + cursor.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + cursor.SetRelayoutEnabled( false ); + } + + // Add or Remove cursor(s) from parent + void CreateCursors() + { + if( mActiveCursor == ACTIVE_CURSOR_NONE ) + { + UnparentAndReset( mPrimaryCursor ); + UnparentAndReset( mSecondaryCursor ); + } + else + { + /* Create Primary and or Secondary Cursor(s) if active and add to parent */ + if ( mActiveCursor == ACTIVE_CURSOR_PRIMARY || + mActiveCursor == ACTIVE_CURSOR_BOTH ) + { + if ( !mPrimaryCursor ) + { + CreateCursor( mPrimaryCursor ); +#ifdef DECORATOR_DEBUG + mPrimaryCursor.SetName( "PrimaryCursorActor" ); +#endif + mActiveLayer.Add( mPrimaryCursor); + } + } + + if ( mActiveCursor == ACTIVE_CURSOR_BOTH ) + { + if ( !mSecondaryCursor ) + { + CreateCursor( mSecondaryCursor ); +#ifdef DECORATOR_DEBUG + mSecondaryCursor.SetName( "SecondaryCursorActor" ); +#endif + mActiveLayer.Add( mSecondaryCursor); + } + } + } + } + + bool OnCursorBlinkTimerTick() + { + // Cursor blinking + if ( mPrimaryCursor ) + { + mPrimaryCursor.SetVisible( mCursorBlinkStatus ); + } + if ( mSecondaryCursor ) + { + mSecondaryCursor.SetVisible( mCursorBlinkStatus ); + } + + mCursorBlinkStatus = !mCursorBlinkStatus; + + return true; + } + + void SetupTouchEvents() + { + if ( !mTapDetector ) + { + mTapDetector = TapGestureDetector::New(); + mTapDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnTap ); + } + + if ( !mPanGestureDetector ) + { + mPanGestureDetector = PanGestureDetector::New(); + mPanGestureDetector.DetectedSignal().Connect( this, &Decorator::Impl::OnPan ); + } + } + + void CreateActiveLayer() + { + if( !mActiveLayer ) + { + Actor parent = mTextControlParent.Self(); + + mActiveLayer = Layer::New(); +#ifdef DECORATOR_DEBUG + mActiveLayer.SetName ( "ActiveLayerActor" ); +#endif + + mActiveLayer.SetParentOrigin( ParentOrigin::CENTER ); + mActiveLayer.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS ); + mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION ); + + parent.Add( mActiveLayer ); + } + + mActiveLayer.RaiseToTop(); + } + + void CreateGrabHandle() + { + if( !mGrabHandle ) + { + if ( !mGrabHandleImage ) + { + mGrabHandleImage = ResourceImage::New( DEFAULT_GRAB_HANDLE_IMAGE ); + } + + mGrabHandle = ImageActor::New( mGrabHandleImage ); +#ifdef DECORATOR_DEBUG + mGrabHandle.SetName( "GrabHandleActor" ); +#endif + mGrabHandle.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + mGrabHandle.SetDrawMode( DrawMode::OVERLAY ); + // Area that Grab handle responds to, larger than actual handle so easier to move +#ifdef DECORATOR_DEBUG + mGrabArea = Toolkit::CreateSolidColorActor( Vector4(0.0f, 0.0f, 0.0f, 0.0f), true, Color::RED, 1 ); + mGrabArea.SetName( "GrabArea" ); +#else + mGrabArea = Actor::New(); + mGrabArea.SetRelayoutEnabled( true ); +#endif + mGrabArea.SetParentOrigin( ParentOrigin::TOP_CENTER ); + mGrabArea.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - mGrabArea.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS ); - mGrabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT ); ++ mGrabArea.SetResizePolicy( SIZE_RELATIVE_TO_PARENT, ALL_DIMENSIONS ); + mGrabArea.SetSizeModeFactor( DEFAULT_GRAB_HANDLE_RELATIVE_SIZE ); + mGrabHandle.Add( mGrabArea ); + + mTapDetector.Attach( mGrabArea ); + mPanGestureDetector.Attach( mGrabArea ); + + mActiveLayer.Add(mGrabHandle); + } + } + + void CreateSelectionHandles() + { + SelectionHandleImpl& primary = mSelectionHandle[ PRIMARY_SELECTION_HANDLE ]; + if ( !primary.actor ) + { + if ( !primary.releasedImage ) + { + primary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_ONE ); + } + + primary.actor = ImageActor::New( primary.releasedImage ); +#ifdef DECORATOR_DEBUG + primary.actor.SetName("SelectionHandleOne"); +#endif + primary.actor.SetAnchorPoint( AnchorPoint::TOP_RIGHT ); // Change to BOTTOM_RIGHT if Look'n'Feel requires handle above text. + primary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text + primary.flipped = false; + + primary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move + primary.grabArea.SetRelayoutEnabled( true ); +#ifdef DECORATOR_DEBUG + primary.grabArea.SetName("SelectionHandleOneGrabArea"); +#endif - primary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT ); ++ primary.grabArea.SetResizePolicy( SIZE_RELATIVE_TO_PARENT, ALL_DIMENSIONS ); + primary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ); + primary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + + mTapDetector.Attach( primary.grabArea ); + mPanGestureDetector.Attach( primary.grabArea ); + primary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleOneTouched ); + + primary.actor.Add( primary.grabArea ); + mActiveLayer.Add( primary.actor ); + } + + SelectionHandleImpl& secondary = mSelectionHandle[ SECONDARY_SELECTION_HANDLE ]; + if ( !secondary.actor ) + { + if ( !secondary.releasedImage ) + { + secondary.releasedImage = ResourceImage::New( DEFAULT_SELECTION_HANDLE_TWO ); + } + + secondary.actor = ImageActor::New( secondary.releasedImage ); +#ifdef DECORATOR_DEBUG + secondary.actor.SetName("SelectionHandleTwo"); +#endif + secondary.actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); // Change to BOTTOM_LEFT if Look'n'Feel requires handle above text. + secondary.actor.SetDrawMode( DrawMode::OVERLAY ); // ensure grab handle above text + secondary.flipped = false; + + secondary.grabArea = Actor::New(); // Area that Grab handle responds to, larger than actual handle so easier to move + secondary.grabArea.SetRelayoutEnabled( true ); +#ifdef DECORATOR_DEBUG + secondary.grabArea.SetName("SelectionHandleTwoGrabArea"); +#endif - secondary.grabArea.SetSizeMode( SIZE_RELATIVE_TO_PARENT ); ++ secondary.grabArea.SetResizePolicy( SIZE_RELATIVE_TO_PARENT, ALL_DIMENSIONS ); + secondary.grabArea.SetSizeModeFactor( DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE ); + secondary.grabArea.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION ); + + mTapDetector.Attach( secondary.grabArea ); + mPanGestureDetector.Attach( secondary.grabArea ); + secondary.grabArea.TouchedSignal().Connect( this, &Decorator::Impl::OnHandleTwoTouched ); + + secondary.actor.Add( secondary.grabArea ); + mActiveLayer.Add( secondary.actor ); + } + + //SetUpHandlePropertyNotifications(); TODO + } + + void CreateHighlight() + { + if ( !mHighlightMeshActor ) + { + mHighlightMaterial = Material::New( "HighlightMaterial" ); + mHighlightMaterial.SetDiffuseColor( mHighlightColor ); + + mHighlightMeshData.SetMaterial( mHighlightMaterial ); + mHighlightMeshData.SetHasNormals( true ); + + mHighlightMesh = Mesh::New( mHighlightMeshData ); + + mHighlightMeshActor = MeshActor::New( mHighlightMesh ); +#ifdef DECORATOR_DEBUG + mHighlightMeshActor.SetName( "HighlightMeshActor" ); +#endif + mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET ); + + Actor parent = mTextControlParent.Self(); + parent.Add( mHighlightMeshActor ); + } + } + + void UpdateHighlight() + { + // Construct a Mesh with a texture to be used as the highlight 'box' for selected text + // + // Example scenarios where mesh is made from 3, 1, 2, 2 ,3 or 3 quads. + // + // [ TOP ] [ TOP ] [TOP ] [ TOP ] [ TOP ] [ TOP ] + // [ MIDDLE ] [BOTTOM] [BOTTOM] [ MIDDLE ] [ MIDDLE ] + // [ BOTTOM] [ MIDDLE ] [ MIDDLE ] + // [BOTTOM] [ MIDDLE ] + // [BOTTOM] + // + // Each quad is created as 2 triangles. + // Middle is just 1 quad regardless of its size. + // + // (0,0) (0,0) + // 0* *2 0* *2 + // TOP TOP + // 3* *1 3* *1 + // 4* *1 4* *6 + // MIDDLE BOTTOM + // 6* *5 7* *5 + // 6* *8 + // BOTTOM + // 9* *7 + // + + if ( mHighlightMesh && mHighlightMaterial && !mHighlightQuadList.empty() ) + { + MeshData::VertexContainer vertices; + Dali::MeshData::FaceIndices faceIndices; + + std::vector::iterator iter = mHighlightQuadList.begin(); + std::vector::iterator endIter = mHighlightQuadList.end(); + + // vertex position defaults to (0 0 0) + MeshData::Vertex vertex; + // set normal for all vertices as (0 0 1) pointing outward from TextInput Actor. + vertex.nZ = 1.0f; + + for(std::size_t v = 0; iter != endIter; ++iter,v+=4 ) + { + // Add each quad geometry (a sub-selection) to the mesh data. + + // 0-----1 + // |\ | + // | \ A | + // | \ | + // | B \ | + // | \| + // 2-----3 + + QuadCoordinates& quad = *iter; + // top-left (v+0) + vertex.x = quad.min.x; + vertex.y = quad.min.y; + vertices.push_back( vertex ); + + // top-right (v+1) + vertex.x = quad.max.x; + vertex.y = quad.min.y; + vertices.push_back( vertex ); + + // bottom-left (v+2) + vertex.x = quad.min.x; + vertex.y = quad.max.y; + vertices.push_back( vertex ); + + // bottom-right (v+3) + vertex.x = quad.max.x; + vertex.y = quad.max.y; + vertices.push_back( vertex ); + + // triangle A (3, 1, 0) + faceIndices.push_back( v + 3 ); + faceIndices.push_back( v + 1 ); + faceIndices.push_back( v ); + + // triangle B (0, 2, 3) + faceIndices.push_back( v ); + faceIndices.push_back( v + 2 ); + faceIndices.push_back( v + 3 ); + + mHighlightMeshData.SetFaceIndices( faceIndices ); + } + + BoneContainer bones(0); // passed empty as bones not required + mHighlightMeshData.SetData( vertices, faceIndices, bones, mHighlightMaterial ); + mHighlightMesh.UpdateMeshData( mHighlightMeshData ); + } + } + + void OnTap( Actor actor, const TapGesture& tap ) + { + if( actor == mGrabHandle ) + { + // TODO + } + } + + void OnPan( Actor actor, const PanGesture& gesture ) + { + if( actor == mGrabArea ) + { + if( Gesture::Started == gesture.state ) + { + mGrabDisplacementX = mGrabDisplacementY = 0; + } + + mGrabDisplacementX += gesture.displacement.x; + mGrabDisplacementY += gesture.displacement.y; + + float x = mCursor[PRIMARY_CURSOR].x + mGrabDisplacementX; + float y = mCursor[PRIMARY_CURSOR].y + mCursor[PRIMARY_CURSOR].height*0.5f + mGrabDisplacementY; + + if( Gesture::Started == gesture.state || + Gesture::Continuing == gesture.state ) + { + mObserver.GrabHandleEvent( GRAB_HANDLE_PRESSED, x, y ); + } + else if( Gesture::Finished == gesture.state || + Gesture::Cancelled == gesture.state ) + { + mObserver.GrabHandleEvent( GRAB_HANDLE_RELEASED, x, y ); + } + } + } + + bool OnHandleOneTouched( Actor actor, const TouchEvent& touch ) + { + // TODO + return false; + } + + bool OnHandleTwoTouched( Actor actor, const TouchEvent& touch ) + { + // TODO + return false; + } + + + Internal::Control& mTextControlParent; + Observer& mObserver; + + Layer mActiveLayer; // Layer for active handles and alike that ensures they are above all else. + + unsigned int mActiveCursor; + bool mActiveGrabHandle; + bool mActiveSelection; + bool mActiveCopyPastePopup; + + CursorImpl mCursor[CURSOR_COUNT]; + + Timer mCursorBlinkTimer; // Timer to signal cursor to blink + unsigned int mCursorBlinkInterval; + float mCursorBlinkDuration; + bool mCursorBlinkStatus; // Flag to switch between blink on and blink off + + ImageActor mPrimaryCursor; + ImageActor mSecondaryCursor; + + ImageActor mGrabHandle; + Actor mGrabArea; + float mGrabDisplacementX; + float mGrabDisplacementY; + + SelectionHandleImpl mSelectionHandle[SELECTION_HANDLE_COUNT]; + + MeshActor mHighlightMeshActor; ///< Mesh Actor to display highlight + Mesh mHighlightMesh; ///< Mesh for highlight + MeshData mHighlightMeshData; ///< Mesh Data for highlight + Material mHighlightMaterial; ///< Material used for highlight + Vector4 mHighlightColor; ///< Color of the highlight + QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight + + TextSelectionPopup mCopyPastePopup; + + Image mCursorImage; + Image mGrabHandleImage; + + TapGestureDetector mTapDetector; + PanGestureDetector mPanGestureDetector; + + Rect mBoundingBox; +}; + +DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer ) +{ + return DecoratorPtr( new Decorator(parent, observer) ); +} + +void Decorator::SetBoundingBox( const Rect& boundingBox ) +{ + mImpl->mBoundingBox = boundingBox; +} + +const Rect& Decorator::GetBoundingBox() const +{ + return mImpl->mBoundingBox; +} + +void Decorator::Relayout( const Vector2& size, const Vector2& scrollPosition ) +{ + mImpl->Relayout( size, scrollPosition ); +} + +/** Cursor **/ + +void Decorator::SetActiveCursor( ActiveCursor activeCursor ) +{ + mImpl->mActiveCursor = activeCursor; +} + +unsigned int Decorator::GetActiveCursor() const +{ + return mImpl->mActiveCursor; +} + +void Decorator::SetPosition( Cursor cursor, float x, float y, float height ) +{ + // Adjust grab handle displacement + mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].x; + mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].y; + + mImpl->mCursor[cursor].x = x; + mImpl->mCursor[cursor].y = y; + mImpl->mCursor[cursor].height = height; +} + +void Decorator::GetPosition( Cursor cursor, float& x, float& y, float& height ) const +{ + x = mImpl->mCursor[cursor].x; + y = mImpl->mCursor[cursor].y; + height = mImpl->mCursor[cursor].height; +} + +void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color ) +{ + mImpl->mCursor[cursor].color = color; +} + +const Dali::Vector4& Decorator::GetColor( Cursor cursor ) const +{ + return mImpl->mCursor[cursor].color; +} + +void Decorator::StartCursorBlink() +{ + if ( !mImpl->mCursorBlinkTimer ) + { + mImpl->mCursorBlinkTimer = Timer::New( mImpl->mCursorBlinkInterval ); + mImpl->mCursorBlinkTimer.TickSignal().Connect( mImpl, &Decorator::Impl::OnCursorBlinkTimerTick ); + } + + if ( !mImpl->mCursorBlinkTimer.IsRunning() ) + { + mImpl->mCursorBlinkTimer.Start(); + } +} + +void Decorator::StopCursorBlink() +{ + if ( mImpl->mCursorBlinkTimer ) + { + mImpl->mCursorBlinkTimer.Stop(); + } +} + +void Decorator::SetCursorBlinkInterval( float seconds ) +{ + mImpl->mCursorBlinkInterval = seconds*MILLISECONDS; // Convert to milliseconds +} + +float Decorator::GetCursorBlinkInterval() const +{ + return mImpl->mCursorBlinkInterval; +} + +void Decorator::SetCursorBlinkDuration( float seconds ) +{ + mImpl->mCursorBlinkDuration = seconds; +} + +float Decorator::GetCursorBlinkDuration() const +{ + return mImpl->mCursorBlinkDuration; +} + +/** GrabHandle **/ + +void Decorator::SetGrabHandleActive( bool active ) +{ + mImpl->mActiveGrabHandle = active; +} + +bool Decorator::IsGrabHandleActive() const +{ + return mImpl->mActiveGrabHandle; +} + +void Decorator::SetGrabHandleImage( Dali::Image image ) +{ + mImpl->mGrabHandleImage = image; +} + +Dali::Image Decorator::GetGrabHandleImage() const +{ + return mImpl->mGrabHandleImage; +} + +/** Selection **/ + +void Decorator::SetSelectionActive( bool active ) +{ + mImpl->mActiveSelection = active; +} + +bool Decorator::IsSelectionActive() const +{ + return mImpl->mActiveSelection; +} + +void Decorator::SetPosition( SelectionHandle handle, float x, float y, float height ) +{ + mImpl->mSelectionHandle[handle].x = x; + mImpl->mSelectionHandle[handle].y = y; + mImpl->mSelectionHandle[handle].cursorHeight = height; +} + +void Decorator::GetPosition( SelectionHandle handle, float& x, float& y, float& height ) const +{ + x = mImpl->mSelectionHandle[handle].x; + y = mImpl->mSelectionHandle[handle].y; + height = mImpl->mSelectionHandle[handle].cursorHeight; +} + +void Decorator::SetImage( SelectionHandle handle, SelectionHandleState state, Dali::Image image ) +{ + if( SELECTION_HANDLE_PRESSED == state ) + { + mImpl->mSelectionHandle[handle].pressedImage = image; + } + else + { + mImpl->mSelectionHandle[handle].releasedImage = image; + } +} + +Dali::Image Decorator::GetImage( SelectionHandle handle, SelectionHandleState state ) const +{ + if( SELECTION_HANDLE_PRESSED == state ) + { + return mImpl->mSelectionHandle[handle].pressedImage; + } + + return mImpl->mSelectionHandle[handle].releasedImage; +} + +void Decorator::AddHighlight( float x1, float y1, float x2, float y2 ) +{ + mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) ); +} + +void Decorator::ClearHighlights() +{ + mImpl->mHighlightQuadList.clear(); +} + +void Decorator::SetPopupActive( bool active ) +{ + mImpl->mActiveCopyPastePopup = active; +} + +bool Decorator::IsPopupActive() const +{ + return mImpl->mActiveCopyPastePopup ; +} + +Decorator::~Decorator() +{ + delete mImpl; +} + +Decorator::Decorator( Dali::Toolkit::Internal::Control& parent, Observer& observer ) +: mImpl( NULL ) +{ + mImpl = new Decorator::Impl( parent, observer ); +} + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali