/* * Copyright (c) 2020 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. * */ #include #include #include #include "shared/utility.h" #include "shared/view.h" using namespace Dali; namespace { const char* const TOOLBAR_IMAGE(DEMO_IMAGE_DIR "top-bar.png"); const char* const APPLICATION_TITLE("Bubble Effect"); const char* const CHANGE_BACKGROUND_ICON(DEMO_IMAGE_DIR "icon-change.png"); const char* const CHANGE_BACKGROUND_ICON_SELECTED(DEMO_IMAGE_DIR "icon-change-selected.png"); const char* const CHANGE_BUBBLE_SHAPE_ICON(DEMO_IMAGE_DIR "icon-replace.png"); const char* const CHANGE_BUBBLE_SHAPE_ICON_SELECTED(DEMO_IMAGE_DIR "icon-replace-selected.png"); const char* BACKGROUND_IMAGES[] = { DEMO_IMAGE_DIR "background-1.jpg", DEMO_IMAGE_DIR "background-2.jpg", DEMO_IMAGE_DIR "background-3.jpg", DEMO_IMAGE_DIR "background-4.jpg", DEMO_IMAGE_DIR "background-5.jpg", }; const unsigned int NUM_BACKGROUND_IMAGES(sizeof(BACKGROUND_IMAGES) / sizeof(BACKGROUND_IMAGES[0])); const char* BUBBLE_SHAPE_IMAGES[] = { DEMO_IMAGE_DIR "bubble-ball.png", DEMO_IMAGE_DIR "icon-effect-cross.png", DEMO_IMAGE_DIR "icon-item-view-layout-spiral.png", DEMO_IMAGE_DIR "icon-replace.png"}; const unsigned int NUM_BUBBLE_SHAPE_IMAGES(sizeof(BUBBLE_SHAPE_IMAGES) / sizeof(BUBBLE_SHAPE_IMAGES[0])); const Vector2 DEFAULT_BUBBLE_SIZE(10.f, 30.f); const unsigned int DEFAULT_NUMBER_OF_BUBBLES(1000); } // namespace // This example shows the usage of BubbleEmitter which displays lots of moving bubbles on the window. class BubbleEffectExample : public ConnectionTracker { public: BubbleEffectExample(Application& app) : mApp(app), mBackground(), mBubbleEmitter(), mEmitAnimation(), mChangeBackgroundButton(), mChangeBubbleShapeButton(), mTimerForBubbleEmission(), mHSVDelta(Vector3(0.f, 0.f, 0.5f)), mCurrentTouchPosition(), mEmitPosition(), mAnimateComponentCount(0), mNonMovementCount(0), mTimerInterval(16), mCurrentBackgroundImageId(0), mCurrentBubbleShapeImageId(0), mNeedNewAnimation(true) { // Connect to the Application's Init signal app.InitSignal().Connect(this, &BubbleEffectExample::Create); } ~BubbleEffectExample() { } private: // The Init signal is received once (only) during the Application lifetime void Create(Application& app) { Window window = app.GetWindow(); Vector2 windowSize = window.GetSize(); window.KeyEventSignal().Connect(this, &BubbleEffectExample::OnKeyEvent); // Creates a default view with a default tool bar. // The view is added to the window. Toolkit::ToolBar toolBar; Layer content = DemoHelper::CreateView(app, mBackground, toolBar, "", TOOLBAR_IMAGE, APPLICATION_TITLE); // Add a button to change background. (right of toolbar) mChangeBackgroundButton = Toolkit::PushButton::New(); mChangeBackgroundButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_BACKGROUND_ICON); mChangeBackgroundButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_BACKGROUND_ICON_SELECTED); mChangeBackgroundButton.ClickedSignal().Connect(this, &BubbleEffectExample::OnChangeIconClicked); toolBar.AddControl(mChangeBackgroundButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HORIZONTAL_RIGHT, DemoHelper::DEFAULT_MODE_SWITCH_PADDING); // Add a button to change bubble shape. ( left of bar ) mChangeBubbleShapeButton = Toolkit::PushButton::New(); mChangeBubbleShapeButton.SetProperty(Toolkit::Button::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_BUBBLE_SHAPE_ICON); mChangeBubbleShapeButton.SetProperty(Toolkit::Button::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_BUBBLE_SHAPE_ICON_SELECTED); mChangeBubbleShapeButton.ClickedSignal().Connect(this, &BubbleEffectExample::OnChangeIconClicked); toolBar.AddControl(mChangeBubbleShapeButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HORIZONTAL_LEFT, DemoHelper::DEFAULT_MODE_SWITCH_PADDING); // Create and initialize the BubbleEmitter object mBubbleEmitter = Toolkit::BubbleEmitter::New(windowSize, DemoHelper::LoadTexture(BUBBLE_SHAPE_IMAGES[mCurrentBubbleShapeImageId]), DEFAULT_NUMBER_OF_BUBBLES, DEFAULT_BUBBLE_SIZE); mBubbleEmitter.SetBackground(DemoHelper::LoadWindowFillingTexture(window.GetSize(), BACKGROUND_IMAGES[mCurrentBackgroundImageId]), mHSVDelta); // Get the root actor of all bubbles, and add it to window. Actor bubbleRoot = mBubbleEmitter.GetRootActor(); bubbleRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); bubbleRoot.SetProperty(Actor::Property::POSITION_Z, 0.1f); // Make sure the bubbles displayed on top og the background. content.Add(bubbleRoot); // Set the application background mBackground.SetProperty(Toolkit::Control::Property::BACKGROUND, BACKGROUND_IMAGES[mCurrentBackgroundImageId]); // Set up the timer to emit bubble regularly when the finger is touched down but not moved mTimerForBubbleEmission = Timer::New(mTimerInterval); mTimerForBubbleEmission.TickSignal().Connect(this, &BubbleEffectExample::OnTimerTick); // Connect the callback to the touch signal on the background mBackground.TouchedSignal().Connect(this, &BubbleEffectExample::OnTouch); } /*********** * Emit bubbles *****************/ // Set up the animation of emitting bubbles, to be efficient, every animation controls multiple emission ( 4 here ) void SetUpAnimation(Vector2 emitPosition, Vector2 direction) { if(mNeedNewAnimation) { float duration = Random::Range(1.f, 1.5f); mEmitAnimation = Animation::New(duration); mNeedNewAnimation = false; mAnimateComponentCount = 0; } mBubbleEmitter.EmitBubble(mEmitAnimation, emitPosition, direction + Vector2(0.f, 30.f) /* upwards */, Vector2(300, 600)); mAnimateComponentCount++; if(mAnimateComponentCount % 4 == 0) { mEmitAnimation.Play(); mNeedNewAnimation = true; } } // Emit bubbles when the finger touches down but keep stationary. // And stops emitting new bubble after being stationary for 2 seconds bool OnTimerTick() { if(mEmitPosition == mCurrentTouchPosition) // finger is not moving { mNonMovementCount++; if(mNonMovementCount < (1000 / mTimerInterval)) // 1 seconds { for(int i = 0; i < 4; i++) // emit 4 bubbles every timer tick { SetUpAnimation(mCurrentTouchPosition + Vector2(rand() % 5, rand() % 5), Vector2(rand() % 60 - 30, rand() % 100 - 50)); } } } else { mNonMovementCount = 0; mEmitPosition = mCurrentTouchPosition; } return true; } // Callback function of the touch signal on the background bool OnTouch(Dali::Actor actor, const Dali::TouchEvent& event) { switch(event.GetState(0)) { case PointState::DOWN: { mCurrentTouchPosition = mEmitPosition = event.GetScreenPosition(0); mTimerForBubbleEmission.Start(); mNonMovementCount = 0; break; } case PointState::MOTION: { Vector2 displacement = event.GetScreenPosition(0) - mCurrentTouchPosition; mCurrentTouchPosition = event.GetScreenPosition(0); //emit multiple bubbles along the moving direction when the finger moves quickly float step = std::min(5.f, displacement.Length()); for(float i = 0.25f; i < step; i = i + 1.f) { SetUpAnimation(mCurrentTouchPosition + displacement * (i / step), displacement); } break; } case PointState::UP: case PointState::LEAVE: case PointState::INTERRUPTED: { mTimerForBubbleEmission.Stop(); mEmitAnimation.Play(); mNeedNewAnimation = true; mAnimateComponentCount = 0; break; } case PointState::STATIONARY: default: { break; } } return true; } bool OnChangeIconClicked(Toolkit::Button button) { if(button == mChangeBackgroundButton) { mCurrentBackgroundImageId = (mCurrentBackgroundImageId + 1) % NUM_BACKGROUND_IMAGES; //Update bubble emitter background mBubbleEmitter.SetBackground(DemoHelper::LoadWindowFillingTexture(mApp.GetWindow().GetSize(), BACKGROUND_IMAGES[mCurrentBackgroundImageId]), mHSVDelta); // Set the application background mBackground.SetProperty(Toolkit::Control::Property::BACKGROUND, BACKGROUND_IMAGES[mCurrentBackgroundImageId]); } else if(button == mChangeBubbleShapeButton) { mBubbleEmitter.SetBubbleShape(DemoHelper::LoadTexture(BUBBLE_SHAPE_IMAGES[++mCurrentBubbleShapeImageId % NUM_BUBBLE_SHAPE_IMAGES])); } return true; } /** * Main key event handler */ void OnKeyEvent(const KeyEvent& event) { if(event.GetState() == KeyEvent::DOWN) { if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK)) { mApp.Quit(); } } } private: Application& mApp; Dali::Toolkit::Control mBackground; Toolkit::BubbleEmitter mBubbleEmitter; Animation mEmitAnimation; Toolkit::PushButton mChangeBackgroundButton; Toolkit::PushButton mChangeBubbleShapeButton; Timer mTimerForBubbleEmission; Vector3 mHSVDelta; Vector2 mCurrentTouchPosition; Vector2 mEmitPosition; unsigned int mAnimateComponentCount; unsigned int mNonMovementCount; unsigned int mTimerInterval; unsigned int mCurrentBackgroundImageId; unsigned int mCurrentBubbleShapeImageId; bool mNeedNewAnimation; }; /*****************************************************************************/ int DALI_EXPORT_API main(int argc, char** argv) { Application app = Application::New(&argc, &argv, DEMO_THEME_PATH); BubbleEffectExample theApp(app); app.MainLoop(); return 0; }