2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <dali-toolkit/internal/controls/text-input/text-input-popup-impl.h>
19 #include <dali-toolkit/public-api/controls/buttons/push-button.h>
27 const char* DEFAULT_PANEL_BACKGROUND = DALI_IMAGE_DIR "cutCopyPastePopup_bg.png";
30 const char* DEFAULT_PANEL_BUTTON_DIVIDER = DALI_IMAGE_DIR "copypanelLine.png";
32 /* Functionality in place to have the end buttons using different images to inner button.
33 * Supply a centre image and then a left and right image, the centre image can have straight ends while
34 * the left image can be rounded on the left and straight on the right, the right image can be straight on the left and rounded on the right.
37 // Popup: Left Pressed Highlight
38 const char* DEFAULT_BUTTON_HIGHLIGHT_LEFT( DALI_IMAGE_DIR "00_popup_button_pressed.png" );
39 const Vector4 DEFAULT_BUTTON_HIGHLIGHT_LEFT_BORDER( 6.0f, 9.0f, 6.0f, 9.0f );
41 // Popup: Center Pressed Highlight
42 const char* DEFAULT_BUTTON_HIGHLIGHT_CENTER( DALI_IMAGE_DIR "00_popup_button_pressed.png" );
43 const Vector4 DEFAULT_BUTTON_HIGHLIGHT_CENTER_BORDER( 6.0f, 9.0f, 6.0f, 9.0f );
45 // Popup: Right Pressed Highlight
46 const char* DEFAULT_BUTTON_HIGHLIGHT_RIGHT( DALI_IMAGE_DIR "00_popup_button_pressed.png" );
47 const Vector4 DEFAULT_BUTTON_HIGHLIGHT_RIGHT_BORDER( 6.0f, 9.0f, 6.0f, 9.0f );
50 const char* DEFAULT_POPUP_TAIL_BOTTOM( DALI_IMAGE_DIR "00_popup_bubble_tail_bottom.png" );
52 // Popup: Vertical Constraint
53 // TODO: Remove - this should come from application - it is not possible to get the
54 // height of the indicator actor from Dali-Toolkit.
55 const Vector2 DEFAULT_POPUP_INDICATOR_OFFSET(0.0f, 60.0f);
57 const Vector4 BACKGROUND_IMAGE_BORDER( 22.0f, 20.0f, 29.0f, 27.0f );
58 const Vector2 BACKGROUND_IMAGE_SIZE( 50.0f, 54.0f );
59 const Vector3 POPUP_TEXT_OFFSET( 12.0f, 10.0f, 0.0f );
60 const Vector3 POPUP_TEXT_ENLARGE( 12.0f, 28.0f, 0.0f );
61 const Vector3 POPUP_MINIMUM_SIZE( 128.0f, 124.0f, 0.0f );
63 const Vector3 BUTTON_TEXT_ENLARGE( 32.0f, 0.0f, 0.0f );
64 const Vector3 BUTTON_TEXT_MINIMUM_SIZE( 128.0f, 126.0f, 0.0f );
65 const Vector3 BUTTON_TEXT_MAXIMUM_SIZE( 196.0f, 126.0f, 0.0f );
66 const Vector3 TEXT_LABEL_MAX_SIZE( 160.0f, 30.0f, 0.0f );
68 const float DIVIDER_WIDTH(2.0f); ///< Width of each button divider
69 const float DIVIDER_MARGIN(10.0f); ///< Top/Bottom Margin between divider and edge of popup.
71 const float DEFAULT_UI_FONT_SIZE(7.0f); ///< Standard font size for Text-Input's UI
73 const float HIDE_POPUP_ANIMATION_DURATION(0.2f); ///< Duration of popup hide animation in seconds.
74 const float SHOW_POPUP_ANIMATION_DURATION(0.2f); ///< Duration of popup show animation in seconds.
76 const Vector2 DEFAULT_ICON_SIZE( 45.0f, 45.0f ); ///< Default icon size for image in options
77 const float TEXT_POSITION_OFFSET( -19.0f ); ///< Default offset for text label
78 const float ICON_POSITION_OFFSET( 19.0f ); ///< Default offset for icon
80 // TODO: This should be based on the content for example:
81 // 1. For selection: should be above top of highlighted selection, or below bottom of highlighted selection + end handle.
82 // 2. For cursor: should be above top of cursor, or below bottom of cursor + grab handle.
83 const std::string POPUP_ALTERNATIVE_OFFSET("popup-alternative-offset"); ///< Alternative offset property for confinenment constraint.
87 * Confine Actor to boundaries of reference actor (e.g. Parent)
88 * Actor bounds (top-left position + size) are confined to reference Actor's
91 struct ConfinementConstraint
94 * Confinement constraint constructor.
95 * @param[in] topLeftMargin (optional) Top-Left margins (defaults to 0.0f, 0.0f)
96 * @param[in] bottomRightMargin (optional) Bottom-Right margins (defaults to 0.0f, 0.0f)
97 * @param[in] flipVertical (optional) whether to flip Actor to the other side if near edge, and by
98 * how much (defaults to 0.0f i.e. no flip)
100 ConfinementConstraint(Vector2 topLeftMargin = Vector2::ZERO, Vector2 bottomRightMargin = Vector2::ZERO, bool flipHorizontal = false, bool flipVertical = false)
101 : mMinIndent(topLeftMargin),
102 mMaxIndent(bottomRightMargin),
103 mFlipHorizontal(flipHorizontal),
104 mFlipVertical(flipVertical)
108 Vector3 operator()(const Vector3& constPosition,
109 const PropertyInput& sizeProperty,
110 const PropertyInput& parentOriginProperty,
111 const PropertyInput& anchorPointProperty,
112 const PropertyInput& referenceSizeProperty,
113 const PropertyInput& alternativeOffsetProperty)
115 const Vector3& size = sizeProperty.GetVector3();
116 const Vector3& origin = parentOriginProperty.GetVector3();
117 const Vector3& anchor = anchorPointProperty.GetVector3();
118 const Vector3& referenceSize = referenceSizeProperty.GetVector3();
119 const Vector2& alternativeOffset = alternativeOffsetProperty.GetVector2();
121 Vector3 newPosition(constPosition);
123 // Get actual position of Actor relative to parent's Top-Left.
124 Vector3 position(constPosition + origin * referenceSize);
126 // if top-left corner is outside of Top-Left bounds, then push back in screen.
127 Vector3 corner(position - size * anchor - mMinIndent);
129 if(mFlipHorizontal && corner.x < 0.0f)
132 newPosition.x += size.width + alternativeOffset.width;
135 if(mFlipVertical && corner.y < 0.0f)
138 newPosition.y += size.height + alternativeOffset.height;
141 newPosition.x -= std::min(corner.x, 0.0f);
142 newPosition.y -= std::min(corner.y, 0.0f);
144 // if bottom-right corner is outside of Bottom-Right bounds, then push back in screen.
145 corner += size - referenceSize + mMinIndent + mMaxIndent;
147 if(mFlipHorizontal && corner.x > 0.0f)
150 newPosition.x -= size.width + alternativeOffset.width;
153 if(mFlipVertical && corner.y > 0.0f)
156 newPosition.y -= size.height + alternativeOffset.height;
159 newPosition.x -= std::max(corner.x, 0.0f);
160 newPosition.y -= std::max(corner.y, 0.0f);
165 Vector3 mMinIndent; ///< Top-Left Margin
166 Vector3 mMaxIndent; ///< Bottom-Right Margin.
167 bool mFlipHorizontal; ///< Whether to flip actor's position if exceeds horizontal screen bounds
168 bool mFlipVertical; ///< Whether to flip actor's position if exceeds vertical screen bounds
171 } // unnamed namespace
182 const char* const TextInputPopup::SIGNAL_PRESSED = "pressed";
183 const char* const TextInputPopup::SIGNAL_HIDE_FINISHED = "hide-finished";
184 const char* const TextInputPopup::SIGNAL_SHOW_FINISHED = "show-finished";
186 TextInputPopup::TextInputPopup()
187 : mState(StateHidden),
188 mRootActor(Layer::New()),
190 mHideFinishedSignal(),
191 mShowFinishedSignal()
193 mAlternativeOffsetProperty = mRootActor.RegisterProperty( POPUP_ALTERNATIVE_OFFSET, Vector2::ZERO );
194 mRootActor.SetParentOrigin( ParentOrigin::CENTER );
195 mRootActor.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
196 // constrain popup to size of parent.
199 Actor TextInputPopup::Self()
204 void TextInputPopup::AddToStage()
206 // TODO: Confinement constraint borders should be defined by the application.
207 // It should also not use the stage directly, instead it should add to parent container.
208 Stage::GetCurrent().Add(mRootActor);
210 ApplyConfinementConstraint();
213 void TextInputPopup::ApplyConfinementConstraint()
215 mRootActor.RemoveConstraints();
216 Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
217 LocalSource( Actor::SIZE ),
218 LocalSource( Actor::PARENT_ORIGIN ),
219 LocalSource( Actor::ANCHOR_POINT ),
220 ParentSource( Actor::SIZE ),
221 LocalSource( mAlternativeOffsetProperty ),
222 ConfinementConstraint( DEFAULT_POPUP_INDICATOR_OFFSET,
226 mRootActor.ApplyConstraint(constraint);
229 void TextInputPopup::RemoveFromStage()
231 Actor rootActor = Self();
232 Stage::GetCurrent().Remove( rootActor );
235 void TextInputPopup::Clear()
239 mRootActor.Remove( mBackground );
241 mButtonContainer.clear();
242 mDividerContainer.clear();
245 mRootActor.RemoveConstraints();
247 mState = StateHidden;
251 Toolkit::TextView TextInputPopup::CreateOptionText( const MarkupProcessor::StyledTextArray& styledCaption )
253 Toolkit::TextView label = Toolkit::TextView::New( styledCaption );
254 label.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
255 label.SetWidthExceedPolicy( Toolkit::TextView::Fade );
256 label.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
257 label.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
258 label.SetPosition( 0.0f, TEXT_POSITION_OFFSET );
263 ImageActor TextInputPopup::CreateOptionIcon( Image iconImage )
265 ImageActor icon = ImageActor::New( iconImage );
267 icon.SetSize( DEFAULT_ICON_SIZE );
268 icon.SetParentOrigin( ParentOrigin::TOP_CENTER );
269 icon.SetAnchorPoint( AnchorPoint::TOP_CENTER );
270 icon.SetPosition( 0.0f, ICON_POSITION_OFFSET );
275 void TextInputPopup::CreatePopUpBackground()
277 // Create background-panel if not already created (required if we have at least one option)
280 Image backgroundImage = Image::New( DEFAULT_PANEL_BACKGROUND );
282 mBackground = ImageActor::New( backgroundImage );
283 // Expand background from bottom-center of root actor.
284 mBackground.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
285 mBackground.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
286 mBackground.SetStyle( ImageActor::STYLE_NINE_PATCH );
288 mBackground.SetNinePatchBorder( Vector4(13.0f, 13.0f, 13.0f, 13.0f) );
290 Self().Add( mBackground );
291 mContentSize = POPUP_TEXT_OFFSET;
297 Image tailImage = Image::New( DEFAULT_POPUP_TAIL_BOTTOM );
299 mTail = ImageActor::New( tailImage );
300 mTail.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
301 mTail.SetAnchorPoint( AnchorPoint::TOP_CENTER );
302 mBackground.Add( mTail );
303 // TODO: Make tail visible, and positioned in relation to original intended position of popup (i.e. before constrained effects)
304 mTail.SetVisible(false);
308 void TextInputPopup::CreateDivider()
310 if(mButtonContainer.size() > 0)
312 Image dividerImage = Image::New( DEFAULT_PANEL_BUTTON_DIVIDER );
313 ImageActor divider = ImageActor::New( dividerImage );
314 divider.SetParentOrigin( ParentOrigin::TOP_LEFT );
315 divider.SetAnchorPoint( AnchorPoint::TOP_LEFT );
316 divider.SetPosition( Vector3( mContentSize.width, POPUP_TEXT_OFFSET.y + 5.0f, 0.0f ) );
317 // Keep track of all the dividers. As their height's need to be updated to the max. of all
318 // buttons currently added.
319 mDividerContainer.push_back(divider);
321 mBackground.Add( divider );
322 mContentSize.width += DIVIDER_WIDTH;
326 ImageActor TextInputPopup::CreatePressedBackground( const Vector3 requiredSize, const bool finalFlag )
328 std::string pressedImageFilename;
329 Vector4 pressedImageBorder;
330 Vector2 pressedImageSize;
332 if(mButtonContainer.size() == 0) // LEFT
334 pressedImageFilename = DEFAULT_BUTTON_HIGHLIGHT_LEFT;
335 pressedImageBorder = DEFAULT_BUTTON_HIGHLIGHT_LEFT_BORDER;
337 else if(!finalFlag) // CENTER
339 pressedImageFilename = DEFAULT_BUTTON_HIGHLIGHT_CENTER;
340 pressedImageBorder = DEFAULT_BUTTON_HIGHLIGHT_CENTER_BORDER;
344 pressedImageFilename = DEFAULT_BUTTON_HIGHLIGHT_RIGHT;
345 pressedImageBorder = DEFAULT_BUTTON_HIGHLIGHT_RIGHT_BORDER;
348 Image pressedImage = Image::New( pressedImageFilename );
349 ImageActor pressedImageBg = ImageActor::New( pressedImage );
350 pressedImageBg.SetStyle( ImageActor::STYLE_NINE_PATCH );
351 pressedImageBg.SetNinePatchBorder( pressedImageBorder );
352 pressedImageBg.SetSize ( requiredSize );
353 pressedImageBg.SetParentOrigin( ParentOrigin::CENTER );
354 pressedImageBg.SetAnchorPoint( AnchorPoint::CENTER );
356 return pressedImageBg;
359 void TextInputPopup::AddOption(const std::string& name, const std::string& caption, const Image iconImage, bool finalOption)
361 CreatePopUpBackground();
365 // Create a Button with Text, Icon and highlight when pressed
367 Toolkit::PushButton button = Toolkit::PushButton::New();
368 button.SetSizePolicy( Toolkit::Control::Fixed, Toolkit::Control::Fixed );
369 button.SetName( name );
371 // Create container for text and icon when not pressed
372 Actor iconTextContainer = Actor::New();
373 iconTextContainer.SetParentOrigin( ParentOrigin::TOP_LEFT );
374 iconTextContainer.SetAnchorPoint( AnchorPoint::TOP_LEFT );
378 style.SetFontPointSize( PointSize( DEFAULT_UI_FONT_SIZE ) );
379 MarkupProcessor::StyledTextArray styledCaption;
380 styledCaption.push_back( MarkupProcessor::StyledText( Text( caption ), style ) );
381 Toolkit::TextView label = CreateOptionText( styledCaption );
382 label.SetName( name );
384 iconTextContainer.Add( label );
386 // Get natural size of text and then constrain it to bounds.
387 const Vector3 textSize = label.GetNaturalSize();
388 const Vector3 constrainedTextSize = Min( textSize, TEXT_LABEL_MAX_SIZE );
389 Vector3 buttonSize( Max(constrainedTextSize + BUTTON_TEXT_ENLARGE, BUTTON_TEXT_MINIMUM_SIZE) );
390 buttonSize = ( Min(buttonSize, BUTTON_TEXT_MAXIMUM_SIZE) );
391 label.SetSize( Min( buttonSize + BUTTON_TEXT_ENLARGE, constrainedTextSize ) );
393 button.SetParentOrigin( ParentOrigin::TOP_LEFT );
394 button.SetAnchorPoint( AnchorPoint::TOP_LEFT );
395 button.SetSize( buttonSize );
396 button.SetPosition( Vector3( mContentSize.width, POPUP_TEXT_OFFSET.y, 0.0f ) );
399 ImageActor icon = CreateOptionIcon( iconImage );
401 iconTextContainer.Add( icon );
403 // 3. Add highlight - Pressed state in Pushbutton needs a new image which means creating the text and icon again but including a highlight this time.
404 ImageActor pressedImageBg = CreatePressedBackground( buttonSize, finalOption );
406 Actor iconPressedTextContainer = Actor::New();
407 iconPressedTextContainer.SetDrawMode( DrawMode::OVERLAY );
409 Toolkit::TextView pressedLabel = CreateOptionText( styledCaption );
410 pressedLabel.SetSize( Min( buttonSize, TEXT_LABEL_MAX_SIZE ) );
411 ImageActor pressedIcon = CreateOptionIcon( iconImage );
413 iconPressedTextContainer.Add( pressedImageBg );
414 iconPressedTextContainer.Add( pressedLabel );
415 iconPressedTextContainer.Add( pressedIcon );
417 // Set Pressed button Image
418 iconPressedTextContainer.SetSize( buttonSize );
419 button.SetPressedImage( iconPressedTextContainer );
421 // Set Normal button Image
422 iconTextContainer.SetSize( buttonSize );
423 button.SetButtonImage( iconTextContainer );
424 mBackground.Add( button );
426 // Update content size (represents size of all content i.e. from top-left of first button, to bottom-right of last button)
427 mContentSize.width += buttonSize.width;
428 mContentSize.height = std::max(mContentSize.height, buttonSize.height);
429 mButtonContainer.push_back(button);
431 // resize all dividers based on the height content (i.e. max of all button heights)
432 const float dividerHeight = mContentSize.height - DIVIDER_MARGIN;
433 for(ActorIter i = mDividerContainer.begin(); i != mDividerContainer.end(); ++i)
435 i->SetSize( DIVIDER_WIDTH, dividerHeight );
438 Vector3 popupSize( Max(mContentSize + POPUP_TEXT_ENLARGE, POPUP_MINIMUM_SIZE) );
440 mBackground.SetSize( popupSize );
441 // Make Root Actor reflect the size of its content
442 mRootActor.SetSize( popupSize );
443 mTail.SetPosition(Vector3(0.0f, -20.0f, 0.0f));
445 button.ClickedSignal().Connect( this, &TextInputPopup::OnButtonPressed );
448 void TextInputPopup::Hide(bool animate)
460 mAnimation = Animation::New( HIDE_POPUP_ANIMATION_DURATION );
461 mAnimation.AnimateTo( Property(mBackground, Actor::SCALE), Vector3::ZERO, AlphaFunctions::EaseOut );
462 mAnimation.AnimateTo( Property(mBackground, Actor::COLOR_ALPHA), 0.0f, AlphaFunctions::EaseOut );
465 mAnimation.FinishedSignal().Connect( this, &TextInputPopup::OnHideFinished );
466 mState = StateHiding;
470 mBackground.SetProperty(Actor::SCALE, Vector3::ZERO);
471 mBackground.SetProperty(Actor::COLOR_ALPHA, 0.0f);
472 mState = StateHidden;
477 void TextInputPopup::Show(bool animate)
489 mAnimation = Animation::New( SHOW_POPUP_ANIMATION_DURATION );
490 mAnimation.AnimateTo( Property(mBackground, Actor::SCALE), Vector3::ONE, AlphaFunctions::EaseOut );
491 mAnimation.AnimateTo( Property(mBackground, Actor::COLOR_ALPHA), 1.0f, AlphaFunctions::EaseOut );
494 mAnimation.FinishedSignal().Connect( this, &TextInputPopup::OnShowFinished );
495 mState = StateShowing;
499 mBackground.SetProperty(Actor::SCALE, Vector3::ONE);
500 mBackground.SetProperty(Actor::COLOR_ALPHA, 1.0f);
506 void TextInputPopup::SetAlternativeOffset(Vector2 offset)
508 mRootActor.SetProperty( mAlternativeOffsetProperty, offset );
509 ApplyConfinementConstraint();
512 TextInputPopup::State TextInputPopup::GetState(void) const
517 Actor TextInputPopup::GetRootActor() const
522 bool TextInputPopup::OnButtonPressed( Toolkit::Button button )
524 mPressedSignal.Emit( button );
528 void TextInputPopup::OnHideFinished(Animation& source)
530 source.FinishedSignal().Disconnect( this, &TextInputPopup::OnHideFinished );
532 mState = StateHidden;
533 mHideFinishedSignal.Emit( *this );
536 void TextInputPopup::OnShowFinished(Animation& source)
538 source.FinishedSignal().Disconnect( this, &TextInputPopup::OnShowFinished );
540 mShowFinishedSignal.Emit( *this );
543 TextInputPopup::PressedSignalV2& TextInputPopup::PressedSignal()
545 return mPressedSignal;
548 TextInputPopup::HideFinishedSignalV2& TextInputPopup::HideFinishedSignal()
550 return mHideFinishedSignal;
553 TextInputPopup::ShowFinishedSignalV2& TextInputPopup::ShowFinishedSignal()
555 return mShowFinishedSignal;
558 } // namespace Internal
560 } // namespace Toolkit