From 8531a1914dee440dd13ee932fd543e21dad174d4 Mon Sep 17 00:00:00 2001 From: halx99 Date: Tue, 4 Aug 2020 12:31:33 +0800 Subject: [PATCH] Add extension fairygui support --- extensions/CMakeLists.txt | 6 + extensions/fairygui/CMakeLists.txt | 10 + extensions/fairygui/Controller.cpp | 204 ++ extensions/fairygui/Controller.h | 61 + extensions/fairygui/DragDropManager.cpp | 85 + extensions/fairygui/DragDropManager.h | 34 + extensions/fairygui/FairyGUI.h | 32 + extensions/fairygui/FairyGUIMacros.h | 36 + extensions/fairygui/FieldTypes.h | 201 ++ extensions/fairygui/GButton.cpp | 453 +++ extensions/fairygui/GButton.h | 101 + extensions/fairygui/GComboBox.cpp | 452 +++ extensions/fairygui/GComboBox.h | 102 + extensions/fairygui/GComponent.cpp | 1385 +++++++++ extensions/fairygui/GComponent.h | 150 + extensions/fairygui/GGraph.cpp | 290 ++ extensions/fairygui/GGraph.h | 54 + extensions/fairygui/GGroup.cpp | 467 +++ extensions/fairygui/GGroup.h | 74 + extensions/fairygui/GImage.cpp | 164 ++ extensions/fairygui/GImage.h | 55 + extensions/fairygui/GLabel.cpp | 190 ++ extensions/fairygui/GLabel.h | 51 + extensions/fairygui/GList.cpp | 2558 +++++++++++++++++ extensions/fairygui/GList.h | 190 ++ extensions/fairygui/GLoader.cpp | 603 ++++ extensions/fairygui/GLoader.h | 110 + extensions/fairygui/GLoader3D.cpp | 470 +++ extensions/fairygui/GLoader3D.h | 110 + extensions/fairygui/GMovieClip.cpp | 483 ++++ extensions/fairygui/GMovieClip.h | 106 + extensions/fairygui/GObject.cpp | 1026 +++++++ extensions/fairygui/GObject.h | 265 ++ extensions/fairygui/GObjectPool.cpp | 41 + extensions/fairygui/GObjectPool.h | 26 + extensions/fairygui/GProgressBar.cpp | 234 ++ extensions/fairygui/GProgressBar.h | 60 + extensions/fairygui/GRichTextField.cpp | 114 + extensions/fairygui/GRichTextField.h | 41 + extensions/fairygui/GRoot.cpp | 569 ++++ extensions/fairygui/GRoot.h | 102 + extensions/fairygui/GScrollBar.cpp | 179 ++ extensions/fairygui/GScrollBar.h | 51 + extensions/fairygui/GSlider.cpp | 290 ++ extensions/fairygui/GSlider.h | 72 + extensions/fairygui/GTextField.cpp | 377 +++ extensions/fairygui/GTextField.h | 98 + extensions/fairygui/GTextInput.cpp | 131 + extensions/fairygui/GTextInput.h | 47 + extensions/fairygui/GTree.cpp | 412 +++ extensions/fairygui/GTree.h | 73 + extensions/fairygui/GTreeNode.cpp | 310 ++ extensions/fairygui/GTreeNode.h | 74 + extensions/fairygui/Margin.cpp | 38 + extensions/fairygui/Margin.h | 78 + extensions/fairygui/PackageItem.cpp | 75 + extensions/fairygui/PackageItem.h | 69 + extensions/fairygui/PopupMenu.cpp | 239 ++ extensions/fairygui/PopupMenu.h | 55 + extensions/fairygui/RelationItem.cpp | 655 +++++ extensions/fairygui/RelationItem.h | 95 + extensions/fairygui/Relations.cpp | 145 + extensions/fairygui/Relations.h | 39 + extensions/fairygui/ScrollPane.cpp | 1789 ++++++++++++ extensions/fairygui/ScrollPane.h | 219 ++ extensions/fairygui/Transition.cpp | 1545 ++++++++++ extensions/fairygui/Transition.h | 94 + extensions/fairygui/TranslationHelper.cpp | 291 ++ extensions/fairygui/TranslationHelper.h | 22 + extensions/fairygui/UIConfig.cpp | 63 + extensions/fairygui/UIConfig.h | 48 + extensions/fairygui/UIObjectFactory.cpp | 145 + extensions/fairygui/UIObjectFactory.h | 35 + extensions/fairygui/UIPackage.cpp | 884 ++++++ extensions/fairygui/UIPackage.h | 89 + extensions/fairygui/Window.cpp | 296 ++ extensions/fairygui/Window.h | 99 + .../controller_action/ChangePageAction.cpp | 50 + .../controller_action/ChangePageAction.h | 24 + .../controller_action/ControllerAction.cpp | 56 + .../controller_action/ControllerAction.h | 33 + .../PlayTransitionAction.cpp | 46 + .../controller_action/PlayTransitionAction.h | 30 + extensions/fairygui/display/BitmapFont.cpp | 24 + extensions/fairygui/display/BitmapFont.h | 37 + extensions/fairygui/display/FUIContainer.cpp | 507 ++++ extensions/fairygui/display/FUIContainer.h | 115 + extensions/fairygui/display/FUIInput.cpp | 102 + extensions/fairygui/display/FUIInput.h | 48 + extensions/fairygui/display/FUILabel.cpp | 184 ++ extensions/fairygui/display/FUILabel.h | 45 + extensions/fairygui/display/FUIRichText.cpp | 780 +++++ extensions/fairygui/display/FUIRichText.h | 74 + extensions/fairygui/display/FUISprite.cpp | 535 ++++ extensions/fairygui/display/FUISprite.h | 66 + extensions/fairygui/display/TextFormat.cpp | 59 + extensions/fairygui/display/TextFormat.h | 50 + extensions/fairygui/event/EventContext.cpp | 23 + extensions/fairygui/event/EventContext.h | 46 + extensions/fairygui/event/HitTest.cpp | 58 + extensions/fairygui/event/HitTest.h | 53 + extensions/fairygui/event/InputEvent.cpp | 25 + extensions/fairygui/event/InputEvent.h | 51 + extensions/fairygui/event/InputProcessor.cpp | 754 +++++ extensions/fairygui/event/InputProcessor.h | 79 + .../fairygui/event/UIEventDispatcher.cpp | 285 ++ extensions/fairygui/event/UIEventDispatcher.h | 79 + extensions/fairygui/event/UIEventType.h | 52 + extensions/fairygui/gears/GearAnimation.cpp | 67 + extensions/fairygui/gears/GearAnimation.h | 41 + extensions/fairygui/gears/GearBase.cpp | 186 ++ extensions/fairygui/gears/GearBase.h | 60 + extensions/fairygui/gears/GearColor.cpp | 127 + extensions/fairygui/gears/GearColor.h | 46 + extensions/fairygui/gears/GearDisplay.cpp | 67 + extensions/fairygui/gears/GearDisplay.h | 38 + extensions/fairygui/gears/GearDisplay2.cpp | 56 + extensions/fairygui/gears/GearDisplay2.h | 35 + extensions/fairygui/gears/GearFontSize.cpp | 48 + extensions/fairygui/gears/GearFontSize.h | 32 + extensions/fairygui/gears/GearIcon.cpp | 49 + extensions/fairygui/gears/GearIcon.h | 32 + extensions/fairygui/gears/GearLook.cpp | 133 + extensions/fairygui/gears/GearLook.h | 48 + extensions/fairygui/gears/GearSize.cpp | 130 + extensions/fairygui/gears/GearSize.h | 37 + extensions/fairygui/gears/GearText.cpp | 48 + extensions/fairygui/gears/GearText.h | 32 + extensions/fairygui/gears/GearXY.cpp | 151 + extensions/fairygui/gears/GearXY.h | 40 + extensions/fairygui/tween/EaseManager.cpp | 200 ++ extensions/fairygui/tween/EaseManager.h | 17 + extensions/fairygui/tween/EaseType.h | 46 + extensions/fairygui/tween/GPath.cpp | 292 ++ extensions/fairygui/tween/GPath.h | 65 + extensions/fairygui/tween/GTween.cpp | 87 + extensions/fairygui/tween/GTween.h | 36 + extensions/fairygui/tween/GTweener.cpp | 465 +++ extensions/fairygui/tween/GTweener.h | 111 + extensions/fairygui/tween/TweenManager.cpp | 198 ++ extensions/fairygui/tween/TweenManager.h | 35 + extensions/fairygui/tween/TweenPropType.cpp | 130 + extensions/fairygui/tween/TweenPropType.h | 39 + extensions/fairygui/tween/TweenValue.cpp | 98 + extensions/fairygui/tween/TweenValue.h | 35 + extensions/fairygui/utils/ByteBuffer.cpp | 228 ++ extensions/fairygui/utils/ByteBuffer.h | 63 + extensions/fairygui/utils/ToolSet.cpp | 154 + extensions/fairygui/utils/ToolSet.h | 41 + extensions/fairygui/utils/UBBParser.cpp | 205 ++ extensions/fairygui/utils/UBBParser.h | 49 + extensions/fairygui/utils/WeakPtr.cpp | 144 + extensions/fairygui/utils/WeakPtr.h | 45 + 153 files changed, 29642 insertions(+) create mode 100644 extensions/fairygui/CMakeLists.txt create mode 100644 extensions/fairygui/Controller.cpp create mode 100644 extensions/fairygui/Controller.h create mode 100644 extensions/fairygui/DragDropManager.cpp create mode 100644 extensions/fairygui/DragDropManager.h create mode 100644 extensions/fairygui/FairyGUI.h create mode 100644 extensions/fairygui/FairyGUIMacros.h create mode 100644 extensions/fairygui/FieldTypes.h create mode 100644 extensions/fairygui/GButton.cpp create mode 100644 extensions/fairygui/GButton.h create mode 100644 extensions/fairygui/GComboBox.cpp create mode 100644 extensions/fairygui/GComboBox.h create mode 100644 extensions/fairygui/GComponent.cpp create mode 100644 extensions/fairygui/GComponent.h create mode 100644 extensions/fairygui/GGraph.cpp create mode 100644 extensions/fairygui/GGraph.h create mode 100644 extensions/fairygui/GGroup.cpp create mode 100644 extensions/fairygui/GGroup.h create mode 100644 extensions/fairygui/GImage.cpp create mode 100644 extensions/fairygui/GImage.h create mode 100644 extensions/fairygui/GLabel.cpp create mode 100644 extensions/fairygui/GLabel.h create mode 100644 extensions/fairygui/GList.cpp create mode 100644 extensions/fairygui/GList.h create mode 100644 extensions/fairygui/GLoader.cpp create mode 100644 extensions/fairygui/GLoader.h create mode 100644 extensions/fairygui/GLoader3D.cpp create mode 100644 extensions/fairygui/GLoader3D.h create mode 100644 extensions/fairygui/GMovieClip.cpp create mode 100644 extensions/fairygui/GMovieClip.h create mode 100644 extensions/fairygui/GObject.cpp create mode 100644 extensions/fairygui/GObject.h create mode 100644 extensions/fairygui/GObjectPool.cpp create mode 100644 extensions/fairygui/GObjectPool.h create mode 100644 extensions/fairygui/GProgressBar.cpp create mode 100644 extensions/fairygui/GProgressBar.h create mode 100644 extensions/fairygui/GRichTextField.cpp create mode 100644 extensions/fairygui/GRichTextField.h create mode 100644 extensions/fairygui/GRoot.cpp create mode 100644 extensions/fairygui/GRoot.h create mode 100644 extensions/fairygui/GScrollBar.cpp create mode 100644 extensions/fairygui/GScrollBar.h create mode 100644 extensions/fairygui/GSlider.cpp create mode 100644 extensions/fairygui/GSlider.h create mode 100644 extensions/fairygui/GTextField.cpp create mode 100644 extensions/fairygui/GTextField.h create mode 100644 extensions/fairygui/GTextInput.cpp create mode 100644 extensions/fairygui/GTextInput.h create mode 100644 extensions/fairygui/GTree.cpp create mode 100644 extensions/fairygui/GTree.h create mode 100644 extensions/fairygui/GTreeNode.cpp create mode 100644 extensions/fairygui/GTreeNode.h create mode 100644 extensions/fairygui/Margin.cpp create mode 100644 extensions/fairygui/Margin.h create mode 100644 extensions/fairygui/PackageItem.cpp create mode 100644 extensions/fairygui/PackageItem.h create mode 100644 extensions/fairygui/PopupMenu.cpp create mode 100644 extensions/fairygui/PopupMenu.h create mode 100644 extensions/fairygui/RelationItem.cpp create mode 100644 extensions/fairygui/RelationItem.h create mode 100644 extensions/fairygui/Relations.cpp create mode 100644 extensions/fairygui/Relations.h create mode 100644 extensions/fairygui/ScrollPane.cpp create mode 100644 extensions/fairygui/ScrollPane.h create mode 100644 extensions/fairygui/Transition.cpp create mode 100644 extensions/fairygui/Transition.h create mode 100644 extensions/fairygui/TranslationHelper.cpp create mode 100644 extensions/fairygui/TranslationHelper.h create mode 100644 extensions/fairygui/UIConfig.cpp create mode 100644 extensions/fairygui/UIConfig.h create mode 100644 extensions/fairygui/UIObjectFactory.cpp create mode 100644 extensions/fairygui/UIObjectFactory.h create mode 100644 extensions/fairygui/UIPackage.cpp create mode 100644 extensions/fairygui/UIPackage.h create mode 100644 extensions/fairygui/Window.cpp create mode 100644 extensions/fairygui/Window.h create mode 100644 extensions/fairygui/controller_action/ChangePageAction.cpp create mode 100644 extensions/fairygui/controller_action/ChangePageAction.h create mode 100644 extensions/fairygui/controller_action/ControllerAction.cpp create mode 100644 extensions/fairygui/controller_action/ControllerAction.h create mode 100644 extensions/fairygui/controller_action/PlayTransitionAction.cpp create mode 100644 extensions/fairygui/controller_action/PlayTransitionAction.h create mode 100644 extensions/fairygui/display/BitmapFont.cpp create mode 100644 extensions/fairygui/display/BitmapFont.h create mode 100644 extensions/fairygui/display/FUIContainer.cpp create mode 100644 extensions/fairygui/display/FUIContainer.h create mode 100644 extensions/fairygui/display/FUIInput.cpp create mode 100644 extensions/fairygui/display/FUIInput.h create mode 100644 extensions/fairygui/display/FUILabel.cpp create mode 100644 extensions/fairygui/display/FUILabel.h create mode 100644 extensions/fairygui/display/FUIRichText.cpp create mode 100644 extensions/fairygui/display/FUIRichText.h create mode 100644 extensions/fairygui/display/FUISprite.cpp create mode 100644 extensions/fairygui/display/FUISprite.h create mode 100644 extensions/fairygui/display/TextFormat.cpp create mode 100644 extensions/fairygui/display/TextFormat.h create mode 100644 extensions/fairygui/event/EventContext.cpp create mode 100644 extensions/fairygui/event/EventContext.h create mode 100644 extensions/fairygui/event/HitTest.cpp create mode 100644 extensions/fairygui/event/HitTest.h create mode 100644 extensions/fairygui/event/InputEvent.cpp create mode 100644 extensions/fairygui/event/InputEvent.h create mode 100644 extensions/fairygui/event/InputProcessor.cpp create mode 100644 extensions/fairygui/event/InputProcessor.h create mode 100644 extensions/fairygui/event/UIEventDispatcher.cpp create mode 100644 extensions/fairygui/event/UIEventDispatcher.h create mode 100644 extensions/fairygui/event/UIEventType.h create mode 100644 extensions/fairygui/gears/GearAnimation.cpp create mode 100644 extensions/fairygui/gears/GearAnimation.h create mode 100644 extensions/fairygui/gears/GearBase.cpp create mode 100644 extensions/fairygui/gears/GearBase.h create mode 100644 extensions/fairygui/gears/GearColor.cpp create mode 100644 extensions/fairygui/gears/GearColor.h create mode 100644 extensions/fairygui/gears/GearDisplay.cpp create mode 100644 extensions/fairygui/gears/GearDisplay.h create mode 100644 extensions/fairygui/gears/GearDisplay2.cpp create mode 100644 extensions/fairygui/gears/GearDisplay2.h create mode 100644 extensions/fairygui/gears/GearFontSize.cpp create mode 100644 extensions/fairygui/gears/GearFontSize.h create mode 100644 extensions/fairygui/gears/GearIcon.cpp create mode 100644 extensions/fairygui/gears/GearIcon.h create mode 100644 extensions/fairygui/gears/GearLook.cpp create mode 100644 extensions/fairygui/gears/GearLook.h create mode 100644 extensions/fairygui/gears/GearSize.cpp create mode 100644 extensions/fairygui/gears/GearSize.h create mode 100644 extensions/fairygui/gears/GearText.cpp create mode 100644 extensions/fairygui/gears/GearText.h create mode 100644 extensions/fairygui/gears/GearXY.cpp create mode 100644 extensions/fairygui/gears/GearXY.h create mode 100644 extensions/fairygui/tween/EaseManager.cpp create mode 100644 extensions/fairygui/tween/EaseManager.h create mode 100644 extensions/fairygui/tween/EaseType.h create mode 100644 extensions/fairygui/tween/GPath.cpp create mode 100644 extensions/fairygui/tween/GPath.h create mode 100644 extensions/fairygui/tween/GTween.cpp create mode 100644 extensions/fairygui/tween/GTween.h create mode 100644 extensions/fairygui/tween/GTweener.cpp create mode 100644 extensions/fairygui/tween/GTweener.h create mode 100644 extensions/fairygui/tween/TweenManager.cpp create mode 100644 extensions/fairygui/tween/TweenManager.h create mode 100644 extensions/fairygui/tween/TweenPropType.cpp create mode 100644 extensions/fairygui/tween/TweenPropType.h create mode 100644 extensions/fairygui/tween/TweenValue.cpp create mode 100644 extensions/fairygui/tween/TweenValue.h create mode 100644 extensions/fairygui/utils/ByteBuffer.cpp create mode 100644 extensions/fairygui/utils/ByteBuffer.h create mode 100644 extensions/fairygui/utils/ToolSet.cpp create mode 100644 extensions/fairygui/utils/ToolSet.h create mode 100644 extensions/fairygui/utils/UBBParser.cpp create mode 100644 extensions/fairygui/utils/UBBParser.h create mode 100644 extensions/fairygui/utils/WeakPtr.cpp create mode 100644 extensions/fairygui/utils/WeakPtr.h diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index 729a1fe584..91dabf78d9 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -13,6 +13,8 @@ option(BUILD_EXTENSION_DRAGONBONES "Build extension DragonBones" OFF) option(BUILD_EXTENSION_COCOSTUDIO "Build extension cocostudio" ON) +option(BUILD_EXTENSION_FAIRYGUI "Build extension FairyGUI" ON) + function(setup_cocos_extension_config target_name) if(ANDROID) target_link_libraries(${target_name} INTERFACE cocos2d) @@ -59,4 +61,8 @@ if(BUILD_EXTENSION_COCOSTUDIO) add_subdirectory(cocostudio) endif() +if(BUILD_EXTENSION_FAIRYGUI) + add_subdirectory(fairygui) +endif() + message(STATUS "CC_EXTENSION_LIBS:${CC_EXTENSION_LIBS}") diff --git a/extensions/fairygui/CMakeLists.txt b/extensions/fairygui/CMakeLists.txt new file mode 100644 index 0000000000..980320ae28 --- /dev/null +++ b/extensions/fairygui/CMakeLists.txt @@ -0,0 +1,10 @@ + +set(target_name fairygui) + +FILE(GLOB_RECURSE FAIRYGUI_SOURCES *.h;*.cpp) + +add_library(${target_name} STATIC ${FAIRYGUI_SOURCES}) + +target_include_directories(${target_name} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +setup_cocos_extension_config(${target_name}) diff --git a/extensions/fairygui/Controller.cpp b/extensions/fairygui/Controller.cpp new file mode 100644 index 0000000000..a668f41912 --- /dev/null +++ b/extensions/fairygui/Controller.cpp @@ -0,0 +1,204 @@ +#include "Controller.h" +#include "GComponent.h" +#include "controller_action/ControllerAction.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GController::GController() : changing(false), + autoRadioGroupDepth(false), + _parent(nullptr), + _selectedIndex(-1), + _previousIndex(-1) +{ +} + +GController::~GController() +{ + for (auto& it : _actions) + delete it; +} + +void GController::setSelectedIndex(int value, bool triggerEvent) +{ + if (_selectedIndex != value) + { + CCASSERT(value < (int)_pageIds.size(), "Invalid selected index"); + + changing = true; + + _previousIndex = _selectedIndex; + _selectedIndex = value; + _parent->applyController(this); + + if (triggerEvent) + dispatchEvent(UIEventType::Changed); + + changing = false; + } +} + +const std::string& GController::getSelectedPage() const +{ + if (_selectedIndex == -1) + return STD_STRING_EMPTY; + else + return _pageNames[_selectedIndex]; +} + +void GController::setSelectedPage(const std::string& value, bool triggerEvent) +{ + int i = ToolSet::findInStringArray(_pageNames, value); + if (i == -1) + i = 0; + setSelectedIndex(i, triggerEvent); +} + +const std::string& GController::getSelectedPageId() const +{ + if (_selectedIndex == -1) + return STD_STRING_EMPTY; + else + return _pageIds[_selectedIndex]; +} + +void GController::setSelectedPageId(const std::string& value, bool triggerEvent) +{ + int i = ToolSet::findInStringArray(_pageIds, value); + if (i != -1) + setSelectedIndex(i, triggerEvent); +} + +const std::string& GController::getPreviousPage() const +{ + if (_previousIndex == -1) + return STD_STRING_EMPTY; + else + return _pageNames[_previousIndex]; +} + +const std::string& GController::getPreviousPageId() const +{ + if (_previousIndex == -1) + return STD_STRING_EMPTY; + else + return _pageIds[_previousIndex]; +} + +int GController::getPageCount() const +{ + return (int)_pageIds.size(); +} + +bool GController::hasPage(const std::string& aName) const +{ + return ToolSet::findInStringArray(_pageNames, aName) != -1; +} + +int GController::getPageIndexById(const std::string& value) const +{ + return ToolSet::findInStringArray(_pageIds, value); +} + +const std::string& GController::getPageNameById(const std::string& value) const +{ + int i = ToolSet::findInStringArray(_pageIds, value); + if (i != -1) + return _pageNames[i]; + else + return STD_STRING_EMPTY; +} + +const std::string& GController::getPageId(int index) const +{ + return _pageIds[index]; +} + +void GController::setOppositePageId(const std::string& value) +{ + int i = ToolSet::findInStringArray(_pageIds, value); + if (i > 0) + setSelectedIndex(0); + else if (_pageIds.size() > 1) + setSelectedIndex(1); +} + +void GController::runActions() +{ + if (_actions.empty()) + return; + + for (auto& it : _actions) + it->run(this, getPreviousPageId(), getSelectedPageId()); +} + +void GController::setup(ByteBuffer* buffer) +{ + int beginPos = buffer->getPos(); + buffer->seek(beginPos, 0); + + name = buffer->readS(); + autoRadioGroupDepth = buffer->readBool(); + + buffer->seek(beginPos, 1); + + int cnt = buffer->readShort(); + _pageIds.resize(cnt); + _pageNames.resize(cnt); + for (int i = 0; i < cnt; i++) + { + _pageIds[i].assign(buffer->readS()); + _pageNames[i].assign(buffer->readS()); + } + + int homePageIndex = 0; + if (buffer->version >= 2) + { + int homePageType = buffer->readByte(); + switch (homePageType) + { + case 1: + homePageIndex = buffer->readShort(); + break; + + case 2: + homePageIndex = ToolSet::findInStringArray(_pageNames, UIPackage::getBranch()); + if (homePageIndex == -1) + homePageIndex = 0; + break; + + case 3: + homePageIndex = ToolSet::findInStringArray(_pageNames, UIPackage::getVar(buffer->readS())); + if (homePageIndex == -1) + homePageIndex = 0; + break; + } + } + + buffer->seek(beginPos, 2); + + cnt = buffer->readShort(); + if (cnt > 0) + { + for (int i = 0; i < cnt; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + ControllerAction* action = ControllerAction::createAction(buffer->readByte()); + action->setup(buffer); + _actions.push_back(action); + + buffer->setPos(nextPos); + } + } + + if (_parent != nullptr && _pageIds.size() > 0) + _selectedIndex = homePageIndex; + else + _selectedIndex = -1; +} + +NS_FGUI_END diff --git a/extensions/fairygui/Controller.h b/extensions/fairygui/Controller.h new file mode 100644 index 0000000000..14a39c6a69 --- /dev/null +++ b/extensions/fairygui/Controller.h @@ -0,0 +1,61 @@ +#ifndef __GCONTROLLER_H__ +#define __GCONTROLLER_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "event/UIEventDispatcher.h" + +NS_FGUI_BEGIN + +class GComponent; +class ControllerAction; +class ByteBuffer; + +class GController : public UIEventDispatcher +{ +public: + GController(); + virtual ~GController(); + + GComponent* getParent() const { return _parent; } + void setParent(GComponent* value) { _parent = value; } + + int getSelectedIndex() const { return _selectedIndex; } + void setSelectedIndex(int value, bool triggerEvent = true); + + const std::string& getSelectedPage() const; + void setSelectedPage(const std::string& value, bool triggerEvent = true); + + const std::string& getSelectedPageId() const; + void setSelectedPageId(const std::string& value, bool triggerEvent = true); + + int getPrevisousIndex() const { return _previousIndex; } + const std::string& getPreviousPage() const; + const std::string& getPreviousPageId() const; + + int getPageCount() const; + bool hasPage(const std::string& aName) const; + int getPageIndexById(const std::string& value) const; + const std::string& getPageNameById(const std::string& value) const; + const std::string& getPageId(int index) const; + void setOppositePageId(const std::string& value); + void runActions(); + + void setup(ByteBuffer* buffer); + + std::string name; + bool changing; + bool autoRadioGroupDepth; + +private: + GComponent* _parent; + int _selectedIndex; + int _previousIndex; + std::vector _pageIds; + std::vector _pageNames; + std::vector _actions; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/DragDropManager.cpp b/extensions/fairygui/DragDropManager.cpp new file mode 100644 index 0000000000..a178ef985b --- /dev/null +++ b/extensions/fairygui/DragDropManager.cpp @@ -0,0 +1,85 @@ +#include "DragDropManager.h" +#include "UIObjectFactory.h" +#include "GRoot.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +DragDropManager* DragDropManager::_inst = nullptr; + +DragDropManager::DragDropManager() : + _agent(nullptr) +{ + _agent = (GLoader*)UIObjectFactory::newObject(ObjectType::LOADER); + _agent->retain(); + _agent->setTouchable(false); + _agent->setDraggable(true); + _agent->setSize(100, 100); + _agent->setPivot(0.5f, 0.5f, true); + _agent->setAlign(TextHAlignment::CENTER); + _agent->setVerticalAlign(TextVAlignment::CENTER); + _agent->setSortingOrder(INT_MAX); + _agent->addEventListener(UIEventType::DragEnd, CC_CALLBACK_1(DragDropManager::onDragEnd, this)); +} + +DragDropManager::~DragDropManager() +{ + CC_SAFE_RELEASE(_agent); +} + +DragDropManager* DragDropManager::getInstance() +{ + if (_inst == nullptr) + _inst = new DragDropManager(); + + return _inst; +} + +void DragDropManager::startDrag(const std::string & icon, const Value& sourceData, int touchPointID) +{ + if (_agent->getParent() != nullptr) + return; + + _sourceData = sourceData; + _agent->setURL(icon); + UIRoot->addChild(_agent); + Vec2 pt = UIRoot->globalToLocal(UIRoot->getTouchPosition(touchPointID)); + _agent->setPosition(pt.x, pt.y); + _agent->startDrag(touchPointID); +} + +void DragDropManager::cancel() +{ + if (_agent->getParent() != nullptr) + { + _agent->stopDrag(); + UIRoot->removeChild(_agent); + _sourceData = Value::Null; + } +} + +void DragDropManager::onDragEnd(EventContext * context) +{ + if (_agent->getParent() == nullptr) //cancelled + return; + + UIRoot->removeChild(_agent); + + GObject* obj = UIRoot->getTouchTarget(); + while (obj != nullptr) + { + if (dynamic_cast(obj)) + { + if (obj->hasEventListener(UIEventType::Drop)) + { + //obj->requestFocus(); + obj->dispatchEvent(UIEventType::Drop, nullptr, _sourceData); + return; + } + } + + obj = obj->getParent(); + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/DragDropManager.h b/extensions/fairygui/DragDropManager.h new file mode 100644 index 0000000000..f59d3a0474 --- /dev/null +++ b/extensions/fairygui/DragDropManager.h @@ -0,0 +1,34 @@ +#ifndef __DRAGDROPMANAGER_H__ +#define __DRAGDROPMANAGER_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "GLoader.h" + +NS_FGUI_BEGIN + +class DragDropManager +{ +public: + DragDropManager(); + ~DragDropManager(); + + static DragDropManager* getInstance(); + + GLoader* getAgent() const { return _agent; } + bool isDragging() const { return _agent->getParent() != nullptr; } + void startDrag(const std::string& icon, const cocos2d::Value& sourceData = cocos2d::Value::Null, int touchPointID = -1); + void cancel(); + +private: + void onDragEnd(EventContext* context); + + static DragDropManager* _inst; + + GLoader* _agent; + cocos2d::Value _sourceData; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/FairyGUI.h b/extensions/fairygui/FairyGUI.h new file mode 100644 index 0000000000..3de0f9a6a1 --- /dev/null +++ b/extensions/fairygui/FairyGUI.h @@ -0,0 +1,32 @@ +#ifndef __FAIRYGUI_H__ +#define __FAIRYGUI_H__ + +#include "UIConfig.h" +#include "UIPackage.h" +#include "GImage.h" +#include "GMovieClip.h" +#include "GTextField.h" +#include "GRichTextField.h" +#include "GTextInput.h" +#include "GGraph.h" +#include "GLoader.h" +#include "GGroup.h" +#include "GComponent.h" +#include "GLabel.h" +#include "GButton.h" +#include "GComboBox.h" +#include "GProgressBar.h" +#include "GSlider.h" +#include "GScrollBar.h" +#include "GList.h" +#include "GTree.h" +#include "GRoot.h" +#include "Window.h" +#include "PopupMenu.h" +#include "UIObjectFactory.h" +#include "GObjectPool.h" +#include "DragDropManager.h" +#include "tween/GTween.h" +#include "FairyGUIMacros.h" + +#endif diff --git a/extensions/fairygui/FairyGUIMacros.h b/extensions/fairygui/FairyGUIMacros.h new file mode 100644 index 0000000000..a2f357471e --- /dev/null +++ b/extensions/fairygui/FairyGUIMacros.h @@ -0,0 +1,36 @@ +#ifndef __FAIRYGUIMACROS_H__ +#define __FAIRYGUIMACROS_H__ + +#include "cocos2d.h" + +#define NS_FGUI_BEGIN namespace fairygui { +#define NS_FGUI_END } +#define USING_NS_FGUI using namespace fairygui + +#define CALL_LATER_FUNC(__TYPE__,__FUNC__) \ +void __selector_##__FUNC__(float dt) \ +{\ + cocos2d::Director::getInstance()->getScheduler()->unschedule(CC_SCHEDULE_SELECTOR(__TYPE__::__selector_##__FUNC__), this);\ + __FUNC__(); \ +}\ +void __FUNC__() + +#define CALL_LATER(__TYPE__,__FUNC__,...) \ +if (!cocos2d::Director::getInstance()->getScheduler()->isScheduled(CC_SCHEDULE_SELECTOR(__TYPE__::__selector_##__FUNC__), this))\ + cocos2d::Director::getInstance()->getScheduler()->schedule(CC_SCHEDULE_SELECTOR(__TYPE__::__selector_##__FUNC__), this, (__VA_ARGS__+0), false) + +#define CALL_LATER_CANCEL(__TYPE__,__FUNC__) \ +cocos2d::Director::getInstance()->getScheduler()->unschedule(CC_SCHEDULE_SELECTOR(__TYPE__::__selector_##__FUNC__), this) + +#define CALL_PER_FRAME(__TYPE__,__FUNC__) \ +if (!cocos2d::Director::getInstance()->getScheduler()->isScheduled(CC_SCHEDULE_SELECTOR(__TYPE__::__FUNC__), this))\ + cocos2d::Director::getInstance()->getScheduler()->schedule(CC_SCHEDULE_SELECTOR(__TYPE__::__FUNC__), this, 0, false) + +#define CALL_PER_FRAME_CANCEL(__TYPE__,__FUNC__) \ +cocos2d::Director::getInstance()->getScheduler()->unschedule(CC_SCHEDULE_SELECTOR(__TYPE__::__FUNC__), this) + +#define UIRoot GRoot::getInstance() + +#include "FieldTypes.h" + +#endif \ No newline at end of file diff --git a/extensions/fairygui/FieldTypes.h b/extensions/fairygui/FieldTypes.h new file mode 100644 index 0000000000..96ecc5f11d --- /dev/null +++ b/extensions/fairygui/FieldTypes.h @@ -0,0 +1,201 @@ +#ifndef __FIELDTYPES_H__ +#define __FIELDTYPES_H__ + +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +enum class PackageItemType +{ + IMAGE, + MOVIECLIP, + SOUND, + COMPONENT, + ATLAS, + FONT, + SWF, + MISC, + UNKNOWN, + SPINE, + DRAGONBONES +}; + +enum class ObjectType +{ + IMAGE, + MOVIECLIP, + SWF, + GRAPH, + LOADER, + GROUP, + TEXT, + RICHTEXT, + INPUTTEXT, + COMPONENT, + LIST, + LABEL, + BUTTON, + COMBOBOX, + PROGRESSBAR, + SLIDER, + SCROLLBAR, + TREE, + LOADER3D +}; + +enum class ButtonMode +{ + COMMON, + CHECK, + RADIO +}; + +enum class ChildrenRenderOrder +{ + ASCENT, + DESCENT, + ARCH, +}; + +enum class OverflowType +{ + VISIBLE, + HIDDEN, + SCROLL +}; + +enum class ScrollType +{ + HORIZONTAL, + VERTICAL, + BOTH +}; + +enum ScrollBarDisplayType +{ + DEFAULT, + VISIBLE, + AUTO, + HIDDEN +}; + +enum class LoaderFillType +{ + NONE, + SCALE, + SCALE_MATCH_HEIGHT, + SCALE_MATCH_WIDTH, + SCALE_FREE, + SCALE_NO_BORDER +}; + +enum class ProgressTitleType +{ + PERCENT, + VALUE_MAX, + VALUE, + MAX +}; + +enum class ListLayoutType +{ + SINGLE_COLUMN, + SINGLE_ROW, + FLOW_HORIZONTAL, + FLOW_VERTICAL, + PAGINATION +}; + +enum class ListSelectionMode +{ + SINGLE, + MULTIPLE, + MULTIPLE_SINGLECLICK, + NONE +}; + +enum class GroupLayoutType +{ + NONE, + HORIZONTAL, + VERTICAL +}; + +enum class PopupDirection +{ + AUTO, + UP, + DOWN +}; + +enum class AutoSizeType +{ + NONE, + BOTH, + HEIGHT, + SHRINK +}; + +enum class FlipType +{ + NONE, + HORIZONTAL, + VERTICAL, + BOTH +}; + +enum class TransitionActionType +{ + XY, + Size, + Scale, + Pivot, + Alpha, + Rotation, + Color, + Animation, + Visible, + Sound, + Transition, + Shake, + ColorFilter, + Skew, + Text, + Icon, + Unknown +}; + +enum class FillMethod +{ + None, + Horizontal, + Vertical, + Radial90, + Radial180, + Radial360, +}; + +enum class FillOrigin +{ + Top, + Bottom, + Left, + Right +}; + +enum class ObjectPropID { + Text, + Icon, + Color, + OutlineColor, + Playing, + Frame, + DeltaTime, + TimeScale, + FontSize, + Selected +}; + +NS_FGUI_END + +#endif \ No newline at end of file diff --git a/extensions/fairygui/GButton.cpp b/extensions/fairygui/GButton.cpp new file mode 100644 index 0000000000..2205c92ec5 --- /dev/null +++ b/extensions/fairygui/GButton.cpp @@ -0,0 +1,453 @@ +#include "GButton.h" +#include "GLabel.h" +#include "GRoot.h" +#include "GTextField.h" +#include "PackageItem.h" +#include "UIConfig.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +const std::string GButton::UP = "up"; +const std::string GButton::DOWN = "down"; +const std::string GButton::OVER = "over"; +const std::string GButton::SELECTED_OVER = "selectedOver"; +const std::string GButton::DISABLED = "disabled"; +const std::string GButton::SELECTED_DISABLED = "selectedDisabled"; + +GButton::GButton() : _mode(ButtonMode::COMMON), + _titleObject(nullptr), + _iconObject(nullptr), + _buttonController(nullptr), + _relatedController(nullptr), + _selected(false), + _over(false), + _down(false), + _downEffect(0), + _downScaled(false), + _downEffectValue(0.8f), + _changeStateOnClick(true) +{ + _sound = UIConfig::buttonSound; + _soundVolumeScale = UIConfig::buttonSoundVolumeScale; +} + +GButton::~GButton() +{ +} + +void GButton::setTitle(const std::string& value) +{ + _title = value; + if (_titleObject != nullptr) + _titleObject->setText((_selected && _selectedTitle.length() > 0) ? _selectedTitle : _title); + updateGear(6); +} + +void GButton::setIcon(const std::string& value) +{ + _icon = value; + if (_iconObject != nullptr) + _iconObject->setIcon((_selected && _selectedIcon.length() > 0) ? _selectedIcon : _icon); + updateGear(7); +} + +void GButton::setSelectedTitle(const std::string& value) +{ + _selectedTitle = value; + if (_titleObject != nullptr) + _titleObject->setText((_selected && _selectedTitle.length() > 0) ? _selectedTitle : _title); +} + +void GButton::setSelectedIcon(const std::string& value) +{ + _selectedIcon = value; + if (_iconObject != nullptr) + _iconObject->setIcon((_selected && _selectedIcon.length() > 0) ? _selectedIcon : _icon); +} + +cocos2d::Color3B GButton::getTitleColor() const +{ + GTextField* tf = getTextField(); + if (tf) + return tf->getColor(); + else + return Color3B::BLACK; +} + +void GButton::setTitleColor(const cocos2d::Color3B& value) +{ + GTextField* tf = getTextField(); + if (tf) + tf->setColor(value); +} + +int GButton::getTitleFontSize() const +{ + GTextField* tf = getTextField(); + if (tf) + return tf->getFontSize(); + else + return 0; +} + +void GButton::setTitleFontSize(int value) +{ + GTextField* tf = getTextField(); + if (tf) + tf->setFontSize(value); +} + +void GButton::setSelected(bool value) +{ + if (_mode == ButtonMode::COMMON) + return; + + if (_selected != value) + { + _selected = value; + setCurrentState(); + if (!_selectedTitle.empty() && _titleObject != nullptr) + _titleObject->setText(_selected ? _selectedTitle : _title); + if (!_selectedIcon.empty()) + { + const std::string& str = _selected ? _selectedIcon : _icon; + if (_iconObject != nullptr) + _iconObject->setIcon(str); + } + if (_relatedController != nullptr && getParent() != nullptr && !getParent()->_buildingDisplayList) + { + if (_selected) + { + _relatedController->setSelectedPageId(_relatedPageId); + if (_relatedController->autoRadioGroupDepth) + getParent()->adjustRadioGroupDepth(this, _relatedController); + } + else if (_mode == ButtonMode::CHECK && _relatedController->getSelectedPageId().compare(_relatedPageId) == 0) + _relatedController->setOppositePageId(_relatedPageId); + } + } +} + +void GButton::setRelatedController(GController* c) +{ + _relatedController = c; +} + +void GButton::setState(const std::string& value) +{ + if (_buttonController != nullptr) + _buttonController->setSelectedPage(value); + + if (_downEffect == 1) + { + int cnt = this->numChildren(); + if (value == DOWN || value == SELECTED_OVER || value == SELECTED_DISABLED) + { + int c = _downEffectValue * 255; + Value color = Value((c << 16) + (c << 8) + c); + for (int i = 0; i < cnt; i++) + { + GObject* obj = this->getChildAt(i); + if (dynamic_cast(obj) == nullptr) + obj->setProp(ObjectPropID::Color, color); + } + } + else + { + Value color = Value(0xFFFFFF); + for (int i = 0; i < cnt; i++) + { + GObject* obj = this->getChildAt(i); + if (dynamic_cast(obj) == nullptr) + obj->setProp(ObjectPropID::Color, color); + } + } + } + else if (_downEffect == 2) + { + if (value == DOWN || value == SELECTED_OVER || value == SELECTED_DISABLED) + { + if (!_downScaled) + { + _downScaled = true; + setScale(getScaleX() * _downEffectValue, getScaleY() * _downEffectValue); + } + } + else + { + if (_downScaled) + { + _downScaled = false; + setScale(getScaleX() / _downEffectValue, getScaleY() / _downEffectValue); + } + } + } +} + +void GButton::setCurrentState() +{ + if (isGrayed() && _buttonController != nullptr && _buttonController->hasPage(DISABLED)) + { + if (_selected) + setState(SELECTED_DISABLED); + else + setState(DISABLED); + } + else + { + if (_selected) + setState(_over ? SELECTED_OVER : DOWN); + else + setState(_over ? OVER : UP); + } +} + +GTextField* GButton::getTextField() const +{ + if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject); + else if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject)->getTextField(); + else if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject)->getTextField(); + else + return nullptr; +} + +cocos2d::Value GButton::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getTitleColor())); + case ObjectPropID::OutlineColor: + { + GTextField* tf = getTextField(); + if (tf != nullptr) + return Value(ToolSet::colorToInt(tf->getOutlineColor())); + else + return Value::Null; + } + case ObjectPropID::FontSize: + return Value(getTitleFontSize()); + case ObjectPropID::Selected: + return Value(isSelected()); + default: + return GComponent::getProp(propId); + } +} + +void GButton::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setTitleColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + case ObjectPropID::OutlineColor: + { + GTextField* tf = getTextField(); + if (tf != nullptr) + tf->setOutlineColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + } + case ObjectPropID::FontSize: + setTitleFontSize(value.asInt()); + break; + case ObjectPropID::Selected: + setSelected(value.asBool()); + break; + default: + GComponent::setProp(propId, value); + break; + } +} + +void GButton::constructExtension(ByteBuffer* buffer) +{ + buffer->seek(0, 6); + + _mode = (ButtonMode)buffer->readByte(); + buffer->readS(_sound); + _soundVolumeScale = buffer->readFloat(); + _downEffect = buffer->readByte(); + _downEffectValue = buffer->readFloat(); + if (_downEffect == 2) + setPivot(0.5f, 0.5f, isPivotAsAnchor()); + + _buttonController = getController("button"); + _titleObject = getChild("title"); + _iconObject = getChild("icon"); + if (_titleObject != nullptr) + _title = _titleObject->getText(); + if (_iconObject != nullptr) + _icon = _iconObject->getIcon(); + + if (_mode == ButtonMode::COMMON) + setState(UP); + + addEventListener(UIEventType::RollOver, CC_CALLBACK_1(GButton::onRollOver, this)); + addEventListener(UIEventType::RollOut, CC_CALLBACK_1(GButton::onRollOut, this)); + addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GButton::onTouchBegin, this)); + addEventListener(UIEventType::TouchEnd, CC_CALLBACK_1(GButton::onTouchEnd, this)); + addEventListener(UIEventType::Click, CC_CALLBACK_1(GButton::onClick, this)); + addEventListener(UIEventType::Exit, CC_CALLBACK_1(GButton::onExit, this)); +} + +void GButton::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GComponent::setup_afterAdd(buffer, beginPos); + + if (!buffer->seek(beginPos, 6)) + return; + + if ((ObjectType)buffer->readByte() != _packageItem->objectType) + return; + + const std::string* str; + + if ((str = buffer->readSP())) + setTitle(*str); + if ((str = buffer->readSP())) + setSelectedTitle(*str); + if ((str = buffer->readSP())) + setIcon(*str); + if ((str = buffer->readSP())) + setSelectedIcon(*str); + if (buffer->readBool()) + setTitleColor((Color3B)buffer->readColor()); + int iv = buffer->readInt(); + if (iv != 0) + setTitleFontSize(iv); + iv = buffer->readShort(); + if (iv >= 0) + _relatedController = _parent->getControllerAt(iv); + _relatedPageId = buffer->readS(); + + buffer->readS(_sound); + if (buffer->readBool()) + _soundVolumeScale = buffer->readFloat(); + + setSelected(buffer->readBool()); +} + +void GButton::handleControllerChanged(GController* c) +{ + GObject::handleControllerChanged(c); + + if (_relatedController == c) + setSelected(_relatedPageId.compare(c->getSelectedPageId()) == 0); +} + +void GButton::onRollOver(EventContext* context) +{ + if (_buttonController == nullptr || !_buttonController->hasPage(OVER)) + return; + + _over = true; + if (_down) + return; + + if (isGrayed() && _buttonController->hasPage(DISABLED)) + return; + + setState(_selected ? SELECTED_OVER : OVER); +} + +void GButton::onRollOut(EventContext* context) +{ + if (_buttonController == nullptr || !_buttonController->hasPage(OVER)) + return; + + _over = false; + if (_down) + return; + + if (isGrayed() && _buttonController->hasPage(DISABLED)) + return; + + setState(_selected ? DOWN : UP); +} + +void GButton::onTouchBegin(EventContext* context) +{ + if (context->getInput()->getButton() != EventMouse::MouseButton::BUTTON_LEFT) + return; + + _down = true; + context->captureTouch(); + + if (_mode == ButtonMode::COMMON) + { + if (isGrayed() && _buttonController != nullptr && _buttonController->hasPage(DISABLED)) + setState(SELECTED_DISABLED); + else + setState(DOWN); + } +} + +void GButton::onTouchEnd(EventContext* context) +{ + if (context->getInput()->getButton() != EventMouse::MouseButton::BUTTON_LEFT) + return; + + if (_down) + { + _down = false; + if (_mode == ButtonMode::COMMON) + { + if (isGrayed() && _buttonController != nullptr && _buttonController->hasPage(DISABLED)) + setState(DISABLED); + else if (_over) + setState(OVER); + else + setState(UP); + } + else + { + if (!_over && _buttonController != nullptr && (_buttonController->getSelectedPage() == OVER || _buttonController->getSelectedPage() == SELECTED_OVER)) + { + setCurrentState(); + } + } + } +} + +void GButton::onClick(EventContext* context) +{ + if (!_sound.empty()) + UIRoot->playSound(_sound, _soundVolumeScale); + + if (_mode == ButtonMode::CHECK) + { + if (_changeStateOnClick) + { + setSelected(!_selected); + dispatchEvent(UIEventType::Changed); + } + } + else if (_mode == ButtonMode::RADIO) + { + if (_changeStateOnClick && !_selected) + { + setSelected(true); + dispatchEvent(UIEventType::Changed); + } + } + else + { + if (_relatedController != nullptr) + _relatedController->setSelectedPageId(_relatedPageId); + } +} + +void GButton::onExit(EventContext* context) +{ + if (_over) + onRollOut(context); +} + +NS_FGUI_END diff --git a/extensions/fairygui/GButton.h b/extensions/fairygui/GButton.h new file mode 100644 index 0000000000..3904d91cfd --- /dev/null +++ b/extensions/fairygui/GButton.h @@ -0,0 +1,101 @@ +#ifndef __GBUTTON_H +#define __GBUTTON_H + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GComponent.h" + +NS_FGUI_BEGIN + +class GTextField; + +class GButton : public GComponent +{ +public: + static const std::string UP; + static const std::string DOWN; + static const std::string OVER; + static const std::string SELECTED_OVER; + static const std::string DISABLED; + static const std::string SELECTED_DISABLED; + + GButton(); + virtual ~GButton(); + + CREATE_FUNC(GButton); + + const std::string& getTitle() { return _title; } + void setTitle(const std::string& value); + + virtual const std::string& getText() const override { return _title; } + virtual void setText(const std::string& value) override { setTitle(value); } + + virtual const std::string& getIcon() const override { return _icon; } + virtual void setIcon(const std::string& value) override; + + const std::string& getSelectedTitle() const { return _selectedTitle; } + void setSelectedTitle(const std::string& value); + + const std::string& getSelectedIcon() const { return _selectedIcon; } + void setSelectedIcon(const std::string& value); + + cocos2d::Color3B getTitleColor() const; + void setTitleColor(const cocos2d::Color3B& value); + + int getTitleFontSize() const; + void setTitleFontSize(int value); + + bool isSelected() const { return _selected; } + void setSelected(bool value); + + GController* getRelatedController() const { return _relatedController; } + void setRelatedController(GController* c); + + bool isChangeStateOnClick() { return _changeStateOnClick; } + void setChangeStateOnClick(bool value) { _changeStateOnClick = value; } + + GTextField* getTextField() const; + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void constructExtension(ByteBuffer* buffer) override; + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + virtual void handleControllerChanged(GController* c) override; + + void setState(const std::string& value); + void setCurrentState(); + +private: + void onRollOver(EventContext* context); + void onRollOut(EventContext* context); + void onTouchBegin(EventContext* context); + void onTouchEnd(EventContext* context); + void onClick(EventContext* context); + void onExit(EventContext* context); + + ButtonMode _mode; + GObject* _titleObject; + GObject* _iconObject; + GController* _buttonController; + GController* _relatedController; + std::string _relatedPageId; + std::string _title; + std::string _selectedTitle; + std::string _icon; + std::string _selectedIcon; + std::string _sound; + float _soundVolumeScale; + bool _selected; + bool _over; + bool _down; + int _downEffect; + bool _downScaled; + float _downEffectValue; + bool _changeStateOnClick; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GComboBox.cpp b/extensions/fairygui/GComboBox.cpp new file mode 100644 index 0000000000..51017a9553 --- /dev/null +++ b/extensions/fairygui/GComboBox.cpp @@ -0,0 +1,452 @@ +#include "GComboBox.h" +#include "GRoot.h" +#include "PackageItem.h" +#include "UIConfig.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GComboBox::GComboBox() + : _dropdown(nullptr), + _titleObject(nullptr), + _iconObject(nullptr), + _list(nullptr), + _selectionController(nullptr), + _itemsUpdated(true), + _selectedIndex(-1), + popupDirection(PopupDirection::AUTO) +{ + visibleItemCount = UIConfig::defaultComboBoxVisibleItemCount; +} + +GComboBox::~GComboBox() +{ + CC_SAFE_RELEASE(_dropdown); +} + +const std::string& GComboBox::getTitle() const +{ + if (_titleObject != nullptr) + return _titleObject->getText(); + else + return STD_STRING_EMPTY; +} + +void GComboBox::setTitle(const std::string& value) +{ + if (_titleObject != nullptr) + _titleObject->setText(value); + updateGear(6); +} + +const cocos2d::Color3B GComboBox::getTitleColor() const +{ + GTextField* tf = getTextField(); + if (tf) + return tf->getColor(); + else + return Color3B::BLACK; +} + +void GComboBox::setTitleColor(const cocos2d::Color3B& value) +{ + GTextField* tf = getTextField(); + if (tf) + tf->setColor(value); +} + +int GComboBox::getTitleFontSize() const +{ + GTextField* tf = getTextField(); + if (tf) + return tf->getFontSize(); + else + return 0; +} + +void GComboBox::setTitleFontSize(int value) +{ + GTextField* tf = getTextField(); + if (tf) + tf->setFontSize(value); +} + +const std::string& GComboBox::getIcon() const +{ + if (_iconObject != nullptr) + return _iconObject->getIcon(); + else + return STD_STRING_EMPTY; +} + +void GComboBox::setIcon(const std::string& value) +{ + if (_iconObject != nullptr) + _iconObject->setIcon(value); + updateGear(7); +} + +const std::string& GComboBox::getValue() const +{ + if (_selectedIndex >= 0 && _selectedIndex < (int)_values.size()) + return _values[_selectedIndex]; + else + return STD_STRING_EMPTY; +} + +void GComboBox::setValue(const std::string& value) +{ + setSelectedIndex(ToolSet::findInStringArray(_values, value)); +} + +void GComboBox::setSelectedIndex(int value) +{ + if (_selectedIndex == value) + return; + + _selectedIndex = value; + if (_selectedIndex >= 0 && _selectedIndex < (int)_items.size()) + { + setText(_items[_selectedIndex]); + if (!_icons.empty() && _selectedIndex != -1 && _selectedIndex < (int)_icons.size()) + setIcon(_icons[_selectedIndex]); + } + else + { + setTitle(STD_STRING_EMPTY); + if (!_icons.empty()) + setIcon(STD_STRING_EMPTY); + } + + updateSelectionController(); +} + +void GComboBox::refresh() +{ + if (!_items.empty()) + { + if (_selectedIndex >= (int)_items.size()) + _selectedIndex = (int)_items.size() - 1; + else if (_selectedIndex == -1) + _selectedIndex = 0; + setTitle(_items[_selectedIndex]); + } + else + { + setTitle(STD_STRING_EMPTY); + _selectedIndex = -1; + } + + if (!_icons.empty()) + { + if (_selectedIndex != -1 && _selectedIndex < (int)_icons.size()) + setIcon(_icons[_selectedIndex]); + else + setIcon(STD_STRING_EMPTY); + } + + _itemsUpdated = true; +} + +void GComboBox::setState(const std::string& value) +{ + if (_buttonController != nullptr) + _buttonController->setSelectedPage(value); +} + +void GComboBox::setCurrentState() +{ + if (isGrayed() && _buttonController != nullptr && _buttonController->hasPage(GButton::DISABLED)) + setState(GButton::DISABLED); + else if (_dropdown != nullptr && _dropdown->getParent() != nullptr) + setState(GButton::DOWN); + else + setState(_over ? GButton::OVER : GButton::UP); +} + +void GComboBox::updateSelectionController() +{ + if (_selectionController != nullptr && !_selectionController->changing && _selectedIndex < _selectionController->getPageCount()) + { + GController* c = _selectionController; + _selectionController = nullptr; + c->setSelectedIndex(_selectedIndex); + _selectionController = c; + } +} + +void GComboBox::updateDropdownList() +{ + if (_itemsUpdated) + { + _itemsUpdated = false; + renderDropdownList(); + _list->resizeToFit(visibleItemCount); + } +} + +void GComboBox::showDropdown() +{ + updateDropdownList(); + if (_list->getSelectionMode() == ListSelectionMode::SINGLE) + _list->setSelectedIndex(-1); + _dropdown->setWidth(_size.width); + _list->ensureBoundsCorrect(); + + UIRoot->togglePopup(_dropdown, this, popupDirection); + if (_dropdown->getParent() != nullptr) + setState(GButton::DOWN); +} + +void GComboBox::renderDropdownList() +{ + _list->removeChildrenToPool(); + size_t cnt = _items.size(); + for (size_t i = 0; i < cnt; i++) + { + GObject* item = _list->addItemFromPool(); + item->setText(_items[i]); + item->setIcon((!_icons.empty() && i < _icons.size()) ? _icons[i] : STD_STRING_EMPTY); + item->name = i < _values.size() ? _values[i] : STD_STRING_EMPTY; + } +} + +void GComboBox::handleControllerChanged(GController* c) +{ + GComponent::handleControllerChanged(c); + + if (_selectionController == c) + setSelectedIndex(c->getSelectedIndex()); +} + +void GComboBox::handleGrayedChanged() +{ + if (_buttonController != nullptr && _buttonController->hasPage(GButton::DISABLED)) + { + if (isGrayed()) + setState(GButton::DISABLED); + else + setState(GButton::UP); + } + else + GComponent::handleGrayedChanged(); +} + +GTextField* GComboBox::getTextField() const +{ + if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject); + else if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject)->getTextField(); + else if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject)->getTextField(); + else + return nullptr; +} + +cocos2d::Value GComboBox::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getTitleColor())); + case ObjectPropID::OutlineColor: + { + GTextField* tf = getTextField(); + if (tf != nullptr) + return Value(ToolSet::colorToInt(tf->getOutlineColor())); + else + return Value::Null; + } + case ObjectPropID::FontSize: + return Value(getTitleFontSize()); + default: + return GComponent::getProp(propId); + } +} + +void GComboBox::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setTitleColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + case ObjectPropID::OutlineColor: + { + GTextField* tf = getTextField(); + if (tf != nullptr) + tf->setOutlineColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + } + case ObjectPropID::FontSize: + setTitleFontSize(value.asInt()); + break; + default: + GComponent::setProp(propId, value); + break; + } +} + +void GComboBox::constructExtension(ByteBuffer* buffer) +{ + buffer->seek(0, 6); + + _buttonController = getController("button"); + _titleObject = getChild("title"); + _iconObject = getChild("icon"); + + const std::string& dropdown = buffer->readS(); + if (!dropdown.empty()) + { + _dropdown = dynamic_cast(UIPackage::createObjectFromURL(dropdown)); + CCASSERT(_dropdown != nullptr, "FairyGUI: should be a component."); + + _dropdown->retain(); + + _list = dynamic_cast(_dropdown->getChild("list")); + CCASSERT(_list != nullptr, "FairyGUI: should container a list component named list."); + + _list->addEventListener(UIEventType::ClickItem, CC_CALLBACK_1(GComboBox::onClickItem, this)); + + _list->addRelation(_dropdown, RelationType::Width); + _list->removeRelation(_dropdown, RelationType::Height); + + _dropdown->addRelation(_list, RelationType::Height); + _dropdown->removeRelation(_list, RelationType::Width); + + _dropdown->addEventListener(UIEventType::Exit, CC_CALLBACK_1(GComboBox::onPopupWinClosed, this)); + } + + addEventListener(UIEventType::RollOver, CC_CALLBACK_1(GComboBox::onRollover, this)); + addEventListener(UIEventType::RollOut, CC_CALLBACK_1(GComboBox::onRollout, this)); + addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GComboBox::onTouchBegin, this)); + addEventListener(UIEventType::TouchEnd, CC_CALLBACK_1(GComboBox::onTouchEnd, this)); +} + +void GComboBox::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GComponent::setup_afterAdd(buffer, beginPos); + + if (!buffer->seek(beginPos, 6)) + return; + + if ((ObjectType)buffer->readByte() != _packageItem->objectType) + return; + + const std::string* str; + bool hasIcon = false; + int itemCount = buffer->readShort(); + for (int i = 0; i < itemCount; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + _items.push_back(buffer->readS()); + _values.push_back(buffer->readS()); + if ((str = buffer->readSP())) + { + if (!hasIcon) + { + for (int i = 0; i < (int)_items.size() - 1; i++) + _icons.push_back(STD_STRING_EMPTY); + } + _icons.push_back(*str); + } + + buffer->setPos(nextPos); + } + + if ((str = buffer->readSP())) + { + setTitle(*str); + _selectedIndex = ToolSet::findInStringArray(_items, *str); + } + else if (!_items.empty()) + { + _selectedIndex = 0; + setTitle(_items[0]); + } + else + _selectedIndex = -1; + + if ((str = buffer->readSP())) + setIcon(*str); + + if (buffer->readBool()) + setTitleColor((Color3B)buffer->readColor()); + int iv = buffer->readInt(); + if (iv > 0) + visibleItemCount = iv; + popupDirection = (PopupDirection)buffer->readByte(); + + iv = buffer->readShort(); + if (iv >= 0) + _selectionController = _parent->getControllerAt(iv); +} + +void GComboBox::onClickItem(EventContext* context) +{ + if (dynamic_cast(_dropdown->getParent())) + ((GRoot*)_dropdown->getParent())->hidePopup(_dropdown); + _selectedIndex = INT_MIN; + setSelectedIndex(_list->getChildIndex((GObject*)context->getData())); + + dispatchEvent(UIEventType::Changed); +} + +void GComboBox::onRollover(EventContext* context) +{ + _over = true; + if (_down || (_dropdown != nullptr && _dropdown->getParent() != nullptr)) + return; + + setCurrentState(); +} + +void GComboBox::onRollout(EventContext* context) +{ + _over = false; + if (_down || (_dropdown != nullptr && _dropdown->getParent() != nullptr)) + return; + + setCurrentState(); +} + +void GComboBox::onTouchBegin(EventContext* context) +{ + if (context->getInput()->getButton() != EventMouse::MouseButton::BUTTON_LEFT) + return; + + if (dynamic_cast(context->getInput()->getTarget())) + return; + + _down = true; + + if (_dropdown != nullptr) + showDropdown(); + + context->captureTouch(); +} + +void GComboBox::onTouchEnd(EventContext* context) +{ + if (context->getInput()->getButton() != EventMouse::MouseButton::BUTTON_LEFT) + return; + + if (_down) + { + _down = false; + if (_dropdown != nullptr && _dropdown->getParent() != nullptr) + setCurrentState(); + } +} + +void GComboBox::onPopupWinClosed(EventContext* context) +{ + setCurrentState(); +} + +NS_FGUI_END diff --git a/extensions/fairygui/GComboBox.h b/extensions/fairygui/GComboBox.h new file mode 100644 index 0000000000..c0f1613319 --- /dev/null +++ b/extensions/fairygui/GComboBox.h @@ -0,0 +1,102 @@ +#ifndef __GCOMBOBOX_H__ +#define __GCOMBOBOX_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GComponent.h" +#include "GList.h" + +NS_FGUI_BEGIN + +class GTextField; + +class GComboBox : public GComponent +{ +public: + GComboBox(); + virtual ~GComboBox(); + + CREATE_FUNC(GComboBox); + + const std::string& getTitle() const; + void setTitle(const std::string& value); + + virtual const std::string& getText() const override { return getTitle(); } + virtual void setText(const std::string& value) override { setTitle(value); } + + const cocos2d::Color3B getTitleColor() const; + void setTitleColor(const cocos2d::Color3B& value); + + int getTitleFontSize() const; + void setTitleFontSize(int value); + + virtual const std::string& getIcon() const override; + virtual void setIcon(const std::string& value) override; + + const std::string& getValue() const; + void setValue(const std::string& value); + + int getSelectedIndex() const { return _selectedIndex; } + void setSelectedIndex(int value); + + GController* getSelectionController() const { return _selectionController; } + void setSelectionController(GController* value) { _selectionController = value; } + + std::vector& getItems() { return _items; } + std::vector& getIcons() { return _icons; } + std::vector& getValues() { return _values; } + + GObject* getDropdown() const { return _dropdown; } + + void refresh(); + + int visibleItemCount; + PopupDirection popupDirection; + + GTextField* getTextField() const; + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void constructExtension(ByteBuffer* buffer) override; + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + virtual void handleControllerChanged(GController* c) override; + virtual void handleGrayedChanged() override; + + void setState(const std::string& value); + void setCurrentState(); + void updateSelectionController(); + void updateDropdownList(); + void showDropdown(); + void renderDropdownList(); + + GComponent* _dropdown; + GObject* _titleObject; + GObject* _iconObject; + GList* _list; + GController* _selectionController; + + std::vector _items; + std::vector _icons; + std::vector _values; + +private: + + void onClickItem(EventContext* context); + void onRollover(EventContext* context); + void onRollout(EventContext* context); + void onTouchBegin(EventContext* context); + void onTouchEnd(EventContext* context); + void onPopupWinClosed(EventContext* context); + + bool _itemsUpdated; + int _selectedIndex; + GController* _buttonController; + bool _down; + bool _over; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GComponent.cpp b/extensions/fairygui/GComponent.cpp new file mode 100644 index 0000000000..6e42e01a14 --- /dev/null +++ b/extensions/fairygui/GComponent.cpp @@ -0,0 +1,1385 @@ +#include "GComponent.h" +#include "GButton.h" +#include "GGroup.h" +#include "Relations.h" +#include "TranslationHelper.h" +#include "UIObjectFactory.h" +#include "UIPackage.h" +#include "display/FUIContainer.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +using namespace std; + +GComponent::GComponent() : _container(nullptr), + _scrollPane(nullptr), + _childrenRenderOrder(ChildrenRenderOrder::ASCENT), + _apexIndex(0), + _boundsChanged(false), + _trackBounds(false), + _opaque(false), + _sortingChildCount(0), + _applyingController(nullptr), + _buildingDisplayList(false), + _maskOwner(nullptr), + _hitArea(nullptr) +{ +} + +GComponent::~GComponent() +{ + for (auto& child : _children) + child->_parent = nullptr; + _children.clear(); + _controllers.clear(); + _transitions.clear(); + CC_SAFE_RELEASE(_maskOwner); + CC_SAFE_RELEASE(_container); + CC_SAFE_RELEASE(_scrollPane); + CC_SAFE_DELETE(_hitArea); + CALL_LATER_CANCEL(GComponent, doUpdateBounds); + CALL_LATER_CANCEL(GComponent, buildNativeDisplayList); +} + +void GComponent::handleInit() +{ + FUIContainer* c = FUIContainer::create(); + c->retain(); + c->gOwner = this; + + _displayObject = c; + + _container = FUIInnerContainer::create(); + _container->retain(); + _container->setCascadeOpacityEnabled(true); + _displayObject->addChild(_container); +} + +GObject* GComponent::addChild(GObject* child) +{ + addChildAt(child, (int)_children.size()); + return child; +} + +GObject* GComponent::addChildAt(GObject* child, int index) +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + if (child->_parent == this) + { + setChildIndex(child, index); + } + else + { + child->retain(); + child->removeFromParent(); + child->_parent = this; + + int cnt = (int)_children.size(); + if (child->_sortingOrder != 0) + { + _sortingChildCount++; + index = getInsertPosForSortingChild(child); + } + else if (_sortingChildCount > 0) + { + if (index > (cnt - _sortingChildCount)) + index = cnt - _sortingChildCount; + } + + if (index == cnt) + _children.pushBack(child); + else + _children.insert(index, child); + + child->release(); + + childStateChanged(child); + setBoundsChangedFlag(); + } + return child; +} + +int GComponent::getInsertPosForSortingChild(GObject* target) +{ + size_t cnt = _children.size(); + size_t i; + for (i = 0; i < cnt; i++) + { + GObject* child = _children.at(i); + if (child == target) + continue; + + if (target->_sortingOrder < child->_sortingOrder) + break; + } + return (int)i; +} + +void GComponent::removeChild(GObject* child) +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + int childIndex = (int)_children.getIndex(child); + if (childIndex != -1) + removeChildAt(childIndex); +} + +void GComponent::removeChildAt(int index) +{ + CCASSERT(index >= 0 && index < _children.size(), "Invalid child index"); + + GObject* child = _children.at(index); + + child->_parent = nullptr; + + if (child->_sortingOrder != 0) + _sortingChildCount--; + + child->setGroup(nullptr); + if (child->_displayObject->getParent() != nullptr) + { + _container->removeChild(child->_displayObject, false); + if (_childrenRenderOrder == ChildrenRenderOrder::ARCH) + CALL_LATER(GComponent, buildNativeDisplayList); + } + + _children.erase(index); + setBoundsChangedFlag(); +} + +void GComponent::removeChildren(int beginIndex, int endIndex) +{ + if (endIndex < 0 || endIndex >= _children.size()) + endIndex = (int)_children.size() - 1; + + for (int i = beginIndex; i <= endIndex; ++i) + removeChildAt(beginIndex); +} + +GObject* GComponent::getChildAt(int index) const +{ + CCASSERT(index >= 0 && index < _children.size(), "Invalid child index"); + + return _children.at(index); +} + +GObject* GComponent::getChild(const std::string& name) const +{ + for (const auto& child : _children) + { + if (child->name.compare(name) == 0) + return child; + } + + return nullptr; +} + +GObject* GComponent::getChildByPath(const std::string& path) const +{ + const GComponent* gcom = this; + GObject* obj = nullptr; + + FastSplitter fs; + fs.start(path.data(), path.length(), '.'); + std::string str; + while (fs.next()) + { + if (gcom == nullptr) + { + gcom = dynamic_cast(obj); + if (gcom == nullptr) + { + obj = nullptr; + break; + } + } + + str.append(fs.getText(), fs.getTextLength()); + obj = gcom->getChild(std::string(fs.getText(), fs.getTextLength())); + if (!obj) + break; + + gcom = nullptr; + } + + return obj; +} + +GObject* GComponent::getChildInGroup(const GGroup* group, const std::string& name) const +{ + CCASSERT(group != nullptr, "Argument must be non-nil"); + + for (const auto& child : _children) + { + if (child->_group == group && child->name.compare(name) == 0) + return child; + } + + return nullptr; +} + +GObject* GComponent::getChildById(const std::string& id) const +{ + for (const auto& child : _children) + { + if (child->id.compare(id) == 0) + return child; + } + + return nullptr; +} + +int GComponent::getChildIndex(const GObject* child) const +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + return (int)_children.getIndex((GObject*)child); +} + +void GComponent::setChildIndex(GObject* child, int index) +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + int oldIndex = (int)_children.getIndex(child); + CCASSERT(oldIndex != -1, "Not a child of this container"); + + if (child->_sortingOrder != 0) //no effect + return; + + int cnt = (int)_children.size(); + if (_sortingChildCount > 0) + { + if (index > (cnt - _sortingChildCount - 1)) + index = cnt - _sortingChildCount - 1; + } + + moveChild(child, oldIndex, index); +} + +int GComponent::setChildIndexBefore(GObject* child, int index) +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + int oldIndex = (int)_children.getIndex(child); + CCASSERT(oldIndex != -1, "Not a child of this container"); + + if (child->_sortingOrder != 0) //no effect + return oldIndex; + + int cnt = (int)_children.size(); + if (_sortingChildCount > 0) + { + if (index > (cnt - _sortingChildCount - 1)) + index = cnt - _sortingChildCount - 1; + } + + if (oldIndex < index) + return moveChild(child, oldIndex, index - 1); + else + return moveChild(child, oldIndex, index); +} + +int GComponent::moveChild(GObject* child, int oldIndex, int index) +{ + int cnt = (int)_children.size(); + if (index > cnt) + index = cnt; + + if (oldIndex == index) + return oldIndex; + + child->retain(); + _children.erase(oldIndex); + if (index >= cnt) + _children.pushBack(child); + else + _children.insert(index, child); + child->release(); + + if (child->_displayObject->getParent() != nullptr) + { + if (_childrenRenderOrder == ChildrenRenderOrder::ASCENT) + { + int fromIndex = MIN(index, oldIndex); + int toIndex = MIN(max(index, oldIndex), cnt - 1); + for (int i = fromIndex; i <= toIndex; i++) + { + GObject* g = _children.at(i); + if (g->_displayObject->getParent() != nullptr) + g->_displayObject->setLocalZOrder(i); + } + } + else if (_childrenRenderOrder == ChildrenRenderOrder::DESCENT) + { + int fromIndex = MIN(index, oldIndex); + int toIndex = MIN(max(index, oldIndex), cnt - 1); + for (int i = fromIndex; i <= toIndex; i++) + { + GObject* g = _children.at(i); + if (g->_displayObject->getParent() != nullptr) + g->_displayObject->setLocalZOrder(cnt - 1 - i); + } + } + else + CALL_LATER(GComponent, buildNativeDisplayList); + + setBoundsChangedFlag(); + } + + return index; +} + +void GComponent::swapChildren(GObject* child1, GObject* child2) +{ + CCASSERT(child1 != nullptr, "Argument1 must be non-nil"); + CCASSERT(child2 != nullptr, "Argument2 must be non-nil"); + + int index1 = (int)_children.getIndex(child1); + int index2 = (int)_children.getIndex(child2); + + CCASSERT(index1 != -1, "Not a child of this container"); + CCASSERT(index2 != -1, "Not a child of this container"); + + swapChildrenAt(index1, index2); +} + +void GComponent::swapChildrenAt(int index1, int index2) +{ + GObject* child1 = _children.at(index1); + GObject* child2 = _children.at(index2); + + setChildIndex(child1, index2); + setChildIndex(child2, index1); +} + +int GComponent::numChildren() const +{ + return (int)_children.size(); +} + +bool GComponent::isAncestorOf(const GObject* obj) const +{ + if (obj == nullptr) + return false; + + GComponent* p = obj->_parent; + while (p != nullptr) + { + if (p == this) + return true; + + p = p->_parent; + } + return false; +} + +bool GComponent::isChildInView(GObject* child) +{ + if (_scrollPane != nullptr) + { + return _scrollPane->isChildInView(child); + } + else if (((FUIContainer*)_displayObject)->isClippingEnabled()) + { + return child->_position.x + child->_size.width >= 0 && child->_position.x <= _size.width && child->_position.y + child->_size.height >= 0 && child->_position.y <= _size.height; + } + else + return true; +} + +int GComponent::getFirstChildInView() +{ + int i = 0; + for (auto& child : _children) + { + + if (isChildInView(child)) + return i; + i++; + } + return -1; +} + +GController* GComponent::getController(const std::string& name) const +{ + for (const auto& c : _controllers) + { + if (c->name.compare(name) == 0) + return c; + } + + return nullptr; +} + +void GComponent::addController(GController* c) +{ + CCASSERT(c != nullptr, "Argument must be non-nil"); + + _controllers.pushBack(c); +} + +GController* GComponent::getControllerAt(int index) const +{ + CCASSERT(index >= 0 && index < _controllers.size(), "Invalid controller index"); + + return _controllers.at(index); +} + +void GComponent::removeController(GController* c) +{ + CCASSERT(c != nullptr, "Argument must be non-nil"); + + ssize_t index = _controllers.getIndex(c); + CCASSERT(index != -1, "controller not exists"); + + c->setParent(nullptr); + applyController(c); + _controllers.erase(index); +} + +void GComponent::applyController(GController* c) +{ + _applyingController = c; + + for (const auto& child : _children) + child->handleControllerChanged(c); + + _applyingController = nullptr; + + c->runActions(); +} + +void GComponent::applyAllControllers() +{ + for (const auto& c : _controllers) + applyController(c); +} + +Transition* GComponent::getTransition(const std::string& name) const +{ + for (const auto& c : _transitions) + { + if (c->name.compare(name) == 0) + return c; + } + + return nullptr; +} + +Transition* GComponent::getTransitionAt(int index) const +{ + CCASSERT(index >= 0 && index < _transitions.size(), "Invalid transition index"); + + return _transitions.at(index); +} + +void GComponent::adjustRadioGroupDepth(GObject* obj, GController* c) +{ + ssize_t cnt = (ssize_t)_children.size(); + ssize_t i; + GObject* child; + ssize_t myIndex = -1, maxIndex = -1; + for (i = 0; i < cnt; i++) + { + child = _children.at(i); + if (child == obj) + { + myIndex = i; + } + else if (dynamic_cast(child) && ((GButton*)child)->getRelatedController() == c) + { + if (i > maxIndex) + maxIndex = i; + } + } + if (myIndex < maxIndex) + { + if (_applyingController != nullptr) + _children.at(maxIndex)->handleControllerChanged(_applyingController); + swapChildrenAt((int)myIndex, (int)maxIndex); + } +} + +void GComponent::setOpaque(bool value) +{ + _opaque = value; +} + +void GComponent::setMargin(const Margin& value) +{ + _margin = value; +} + +void GComponent::setChildrenRenderOrder(ChildrenRenderOrder value) +{ + if (_childrenRenderOrder != value) + { + _childrenRenderOrder = value; + CALL_LATER(GComponent, buildNativeDisplayList); + } +} + +void GComponent::setApexIndex(int value) +{ + if (_apexIndex != value) + { + _apexIndex = value; + + if (_childrenRenderOrder == ChildrenRenderOrder::ARCH) + CALL_LATER(GComponent, buildNativeDisplayList); + } +} + +cocos2d::Node* GComponent::getMask() const +{ + return ((FUIContainer*)_displayObject)->getStencil(); +} + +void GComponent::setMask(cocos2d::Node* value, bool inverted) +{ + if (_maskOwner) + { + _maskOwner->_alignToBL = false; + childStateChanged(_maskOwner); + _maskOwner->handlePositionChanged(); + _maskOwner->release(); + _maskOwner = nullptr; + } + + if (value) + { + for (auto& child : _children) + { + if (child->_displayObject == value) + { + _maskOwner = child; + if (value->getParent()) + value->getParent()->removeChild(value, false); + _maskOwner->_alignToBL = true; + _maskOwner->handlePositionChanged(); + _maskOwner->retain(); + break; + } + } + } + + ((FUIContainer*)_displayObject)->setStencil(value); + if (value) + { + ((FUIContainer*)_displayObject)->setAlphaThreshold(0.05f); + ((FUIContainer*)_displayObject)->setInverted(inverted); + } +} + +void GComponent::setHitArea(IHitTest* value) +{ + if (_hitArea != value) + { + CC_SAFE_DELETE(_hitArea); + _hitArea = value; + } +} + +float GComponent::getViewWidth() const +{ + if (_scrollPane != nullptr) + return _scrollPane->getViewSize().width; + else + return _size.width - _margin.left - _margin.right; +} + +void GComponent::setViewWidth(float value) +{ + if (_scrollPane != nullptr) + _scrollPane->setViewWidth(value); + else + setWidth(value + _margin.left + _margin.right); +} + +float GComponent::getViewHeight() const +{ + if (_scrollPane != nullptr) + return _scrollPane->getViewSize().height; + else + return _size.height - _margin.top - _margin.bottom; +} + +void GComponent::setViewHeight(float value) +{ + if (_scrollPane != nullptr) + _scrollPane->setViewHeight(value); + else + setHeight(value + _margin.top + _margin.bottom); +} + +void GComponent::setBoundsChangedFlag() +{ + if (_scrollPane == nullptr && !_trackBounds) + return; + + _boundsChanged = true; + CALL_LATER(GComponent, doUpdateBounds); +} + +void GComponent::ensureBoundsCorrect() +{ + if (_boundsChanged) + updateBounds(); +} + +void GComponent::updateBounds() +{ + float ax, ay, aw, ah; + if (!_children.empty()) + { + ax = FLT_MAX; + ay = FLT_MAX; + float ar = -FLT_MAX, ab = -FLT_MAX; + float tmp; + + size_t cnt = _children.size(); + for (size_t i = 0; i < cnt; ++i) + { + GObject* child = _children.at(i); + tmp = child->getX(); + if (tmp < ax) + ax = tmp; + tmp = child->getY(); + if (tmp < ay) + ay = tmp; + tmp = child->getX() + child->getWidth(); + if (tmp > ar) + ar = tmp; + tmp = child->getY() + child->getHeight(); + if (tmp > ab) + ab = tmp; + } + aw = ar - ax; + ah = ab - ay; + } + else + { + ax = 0; + ay = 0; + aw = 0; + ah = 0; + } + setBounds(ax, ay, aw, ah); +} + +void GComponent::setBounds(float ax, float ay, float aw, float ah) +{ + _boundsChanged = false; + if (_scrollPane != nullptr) + _scrollPane->setContentSize(ceil(ax + aw), ceil(ay + ah)); +} + +void GComponent::doUpdateBounds() +{ + if (_boundsChanged) + updateBounds(); +} + +void GComponent::childStateChanged(GObject* child) +{ + if (_buildingDisplayList) + return; + + int cnt = (int)_children.size(); + + if (dynamic_cast(child) != nullptr) + { + for (int i = 0; i < cnt; ++i) + { + GObject* g = _children.at(i); + if (g->_group == child) + childStateChanged(g); + } + } + + if (child->_displayObject == nullptr || child == _maskOwner) + return; + + if (child->internalVisible()) + { + if (child->_displayObject->getParent() == nullptr) + { + if (_childrenRenderOrder == ChildrenRenderOrder::ASCENT) + { + int index = (int)_children.getIndex(child); + _container->addChild(child->_displayObject, index); + size_t cnt = _children.size(); + for (size_t i = index + 1; i < cnt; i++) + { + child = _children.at(i); + if (child->_displayObject->getParent() != nullptr) + child->_displayObject->setLocalZOrder((int)i); + } + } + else if (_childrenRenderOrder == ChildrenRenderOrder::DESCENT) + { + ssize_t index = _children.getIndex(child); + _container->addChild(child->_displayObject, (int)(cnt - 1 - index)); + for (ssize_t i = 0; i < index; i++) + { + child = _children.at(i); + if (child->_displayObject->getParent() != nullptr) + child->_displayObject->setLocalZOrder((int)(cnt - 1 - i)); + } + } + else + { + CALL_LATER(GComponent, buildNativeDisplayList); + } + } + } + else + { + if (child->_displayObject->getParent() != nullptr) + { + _container->removeChild(child->_displayObject, false); + if (_childrenRenderOrder == ChildrenRenderOrder::ARCH) + { + CALL_LATER(GComponent, buildNativeDisplayList); + } + } + } +} + +void GComponent::childSortingOrderChanged(GObject* child, int oldValue, int newValue) +{ + if (newValue == 0) + { + _sortingChildCount--; + setChildIndex(child, (int)_children.size()); + } + else + { + if (oldValue == 0) + _sortingChildCount++; + + int oldIndex = (int)_children.getIndex(child); + int index = getInsertPosForSortingChild(child); + if (oldIndex < index) + moveChild(child, oldIndex, index - 1); + else + moveChild(child, oldIndex, index); + } +} + +void GComponent::buildNativeDisplayList() +{ + int cnt = (int)_children.size(); + if (cnt == 0) + return; + + switch (_childrenRenderOrder) + { + case ChildrenRenderOrder::ASCENT: + { + for (int i = 0; i < cnt; i++) + { + GObject* child = _children.at(i); + if (child->_displayObject != nullptr && child != _maskOwner && child->internalVisible()) + _container->addChild(child->_displayObject, i); + } + } + break; + case ChildrenRenderOrder::DESCENT: + { + for (int i = 0; i < cnt; i++) + { + GObject* child = _children.at(i); + if (child->_displayObject != nullptr && child != _maskOwner && child->internalVisible()) + _container->addChild(child->_displayObject, cnt - 1 - i); + } + } + break; + + case ChildrenRenderOrder::ARCH: + { + int ai = MIN(_apexIndex, cnt); + for (int i = 0; i < ai; i++) + { + GObject* child = _children.at(i); + if (child->_displayObject != nullptr && child != _maskOwner && child->internalVisible()) + { + if (child->_displayObject->getParent() == nullptr) + _container->addChild(child->_displayObject, i); + else + child->_displayObject->setLocalZOrder((int)i); + } + } + for (int i = cnt - 1; i >= ai; i--) + { + GObject* child = _children.at(i); + if (child->_displayObject != nullptr && child != _maskOwner && child->internalVisible()) + { + if (child->_displayObject->getParent() == nullptr) + _container->addChild(child->_displayObject, ai + cnt - 1 - i); + else + child->_displayObject->setLocalZOrder(ai + cnt - 1 - i); + } + } + } + break; + } +} + +cocos2d::Vec2 GComponent::getSnappingPosition(const cocos2d::Vec2& pt) +{ + int cnt = (int)_children.size(); + if (cnt == 0) + return pt; + + ensureBoundsCorrect(); + + GObject* obj = nullptr; + + Vec2 ret = pt; + + int i = 0; + if (ret.y != 0) + { + for (; i < cnt; i++) + { + obj = _children.at(i); + if (ret.y < obj->getY()) + { + if (i == 0) + { + ret.y = 0; + break; + } + else + { + GObject* prev = _children.at(i - 1); + if (ret.y < prev->getY() + prev->getHeight() / 2) //top half part + ret.y = prev->getY(); + else //bottom half part + ret.y = obj->getY(); + break; + } + } + } + + if (i == cnt) + ret.y = obj->getY(); + } + + if (ret.x != 0) + { + if (i > 0) + i--; + for (; i < cnt; i++) + { + obj = _children.at(i); + if (ret.x < obj->getX()) + { + if (i == 0) + { + ret.x = 0; + break; + } + else + { + GObject* prev = _children.at(i - 1); + if (ret.x < prev->getX() + prev->getWidth() / 2) // top half part + ret.x = prev->getX(); + else //bottom half part + ret.x = obj->getX(); + break; + } + } + } + if (i == cnt) + ret.x = obj->getX(); + } + + return ret; +} + +GObject* GComponent::hitTest(const Vec2& worldPoint, const Camera* camera) +{ + if (_touchDisabled || !_touchable || !_displayObject->isVisible() || !_displayObject->getParent()) + return nullptr; + + GObject* target = nullptr; + if (_maskOwner) + { + if (_maskOwner->hitTest(worldPoint, camera) != nullptr) + { + if (((FUIContainer*)_displayObject)->isInverted()) + return nullptr; + } + else + { + if (!((FUIContainer*)_displayObject)->isInverted()) + return nullptr; + } + } + + Rect rect; + int flag = 0; + + if (_hitArea) + { + Rect rect; + rect.size = _size; + Vec2 localPoint = _displayObject->convertToNodeSpace(worldPoint); + flag = rect.containsPoint(localPoint) ? 1 : 2; + + if (!_hitArea->hitTest(this, localPoint)) + return nullptr; + } + else + { + if (((FUIContainer*)_displayObject)->isClippingEnabled()) + { + Rect rect; + rect.size = _size; + Vec2 localPoint = _displayObject->convertToNodeSpace(worldPoint); + flag = rect.containsPoint(localPoint) ? 1 : 2; + + const Rect& clipRect = ((FUIContainer*)_displayObject)->getClippingRegion(); + if (!clipRect.containsPoint(localPoint)) + return nullptr; + } + } + + if (_scrollPane) + { + target = _scrollPane->hitTest(worldPoint, camera); + if (!target) + return nullptr; + + if (target != this) + return target; + } + + int cnt = (int)_children.size(); + + switch (_childrenRenderOrder) + { + case ChildrenRenderOrder::ASCENT: + { + for (int i = cnt - 1; i >= 0; i--) + { + GObject* child = _children.at(i); + if (!child->_displayObject || child == _maskOwner) + continue; + + target = child->hitTest(worldPoint, camera); + if (target) + return target; + } + } + break; + case ChildrenRenderOrder::DESCENT: + { + for (int i = 0; i < cnt; i++) + { + GObject* child = _children.at(i); + if (!child->_displayObject || child == _maskOwner) + continue; + + target = child->hitTest(worldPoint, camera); + if (target) + return target; + } + } + break; + + case ChildrenRenderOrder::ARCH: + { + int ai = MIN(_apexIndex, cnt); + for (int i = ai; i < cnt; i++) + { + GObject* child = _children.at(i); + if (!child->_displayObject || child == _maskOwner) + continue; + + target = child->hitTest(worldPoint, camera); + if (target) + return target; + } + for (int i = ai - 1; i >= 0; i--) + { + GObject* child = _children.at(i); + if (!child->_displayObject || child == _maskOwner) + continue; + + target = child->hitTest(worldPoint, camera); + if (target) + return target; + } + } + } + + if (_opaque) + { + if (flag == 0) + { + rect.size = _size; + flag = rect.containsPoint(_displayObject->convertToNodeSpace(worldPoint)) ? 1 : 2; + } + + if (flag == 1) + return this; + else + return nullptr; + } + else + return nullptr; +} + +void GComponent::setupOverflow(OverflowType overflow) +{ + if (overflow == OverflowType::HIDDEN) + { + ((FUIContainer*)_displayObject)->setClippingEnabled(true); + ((FUIContainer*)_displayObject)->setClippingRegion(Rect(_margin.left, _margin.top, _size.width - _margin.left - _margin.right, _size.height - _margin.top - _margin.bottom)); + } + + _container->setPosition2(_margin.left, _margin.top); +} + +void GComponent::setupScroll(ByteBuffer* buffer) +{ + _scrollPane = new ScrollPane(this); + _scrollPane->setup(buffer); +} + +void GComponent::handleSizeChanged() +{ + GObject::handleSizeChanged(); + + if (_scrollPane != nullptr) + _scrollPane->onOwnerSizeChanged(); + else + _container->setPosition2(_margin.left, _margin.top); + + if (_maskOwner) + _maskOwner->handlePositionChanged(); + + if (((FUIContainer*)_displayObject)->isClippingEnabled()) + ((FUIContainer*)_displayObject)->setClippingRegion(Rect(_margin.left, _margin.top, _size.width - _margin.left - _margin.right, _size.height - _margin.top - _margin.bottom)); + + if (_hitArea) + { + PixelHitTest* test = dynamic_cast(_hitArea); + if (sourceSize.width != 0) + test->scaleX = _size.width / sourceSize.width; + if (sourceSize.height != 0) + test->scaleY = _size.height / sourceSize.height; + } +} + +void GComponent::handleGrayedChanged() +{ + GObject::handleGrayedChanged(); + + GController* cc = getController("grayed"); + if (cc != nullptr) + cc->setSelectedIndex(isGrayed() ? 1 : 0); + else + { + for (auto& child : _children) + child->handleGrayedChanged(); + } +} + +void GComponent::handleControllerChanged(GController* c) +{ + GObject::handleControllerChanged(c); + + if (_scrollPane != nullptr) + _scrollPane->handleControllerChanged(c); +} + +void GComponent::onEnter() +{ + GObject::onEnter(); + + if (!_transitions.empty()) + { + for (auto& trans : _transitions) + trans->onOwnerAddedToStage(); + } +} + +void GComponent::onExit() +{ + GObject::onExit(); + + if (!_transitions.empty()) + { + for (auto& trans : _transitions) + trans->onOwnerRemovedFromStage(); + } +} + +void GComponent::constructFromResource() +{ + constructFromResource(nullptr, 0); +} + +void GComponent::constructFromResource(std::vector* objectPool, int poolIndex) +{ + PackageItem* contentItem = _packageItem->getBranch(); + + if (!contentItem->translated) + { + contentItem->translated = true; + TranslationHelper::translateComponent(contentItem); + } + + ByteBuffer* buffer = contentItem->rawData; + buffer->seek(0, 0); + + _underConstruct = true; + + sourceSize.width = buffer->readInt(); + sourceSize.height = buffer->readInt(); + initSize = sourceSize; + + setSize(sourceSize.width, sourceSize.height); + + if (buffer->readBool()) + { + minSize.width = buffer->readInt(); + maxSize.width = buffer->readInt(); + minSize.height = buffer->readInt(); + maxSize.height = buffer->readInt(); + } + + if (buffer->readBool()) + { + float f1 = buffer->readFloat(); + float f2 = buffer->readFloat(); + setPivot(f1, f2, buffer->readBool()); + } + + if (buffer->readBool()) + { + _margin.top = buffer->readInt(); + _margin.bottom = buffer->readInt(); + _margin.left = buffer->readInt(); + _margin.right = buffer->readInt(); + } + + OverflowType overflow = (OverflowType)buffer->readByte(); + if (overflow == OverflowType::SCROLL) + { + int savedPos = buffer->getPos(); + buffer->seek(0, 7); + setupScroll(buffer); + buffer->setPos(savedPos); + } + else + setupOverflow(overflow); + + if (buffer->readBool()) //clipsoft + buffer->skip(8); + + _buildingDisplayList = true; + + buffer->seek(0, 1); + + int controllerCount = buffer->readShort(); + for (int i = 0; i < controllerCount; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + GController* controller = new GController(); + _controllers.pushBack(controller); + controller->setParent(this); + controller->setup(buffer); + controller->release(); + + buffer->setPos(nextPos); + } + + buffer->seek(0, 2); + + GObject* child; + int childCount = buffer->readShort(); + for (int i = 0; i < childCount; i++) + { + int dataLen = buffer->readShort(); + int curPos = buffer->getPos(); + + if (objectPool != nullptr) + child = (*objectPool)[poolIndex + i]; + else + { + buffer->seek(curPos, 0); + + ObjectType type = (ObjectType)buffer->readByte(); + const string& src = buffer->readS(); + const string& pkgId = buffer->readS(); + + PackageItem* pi = nullptr; + if (!src.empty()) + { + UIPackage* pkg; + if (!pkgId.empty()) + pkg = UIPackage::getById(pkgId); + else + pkg = contentItem->owner; + + pi = pkg != nullptr ? pkg->getItem(src) : nullptr; + } + + if (pi != nullptr) + { + child = UIObjectFactory::newObject(pi); + child->constructFromResource(); + } + else + child = UIObjectFactory::newObject(type); + } + + child->_underConstruct = true; + child->setup_beforeAdd(buffer, curPos); + child->_parent = this; + _children.pushBack(child); + + buffer->setPos(curPos + dataLen); + } + + buffer->seek(0, 3); + _relations->setup(buffer, true); + + buffer->seek(0, 2); + buffer->skip(2); + + for (int i = 0; i < childCount; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + buffer->seek(buffer->getPos(), 3); + _children.at(i)->relations()->setup(buffer, false); + + buffer->setPos(nextPos); + } + + buffer->seek(0, 2); + buffer->skip(2); + + for (int i = 0; i < childCount; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + child = _children.at(i); + child->setup_afterAdd(buffer, buffer->getPos()); + child->_underConstruct = false; + + buffer->setPos(nextPos); + } + + buffer->seek(0, 4); + + buffer->skip(2); //customData + _opaque = buffer->readBool(); + int maskId = buffer->readShort(); + if (maskId != -1) + { + bool inverted = buffer->readBool(); + setMask(getChildAt(maskId)->displayObject(), inverted); + } + + const string& hitTestId = buffer->readS(); + int i1 = buffer->readInt(); + int i2 = buffer->readInt(); + if (!hitTestId.empty()) + { + PackageItem* pi = contentItem->owner->getItem(hitTestId); + if (pi != nullptr && pi->pixelHitTestData != nullptr) + setHitArea(new PixelHitTest(pi->pixelHitTestData, i1, i2)); + } + else if (i1 != 0 && i2 != -1) + { + //setHitArea(new ChildHitArea(getChildAt(i2))); + } + + buffer->seek(0, 5); + + int transitionCount = buffer->readShort(); + for (int i = 0; i < transitionCount; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + Transition* trans = new Transition(this); + trans->setup(buffer); + _transitions.pushBack(trans); + trans->release(); + + buffer->setPos(nextPos); + } + + applyAllControllers(); + + _buildingDisplayList = false; + _underConstruct = false; + + buildNativeDisplayList(); + setBoundsChangedFlag(); + + if (contentItem->objectType != ObjectType::COMPONENT) + constructExtension(buffer); + + onConstruct(); +} + +void GComponent::constructExtension(ByteBuffer* buffer) +{ +} + +void GComponent::onConstruct() +{ +} + +void GComponent::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_afterAdd(buffer, beginPos); + + buffer->seek(beginPos, 4); + + int pageController = buffer->readShort(); + if (pageController != -1 && _scrollPane != nullptr && _scrollPane->isPageMode()) + _scrollPane->setPageController(_parent->getControllerAt(pageController)); + + int cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + GController* cc = getController(buffer->readS()); + const string& pageId = buffer->readS(); + if (cc) + cc->setSelectedPageId(pageId); + } + + if (buffer->version >= 2) + { + cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + std::string target = buffer->readS(); + ObjectPropID propId = (ObjectPropID)buffer->readShort(); + std::string value = buffer->readS(); + GObject* obj = getChildByPath(target); + if (obj != nullptr) + obj->setProp(propId, Value(value)); + } + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/GComponent.h b/extensions/fairygui/GComponent.h new file mode 100644 index 0000000000..7cc929fd4c --- /dev/null +++ b/extensions/fairygui/GComponent.h @@ -0,0 +1,150 @@ +#ifndef __GCOMPONENT_H__ +#define __GCOMPONENT_H__ + +#include "FairyGUIMacros.h" +#include "GObject.h" +#include "Margin.h" +#include "ScrollPane.h" +#include "Transition.h" +#include "cocos2d.h" +#include "display/FUIContainer.h" +#include "event/HitTest.h" + +NS_FGUI_BEGIN + +class GComponent : public GObject +{ +public: + GComponent(); + virtual ~GComponent(); + + CREATE_FUNC(GComponent); + + GObject* addChild(GObject* child); + virtual GObject* addChildAt(GObject* child, int index); + + void removeChild(GObject* child); + virtual void removeChildAt(int index); + void removeChildren() { removeChildren(0, -1); } + void removeChildren(int beginIndex, int endIndex); + + GObject* getChildAt(int index) const; + GObject* getChild(const std::string& name) const; + GObject* getChildByPath(const std::string& path) const; + GObject* getChildInGroup(const GGroup* group, const std::string& name) const; + GObject* getChildById(const std::string& id) const; + const cocos2d::Vector& getChildren() const { return _children; } + + int getChildIndex(const GObject* child) const; + void setChildIndex(GObject* child, int index); + int setChildIndexBefore(GObject* child, int index); + void swapChildren(GObject* child1, GObject* child2); + void swapChildrenAt(int index1, int index2); + + int numChildren() const; + bool isAncestorOf(const GObject* obj) const; + + virtual bool isChildInView(GObject* child); + virtual int getFirstChildInView(); + + void addController(GController* c); + GController* getControllerAt(int index) const; + GController* getController(const std::string& name) const; + const cocos2d::Vector& getControllers() const { return _controllers; } + void removeController(GController* c); + void applyController(GController* c); + void applyAllControllers(); + + Transition* getTransition(const std::string& name) const; + Transition* getTransitionAt(int index) const; + const cocos2d::Vector& getTransitions() const { return _transitions; } + + bool getOpaque() const { return _opaque; } + void setOpaque(bool value); + + const Margin& getMargin() { return _margin; } + void setMargin(const Margin& value); + + ChildrenRenderOrder getChildrenRenderOrder() const { return _childrenRenderOrder; } + void setChildrenRenderOrder(ChildrenRenderOrder value); + int getApexIndex() const { return _apexIndex; } + void setApexIndex(int value); + + cocos2d::Node* getMask() const; + void setMask(cocos2d::Node* value, bool inverted = false); + + IHitTest* getHitArea() const { return _hitArea; } + void setHitArea(IHitTest* value); + + ScrollPane* getScrollPane() const { return _scrollPane; } + + float getViewWidth() const; + void setViewWidth(float value); + float getViewHeight() const; + void setViewHeight(float value); + + void setBoundsChangedFlag(); + void ensureBoundsCorrect(); + + virtual GObject* hitTest(const cocos2d::Vec2& worldPoint, const cocos2d::Camera* camera) override; + virtual cocos2d::Vec2 getSnappingPosition(const cocos2d::Vec2& pt); + + //internal use + void childSortingOrderChanged(GObject* child, int oldValue, int newValue); + void childStateChanged(GObject* child); + void adjustRadioGroupDepth(GObject* obj, GController* c); + + virtual void constructFromResource() override; + void constructFromResource(std::vector* objectPool, int poolIndex); + + bool _buildingDisplayList; + +protected: + virtual void constructExtension(ByteBuffer* buffer); + virtual void onConstruct(); + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + virtual void handleInit() override; + virtual void handleSizeChanged() override; + virtual void handleGrayedChanged() override; + virtual void handleControllerChanged(GController* c) override; + + virtual void onEnter() override; + virtual void onExit() override; + + virtual void updateBounds(); + void setBounds(float ax, float ay, float aw, float ah); + + void setupOverflow(OverflowType overflow); + void setupScroll(ByteBuffer* buffer); + + cocos2d::Vector _children; + cocos2d::Vector _controllers; + cocos2d::Vector _transitions; + FUIInnerContainer* _container; + ScrollPane* _scrollPane; + Margin _margin; + cocos2d::Vec2 _alignOffset; + ChildrenRenderOrder _childrenRenderOrder; + int _apexIndex; + bool _boundsChanged; + bool _trackBounds; + GObject* _maskOwner; + IHitTest* _hitArea; + +private: + int getInsertPosForSortingChild(GObject* target); + int moveChild(GObject* child, int oldIndex, int index); + + CALL_LATER_FUNC(GComponent, doUpdateBounds); + CALL_LATER_FUNC(GComponent, buildNativeDisplayList); + + bool _opaque; + int _sortingChildCount; + GController* _applyingController; + + friend class ScrollPane; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GGraph.cpp b/extensions/fairygui/GGraph.cpp new file mode 100644 index 0000000000..430ce44d46 --- /dev/null +++ b/extensions/fairygui/GGraph.cpp @@ -0,0 +1,290 @@ +#include "GGraph.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +static void drawVertRect(cocos2d::DrawNode* shape, float x, float y, float width, float height, const cocos2d::Color4F& color) +{ + float mx = x + width; + float my = y + height; + shape->drawTriangle(Vec2(x, y), Vec2(mx, y), Vec2(x, my), color); + shape->drawTriangle(Vec2(mx, y), Vec2(mx, my), Vec2(x, my), color); +} + +GGraph::GGraph() : _shape(nullptr), + _type(0), + _lineSize(1), + _lineColor(Color4F::BLACK), + _fillColor(Color4F::WHITE), + _cornerRadius(nullptr), + _polygonPoints(nullptr), + _distances(nullptr) +{ + _touchDisabled = true; +} + +GGraph::~GGraph() +{ + CC_SAFE_DELETE(_cornerRadius); + CC_SAFE_DELETE(_polygonPoints); + CC_SAFE_DELETE(_distances); +} + +void GGraph::handleInit() +{ + _shape = DrawNode::create(); + _shape->retain(); + + _displayObject = _shape; +} + +void GGraph::drawRect(float aWidth, float aHeight, int lineSize, const cocos2d::Color4F& lineColor, const cocos2d::Color4F& fillColor) +{ + _type = 0; //avoid updateshape call in handleSizeChange + setSize(aWidth, aHeight); + _type = 1; + _lineSize = lineSize; + _lineColor = lineColor; + _fillColor = fillColor; + updateShape(); +} + +void GGraph::drawEllipse(float aWidth, float aHeight, int lineSize, const cocos2d::Color4F& lineColor, const cocos2d::Color4F& fillColor) +{ + _type = 0; //avoid updateshape call in handleSizeChange + setSize(aWidth, aHeight); + _type = 2; + _lineSize = lineSize; + _lineColor = lineColor; + _fillColor = fillColor; + updateShape(); +} + +void GGraph::drawPolygon(int lineSize, const cocos2d::Color4F& lineColor, const cocos2d::Color4F& fillColor, const cocos2d::Vec2* points, int count) +{ + _type = 3; + _lineSize = lineSize; + _lineColor = lineColor; + _fillColor = fillColor; + if (_polygonPoints == nullptr) + _polygonPoints = new std::vector(); + else + _polygonPoints->clear(); + float h = getHeight(); + _polygonPointOffset = h; + for (int i = 0; i < count; i++) + { + Vec2 pt = *(points + i); + pt.y = h - pt.y; + _polygonPoints->push_back(*(points + i)); + } + updateShape(); +} + +void GGraph::drawRegularPolygon(int lineSize, const cocos2d::Color4F& lineColor, const cocos2d::Color4F& fillColor, + int sides, float startAngle, const float* distances, int count) +{ + _type = 4; + _lineSize = lineSize; + _lineColor = lineColor; + _fillColor = fillColor; + _sides = sides; + _startAngle = startAngle; + if (distances != nullptr) + { + if (_distances == nullptr) + _distances = new std::vector(); + else + _distances->clear(); + for (int i = 0; i < count; i++) + _distances->push_back(*(distances + i)); + } + else if (_distances != nullptr) + _distances->clear(); +} + +void GGraph::updateShape() +{ + _shape->clear(); + if (_type == 0) + { + _touchDisabled = false; + return; + } + + switch (_type) + { + case 1: + { + if (_lineSize > 0) + { + float wl = _size.width - _lineSize; + float hl = _size.height - _lineSize; + drawVertRect(_shape, 0, 0, wl, _lineSize, _lineColor); + drawVertRect(_shape, wl, 0, _lineSize, hl, _lineColor); + drawVertRect(_shape, _lineSize, hl, wl, _lineSize, _lineColor); + drawVertRect(_shape, 0, _lineSize, _lineSize, hl, _lineColor); + + drawVertRect(_shape, _lineSize, _lineSize, _size.width - _lineSize * 2, _size.height - _lineSize * 2, _fillColor); + } + else + drawVertRect(_shape, 0, 0, _size.width, _size.height, _fillColor); + break; + } + case 2: + { + if (_lineSize > 0) + { + _shape->setLineWidth(_lineSize); + _shape->drawCircle(Vec2(_size.width / 2, _size.height / 2), _size.width / 2, 0, 360, false, 1, _size.height / _size.width, _lineColor); + } + _shape->drawSolidCircle(Vec2(_size.width / 2, _size.height / 2), _size.width / 2, 0, 360, 1, _size.height / _size.width, _fillColor); + break; + } + case 3: + { + _shape->drawPolygon(_polygonPoints->data(), (int)_polygonPoints->size(), _fillColor, _lineSize * 0.5f, _lineColor); + break; + } + + case 4: + { + float h = getHeight(); + _polygonPointOffset = h; + if (_polygonPoints == nullptr) + _polygonPoints = new std::vector(); + else + _polygonPoints->clear(); + + float radius = MIN(getWidth(), getHeight()) * 0.5f; + float angle = MATH_DEG_TO_RAD(_startAngle); + float deltaAngle = 2 * M_PI / _sides; + float dist; + for (int i = 0; i < _sides; i++) + { + if (_distances != nullptr && i < (int)_distances->size()) + dist = (*_distances)[i]; + else + dist = 1; + + float xv = radius + radius * dist * cos(angle); + float yv = h - (radius + radius * dist * sin(angle)); + _polygonPoints->push_back(Vec2(xv, yv)); + + angle += deltaAngle; + } + + _shape->drawPolygon(_polygonPoints->data(), (int)_polygonPoints->size(), _fillColor, _lineSize * 0.5f, _lineColor); + + break; + } + } +} + +cocos2d::Color3B GGraph::getColor() const +{ + return (Color3B)_fillColor; +} + +void GGraph::setColor(const cocos2d::Color3B& value) +{ + _fillColor = Color4F(value, _fillColor.a); + updateShape(); +} + +cocos2d::Value GGraph::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getColor())); + default: + return GObject::getProp(propId); + } +} + +void GGraph::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + default: + GObject::setProp(propId, value); + break; + } +} + +void GGraph::handleSizeChanged() +{ + GObject::handleSizeChanged(); + + if (_type == 3 || _type == 4) + { + float h = getHeight(); + int count = (int)_polygonPoints->size(); + for (int i = 0; i < count; i++) + { + Vec2 pt = (*_polygonPoints)[i]; + pt.y = h - (_polygonPointOffset - pt.y); + (*_polygonPoints)[i] = pt; + } + _polygonPointOffset = h; + } + + updateShape(); +} + +void GGraph::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 5); + + _type = buffer->readByte(); + if (_type != 0) + { + _lineSize = buffer->readInt(); + _lineColor = (Color4F)buffer->readColor(); + _fillColor = (Color4F)buffer->readColor(); + if (buffer->readBool()) + { + _cornerRadius = new float[4]; + for (int i = 0; i < 4; i++) + _cornerRadius[i] = buffer->readFloat(); + } + + if (_type == 3) + { + int cnt = buffer->readShort() / 2; + _polygonPoints = new std::vector(cnt); + float h = getHeight(); + _polygonPointOffset = h; + for (int i = 0; i < cnt; i++) + { + float f1 = buffer->readFloat(); + float f2 = h - buffer->readFloat(); + (*_polygonPoints)[i] = Vec2(f1, f2); + } + } + else if (_type == 4) + { + _sides = buffer->readShort(); + _startAngle = buffer->readFloat(); + int cnt = buffer->readShort(); + if (cnt > 0) + { + _distances = new std::vector(cnt); + for (int i = 0; i < cnt; i++) + (*_distances)[i] = buffer->readFloat(); + } + } + + updateShape(); + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/GGraph.h b/extensions/fairygui/GGraph.h new file mode 100644 index 0000000000..4116f0609c --- /dev/null +++ b/extensions/fairygui/GGraph.h @@ -0,0 +1,54 @@ +#ifndef __GGRAPH_H__ +#define __GGRAPH_H__ + +#include "FairyGUIMacros.h" +#include "GObject.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GGraph : public GObject +{ +public: + GGraph(); + virtual ~GGraph(); + + CREATE_FUNC(GGraph); + + void drawRect(float aWidth, float aHeight, int lineSize, const cocos2d::Color4F& lineColor, const cocos2d::Color4F& fillColor); + void drawEllipse(float aWidth, float aHeight, int lineSize, const cocos2d::Color4F& lineColor, const cocos2d::Color4F& fillColor); + void drawPolygon(int lineSize, const cocos2d::Color4F& lineColor, const cocos2d::Color4F& fillColor, const cocos2d::Vec2* points, int count); + void drawRegularPolygon(int lineSize, const cocos2d::Color4F& lineColor, const cocos2d::Color4F& fillColor, int sides, float startAngle = 0, const float* distances = nullptr, int distanceCount = 0); + bool isEmpty() const { return _type == 0; } + + cocos2d::Color3B getColor() const; + void setColor(const cocos2d::Color3B& value); + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void handleInit() override; + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + virtual void handleSizeChanged() override; + +private: + void updateShape(); + + int _type; + cocos2d::Color4F _lineColor; + cocos2d::Color4F _fillColor; + int _lineSize; + float* _cornerRadius; + std::vector* _polygonPoints; + float _polygonPointOffset; + int _sides; + float _startAngle; + std::vector* _distances; + + cocos2d::DrawNode* _shape; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GGroup.cpp b/extensions/fairygui/GGroup.cpp new file mode 100644 index 0000000000..06c621ba97 --- /dev/null +++ b/extensions/fairygui/GGroup.cpp @@ -0,0 +1,467 @@ +#include "GGroup.h" +#include "GComponent.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GGroup::GGroup() : _layout(GroupLayoutType::NONE), + _lineGap(0), + _columnGap(0), + _excludeInvisibles(false), + _autoSizeDisabled(false), + _mainGridIndex(-1), + _mainGridMinSize(10), + _mainChildIndex(-1), + _totalSize(0), + _numChildren(0), + _percentReady(false), + _boundsChanged(false), + _updating(false) +{ + _touchDisabled = true; +} + +GGroup::~GGroup() +{ + CALL_LATER_CANCEL(GGroup, ensureBoundsCorrect); +} + +void GGroup::setLayout(GroupLayoutType value) +{ + if (_layout != value) + { + _layout = value; + setBoundsChangedFlag(true); + } +} + +void GGroup::setColumnGap(int value) +{ + if (_columnGap != value) + { + _columnGap = value; + setBoundsChangedFlag(); + } +} + +void GGroup::setLineGap(int value) +{ + if (_lineGap != value) + { + _lineGap = value; + setBoundsChangedFlag(); + } +} + +void GGroup::setExcludeInvisibles(bool value) +{ + if (_excludeInvisibles != value) + { + _excludeInvisibles = value; + setBoundsChangedFlag(); + } +} +void GGroup::setAutoSizeDisabled(bool value) +{ + if (_autoSizeDisabled != value) + { + _autoSizeDisabled = value; + setBoundsChangedFlag(); + } +} + +void GGroup::setMainGridIndex(int value) +{ + if (_mainGridIndex != value) + { + _mainGridIndex = value; + setBoundsChangedFlag(); + } +} + +void GGroup::setMainGridMinSize(int value) +{ + if (_mainGridMinSize != value) + { + _mainGridMinSize = value; + setBoundsChangedFlag(); + } +} + +void GGroup::setBoundsChangedFlag(bool positionChangedOnly) +{ + if (_updating == 0 && _parent != nullptr) + { + if (!positionChangedOnly) + _percentReady = false; + + if (!_boundsChanged) + { + _boundsChanged = true; + + if (_layout != GroupLayoutType::NONE) + CALL_LATER(GGroup, ensureBoundsCorrect); + } + } +} + +void GGroup::ensureBoundsCorrect() +{ + if (_parent == nullptr || !_boundsChanged) + return; + + CALL_LATER_CANCEL(GGroup, ensureBoundsCorrect); + _boundsChanged = false; + + if (_autoSizeDisabled) + resizeChildren(0, 0); + else + { + handleLayout(); + updateBounds(); + } +} + +void GGroup::updateBounds() +{ + int cnt = _parent->numChildren(); + int i; + GObject* child; + float ax = FLT_MAX, ay = FLT_MAX; + float ar = FLT_MIN, ab = FLT_MIN; + float tmp; + bool empty = true; + + for (i = 0; i < cnt; i++) + { + child = _parent->getChildAt(i); + if (child->_group != this || (_excludeInvisibles && !child->internalVisible3())) + continue; + + tmp = child->getX(); + if (tmp < ax) + ax = tmp; + tmp = child->getY(); + if (tmp < ay) + ay = tmp; + tmp = child->getX() + child->getWidth(); + if (tmp > ar) + ar = tmp; + tmp = child->getY() + child->getHeight(); + if (tmp > ab) + ab = tmp; + + empty = false; + } + + float w; + float h; + if (!empty) + { + _updating |= 1; + setPosition(ax, ay); + _updating &= 2; + + w = ar - ax; + h = ab - ay; + } + else + w = h = 0; + + if ((_updating & 2) == 0) + { + _updating |= 2; + setSize(w, h); + _updating &= 1; + } + else + { + _updating &= 1; + resizeChildren(getWidth() - w, getHeight() - h); + } +} + +void GGroup::handleLayout() +{ + _updating |= 1; + + if (_layout == GroupLayoutType::HORIZONTAL) + { + float curX = getX(); + int cnt = _parent->numChildren(); + for (int i = 0; i < cnt; i++) + { + GObject* child = _parent->getChildAt(i); + if (child->_group != this) + continue; + if (_excludeInvisibles && !child->internalVisible3()) + continue; + + child->setXMin(curX); + if (child->getWidth() != 0) + curX += child->getWidth() + _columnGap; + } + } + else if (_layout == GroupLayoutType::VERTICAL) + { + float curY = getY(); + int cnt = _parent->numChildren(); + for (int i = 0; i < cnt; i++) + { + GObject* child = _parent->getChildAt(i); + if (child->_group != this) + continue; + if (_excludeInvisibles && !child->internalVisible3()) + continue; + + child->setYMin(curY); + if (child->getHeight() != 0) + curY += child->getHeight() + _lineGap; + } + } + + _updating &= 2; +} + +void GGroup::moveChildren(float dx, float dy) +{ + if ((_updating & 1) != 0 || _parent == nullptr) + return; + + _updating |= 1; + + int cnt = _parent->numChildren(); + int i; + GObject* child; + for (i = 0; i < cnt; i++) + { + child = _parent->getChildAt(i); + if (child->_group == this) + { + child->setPosition(child->getX() + dx, child->getY() + dy); + } + } + + _updating &= 2; +} + +void GGroup::resizeChildren(float dw, float dh) +{ + if (_layout == GroupLayoutType::NONE || (_updating & 2) != 0 || _parent == nullptr) + return; + + _updating |= 2; + + if (_boundsChanged) + { + _boundsChanged = false; + if (!_autoSizeDisabled) + { + updateBounds(); + return; + } + } + + int cnt = _parent->numChildren(); + + if (!_percentReady) + { + _percentReady = true; + _numChildren = 0; + _totalSize = 0; + _mainChildIndex = -1; + + int j = 0; + for (int i = 0; i < cnt; i++) + { + GObject* child = _parent->getChildAt(i); + if (child->_group != this) + continue; + + if (!_excludeInvisibles || child->internalVisible3()) + { + if (j == _mainGridIndex) + _mainChildIndex = i; + + _numChildren++; + + if (_layout == GroupLayoutType::HORIZONTAL) + _totalSize += child->getWidth(); + else + _totalSize += child->getHeight(); + } + + j++; + } + + if (_mainChildIndex != -1) + { + if (_layout == GroupLayoutType::HORIZONTAL) + { + GObject* child = _parent->getChildAt(_mainChildIndex); + _totalSize += _mainGridMinSize - child->getWidth(); + child->_sizePercentInGroup = _mainGridMinSize / _totalSize; + } + else + { + GObject* child = _parent->getChildAt(_mainChildIndex); + _totalSize += _mainGridMinSize - child->getHeight(); + child->_sizePercentInGroup = _mainGridMinSize / _totalSize; + } + } + + for (int i = 0; i < cnt; i++) + { + GObject* child = _parent->getChildAt(i); + if (child->_group != this) + continue; + + if (i == _mainChildIndex) + continue; + + if (_totalSize > 0) + child->_sizePercentInGroup = (_layout == GroupLayoutType::HORIZONTAL ? child->getWidth() : child->getHeight()) / _totalSize; + else + child->_sizePercentInGroup = 0; + } + } + + float remainSize = 0; + float remainPercent = 1; + bool priorHandled = false; + + if (_layout == GroupLayoutType::HORIZONTAL) + { + remainSize = getWidth() - (_numChildren - 1) * _columnGap; + if (_mainChildIndex != -1 && remainSize >= _totalSize) + { + GObject* child = _parent->getChildAt(_mainChildIndex); + child->setSize(remainSize - (_totalSize - _mainGridMinSize), child->_rawSize.height + dh, true); + remainSize -= child->getWidth(); + remainPercent -= child->_sizePercentInGroup; + priorHandled = true; + } + + float curX = getX(); + for (int i = 0; i < cnt; i++) + { + GObject* child = _parent->getChildAt(i); + if (child->_group != this) + continue; + + if (_excludeInvisibles && !child->internalVisible3()) + { + child->setSize(child->_rawSize.width, child->_rawSize.height + dh, true); + continue; + } + + if (!priorHandled || i != _mainChildIndex) + { + child->setSize(round(child->_sizePercentInGroup / remainPercent * remainSize), child->_rawSize.height + dh, true); + remainPercent -= child->_sizePercentInGroup; + remainSize -= child->getWidth(); + } + + child->setXMin(curX); + if (child->getWidth() != 0) + curX += child->getWidth() + _columnGap; + } + } + else + { + remainSize = getHeight() - (_numChildren - 1) * _lineGap; + if (_mainChildIndex != -1 && remainSize >= _totalSize) + { + GObject* child = _parent->getChildAt(_mainChildIndex); + child->setSize(child->_rawSize.width + dw, remainSize - (_totalSize - _mainGridMinSize), true); + remainSize -= child->getHeight(); + remainPercent -= child->_sizePercentInGroup; + priorHandled = true; + } + + float curY = getY(); + for (int i = 0; i < cnt; i++) + { + GObject* child = _parent->getChildAt(i); + if (child->_group != this) + continue; + + if (_excludeInvisibles && !child->internalVisible3()) + { + child->setSize(child->_rawSize.width + dw, child->_rawSize.height, true); + continue; + } + + if (!priorHandled || i != _mainChildIndex) + { + child->setSize(child->_rawSize.width + dw, round(child->_sizePercentInGroup / remainPercent * remainSize), true); + remainPercent -= child->_sizePercentInGroup; + remainSize -= child->getHeight(); + } + + child->setYMin(curY); + if (child->getHeight() != 0) + curY += child->getHeight() + _lineGap; + } + } + + _updating &= 1; +} + +void GGroup::handleAlphaChanged() +{ + GObject::handleAlphaChanged(); + + if (_underConstruct) + return; + + int cnt = _parent->numChildren(); + for (int i = 0; i < cnt; i++) + { + GObject* child = _parent->getChildAt(i); + if (child->_group == this) + child->setAlpha(_alpha); + } +} + +void GGroup::handleVisibleChanged() +{ + if (!_parent) + return; + + int cnt = _parent->numChildren(); + for (int i = 0; i < cnt; i++) + { + GObject* child = _parent->getChildAt(i); + if (child->_group == this) + child->handleVisibleChanged(); + } +} + +void GGroup::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 5); + + _layout = (GroupLayoutType)buffer->readByte(); + _lineGap = buffer->readInt(); + _columnGap = buffer->readInt(); + if (buffer->version >= 2) + { + _excludeInvisibles = buffer->readBool(); + _autoSizeDisabled = buffer->readBool(); + _mainChildIndex = buffer->readShort(); + } +} + +void GGroup::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_afterAdd(buffer, beginPos); + + if (!_visible) + handleVisibleChanged(); +} + +NS_FGUI_END diff --git a/extensions/fairygui/GGroup.h b/extensions/fairygui/GGroup.h new file mode 100644 index 0000000000..c632881cd6 --- /dev/null +++ b/extensions/fairygui/GGroup.h @@ -0,0 +1,74 @@ +#ifndef __GGROUP_H__ +#define __GGROUP_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GObject.h" + +NS_FGUI_BEGIN + +class GGroup : public GObject +{ +public: + GGroup(); + virtual ~GGroup(); + + CREATE_FUNC(GGroup); + + GroupLayoutType getLayout() { return _layout; } + void setLayout(GroupLayoutType value); + + int getColumnGap() { return _columnGap; } + void setColumnGap(int value); + + int getLineGap() { return _lineGap; } + void setLineGap(int value); + + bool isExcludeInvisibles() { return _excludeInvisibles; } + void setExcludeInvisibles(bool value); + + bool isAutoSizeDisabled() { return _autoSizeDisabled; } + void setAutoSizeDisabled(bool value); + + int getMainGridIndex() { return _mainGridIndex; } + void setMainGridIndex(int value); + + int getMainGridMinSize() { return _mainGridMinSize; } + void setMainGridMinSize(int value); + + void setBoundsChangedFlag(bool positionChangedOnly = false); + void moveChildren(float dx, float dy); + void resizeChildren(float dw, float dh); + + int _updating; + +protected: + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + virtual void handleAlphaChanged() override; + virtual void handleVisibleChanged() override; + +private: + void updateBounds(); + void handleLayout(); + CALL_LATER_FUNC(GGroup, ensureBoundsCorrect); + + GroupLayoutType _layout; + int _lineGap; + int _columnGap; + bool _excludeInvisibles; + bool _autoSizeDisabled; + int _mainGridIndex; + int _mainGridMinSize; + + bool _percentReady; + bool _boundsChanged; + int _mainChildIndex; + float _totalSize; + int _numChildren; + +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GImage.cpp b/extensions/fairygui/GImage.cpp new file mode 100644 index 0000000000..85533c8673 --- /dev/null +++ b/extensions/fairygui/GImage.cpp @@ -0,0 +1,164 @@ +#include "GImage.h" +#include "PackageItem.h" +#include "display/FUISprite.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GImage::GImage() : _content(nullptr) +{ + _touchDisabled = true; +} + +GImage::~GImage() +{ +} + +void GImage::handleInit() +{ + _content = FUISprite::create(); + _content->retain(); + + _displayObject = _content; +} + +FlipType GImage::getFlip() const +{ + if (_content->isFlippedX() && _content->isFlippedY()) + return FlipType::BOTH; + else if (_content->isFlippedX()) + return FlipType::HORIZONTAL; + else if (_content->isFlippedY()) + return FlipType::VERTICAL; + else + return FlipType::NONE; +} + +void GImage::setFlip(FlipType value) +{ + _content->setFlippedX(value == FlipType::HORIZONTAL || value == FlipType::BOTH); + _content->setFlippedY(value == FlipType::VERTICAL || value == FlipType::BOTH); +} + +void GImage::handleGrayedChanged() +{ + GObject::handleGrayedChanged(); + + ((FUISprite*)_content)->setGrayed(_finalGrayed); +} + +cocos2d::Color3B GImage::getColor() const +{ + return _content->getColor(); +} + +void GImage::setColor(const cocos2d::Color3B& value) +{ + _content->setColor(value); +} + +FillMethod GImage::getFillMethod() const +{ + return _content->getFillMethod(); +} + +void GImage::setFillMethod(FillMethod value) +{ + _content->setFillMethod(value); +} + +FillOrigin GImage::getFillOrigin() const +{ + return _content->getFillOrigin(); +} + +void GImage::setFillOrigin(FillOrigin value) +{ + _content->setFillOrigin(value); +} + +bool GImage::isFillClockwise() const +{ + return _content->isFillClockwise(); +} + +void GImage::setFillClockwise(bool value) +{ + _content->setFillClockwise(value); +} + +float GImage::getFillAmount() const +{ + return _content->getFillAmount(); +} + +void GImage::setFillAmount(float value) +{ + _content->setFillAmount(value); +} + +cocos2d::Value GImage::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getColor())); + default: + return GObject::getProp(propId); + } +} + +void GImage::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + default: + GObject::setProp(propId, value); + break; + } +} + +void GImage::constructFromResource() +{ + PackageItem* contentItem = _packageItem->getBranch(); + sourceSize.width = contentItem->width; + sourceSize.height = contentItem->height; + initSize = sourceSize; + + contentItem = contentItem->getHighResolution(); + contentItem->load(); + + _content->setSpriteFrame(contentItem->spriteFrame); + if (contentItem->scale9Grid) + ((FUISprite*)_content)->setScale9Grid(contentItem->scale9Grid); + else if (contentItem->scaleByTile) + ((FUISprite*)_content)->setScaleByTile(true); + + setSize(sourceSize.width, sourceSize.height); +} + +void GImage::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 5); + + if (buffer->readBool()) + setColor((Color3B)buffer->readColor()); + setFlip((FlipType)buffer->readByte()); + int fillMethod = buffer->readByte(); + if (fillMethod != 0) + { + _content->setFillMethod((FillMethod)fillMethod); + _content->setFillOrigin((FillOrigin)buffer->readByte()); + _content->setFillClockwise(buffer->readBool()); + _content->setFillAmount(buffer->readFloat()); + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/GImage.h b/extensions/fairygui/GImage.h new file mode 100644 index 0000000000..354b23df3c --- /dev/null +++ b/extensions/fairygui/GImage.h @@ -0,0 +1,55 @@ +#ifndef __GIMAGE_H__ +#define __GIMAGE_H__ + +#include "FairyGUIMacros.h" +#include "GObject.h" +#include "cocos2d.h" +#include "ui/UIScale9Sprite.h" + +NS_FGUI_BEGIN + +class FUISprite; + +class GImage : public GObject +{ +public: + GImage(); + virtual ~GImage(); + + CREATE_FUNC(GImage); + + FlipType getFlip() const; + void setFlip(FlipType value); + + cocos2d::Color3B getColor() const; + void setColor(const cocos2d::Color3B& value); + + FillMethod getFillMethod() const; + void setFillMethod(FillMethod value); + + FillOrigin getFillOrigin() const; + void setFillOrigin(FillOrigin value); + + bool isFillClockwise() const; + void setFillClockwise(bool value); + + float getFillAmount() const; + void setFillAmount(float value); + + virtual void constructFromResource() override; + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void handleInit() override; + virtual void handleGrayedChanged() override; + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + +private: + FUISprite* _content; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GLabel.cpp b/extensions/fairygui/GLabel.cpp new file mode 100644 index 0000000000..c52385696a --- /dev/null +++ b/extensions/fairygui/GLabel.cpp @@ -0,0 +1,190 @@ +#include "GLabel.h" +#include "GButton.h" +#include "GTextField.h" +#include "GTextInput.h" +#include "PackageItem.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GLabel::GLabel() : + _titleObject(nullptr), + _iconObject(nullptr) +{ +} +GLabel::~GLabel() +{ +} + +const std::string& GLabel::getText() const +{ + if (_titleObject != nullptr) + return _titleObject->getText(); + else + return STD_STRING_EMPTY; +} + +void GLabel::setText(const std::string & value) +{ + if (_titleObject != nullptr) + _titleObject->setText(value); + updateGear(6); +} + +const std::string& GLabel::getIcon() const +{ + if (_iconObject != nullptr) + return _iconObject->getIcon(); + else + return STD_STRING_EMPTY; +} + +void GLabel::setIcon(const std::string & value) +{ + if (_iconObject != nullptr) + _iconObject->setIcon(value); + updateGear(7); +} + +cocos2d::Color3B GLabel::getTitleColor() const +{ + GTextField* tf = getTextField(); + if (tf) + return tf->getColor(); + else + return Color3B::BLACK; +} + +void GLabel::setTitleColor(const cocos2d::Color3B & value) +{ + GTextField* tf = getTextField(); + if (tf) + tf->setColor(value); +} + +int GLabel::getTitleFontSize() const +{ + GTextField* tf = getTextField(); + if (tf) + return tf->getFontSize(); + else + return 0; +} + +void GLabel::setTitleFontSize(int value) +{ + GTextField* tf = getTextField(); + if (tf) + tf->setFontSize(value); +} + +GTextField * GLabel::getTextField() const +{ + if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject); + else if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject)->getTextField(); + else if (dynamic_cast(_titleObject)) + return dynamic_cast(_titleObject)->getTextField(); + else + return nullptr; +} + +cocos2d::Value GLabel::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getTitleColor())); + case ObjectPropID::OutlineColor: + { + GTextField* tf = getTextField(); + if (tf != nullptr) + return Value(ToolSet::colorToInt(tf->getOutlineColor())); + else + return Value::Null; + } + case ObjectPropID::FontSize: + return Value(getTitleFontSize()); + default: + return GComponent::getProp(propId); + } +} + +void GLabel::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setTitleColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + case ObjectPropID::OutlineColor: + { + GTextField* tf = getTextField(); + if (tf != nullptr) + tf->setOutlineColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + } + case ObjectPropID::FontSize: + setTitleFontSize(value.asInt()); + break; + default: + GComponent::setProp(propId, value); + break; + } +} + +void GLabel::constructExtension(ByteBuffer* buffer) +{ + _titleObject = getChild("title"); + _iconObject = getChild("icon"); +} + +void GLabel::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GComponent::setup_afterAdd(buffer, beginPos); + + if (!buffer->seek(beginPos, 6)) + return; + + if ((ObjectType)buffer->readByte() != _packageItem->objectType) + return; + + const std::string* str; + + if ((str = buffer->readSP())) + setTitle(*str); + if ((str = buffer->readSP())) + setIcon(*str); + if (buffer->readBool()) + setTitleColor((Color3B)buffer->readColor()); + int iv = buffer->readInt(); + if (iv != 0) + setTitleFontSize(iv); + + if (buffer->readBool()) + { + GTextInput* input = dynamic_cast(getTextField()); + if (input) + { + if ((str = buffer->readSP())) + input->setPrompt(*str); + if ((str = buffer->readSP())) + input->setRestrict(*str); + iv = buffer->readInt(); + if (iv != 0) + input->setMaxLength(iv); + iv = buffer->readInt(); + if (iv != 0) + input->setKeyboardType(iv); + if (buffer->readBool()) + input->setPassword(true); + } + else + buffer->skip(13); + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/GLabel.h b/extensions/fairygui/GLabel.h new file mode 100644 index 0000000000..87542cb555 --- /dev/null +++ b/extensions/fairygui/GLabel.h @@ -0,0 +1,51 @@ +#ifndef __GLABEL_H__ +#define __GLABEL_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GComponent.h" + +NS_FGUI_BEGIN + +class GTextField; + +class GLabel : public GComponent +{ +public: + GLabel(); + virtual ~GLabel(); + + CREATE_FUNC(GLabel); + + const std::string& getTitle() { return getText(); } + void setTitle(const std::string& value) { setText(value); }; + + virtual const std::string& getText() const override; + virtual void setText(const std::string& value) override; + + virtual const std::string& getIcon() const override; + virtual void setIcon(const std::string& value) override; + + cocos2d::Color3B getTitleColor() const; + void setTitleColor(const cocos2d::Color3B& value); + + int getTitleFontSize() const; + void setTitleFontSize(int value); + + GTextField* getTextField() const; + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void constructExtension(ByteBuffer* buffer) override; + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + +private: + GObject* _titleObject; + GObject* _iconObject; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GList.cpp b/extensions/fairygui/GList.cpp new file mode 100644 index 0000000000..191e9b76f8 --- /dev/null +++ b/extensions/fairygui/GList.cpp @@ -0,0 +1,2558 @@ +#include "GList.h" +#include "GButton.h" +#include "GScrollBar.h" +#include "UIConfig.h" +#include "UIPackage.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +using namespace std; + +GList::ItemInfo::ItemInfo() +{ + obj = nullptr; + updateFlag = 0; + selected = false; +} + +GList::GList() : foldInvisibleItems(false), + _selectionMode(ListSelectionMode::SINGLE), + scrollItemToViewOnClick(true), + _layout(ListLayoutType::SINGLE_COLUMN), + _lineCount(0), + _columnCount(0), + _lineGap(0), + _columnGap(0), + _align(TextHAlignment::LEFT), + _verticalAlign(TextVAlignment::TOP), + _autoResizeItem(true), + _selectionController(nullptr), + _pool(nullptr), + _selectionHandled(false), + _lastSelectedIndex(-1), + _virtual(false), + _loop(0), + _numItems(0), + _realNumItems(0), + _firstIndex(-1), + _virtualListChanged(false), + _eventLocked(false), + _itemInfoVer(0) +{ + _trackBounds = true; + setOpaque(true); + _pool = new GObjectPool(); +} + +GList::~GList() +{ + delete _pool; + if (_virtualListChanged != 0) + CALL_LATER_CANCEL(GList, doRefreshVirtualList); + + _selectionController = nullptr; + scrollItemToViewOnClick = false; +} + +void GList::setLayout(ListLayoutType value) +{ + if (_layout != value) + { + _layout = value; + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); + } +} + +void GList::setLineCount(int value) +{ + if (_lineCount != value) + { + _lineCount = value; + if (_layout == ListLayoutType::FLOW_VERTICAL || _layout == ListLayoutType::PAGINATION) + { + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); + } + } +} + +void GList::setColumnCount(int value) +{ + if (_columnCount != value) + { + _columnCount = value; + if (_layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) + { + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); + } + } +} + +void GList::setLineGap(int value) +{ + if (_lineGap != value) + { + _lineGap = value; + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); + } +} + +void GList::setColumnGap(int value) +{ + if (_columnGap != value) + { + _columnGap = value; + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); + } +} + +void GList::setAlign(cocos2d::TextHAlignment value) +{ + if (_align != value) + { + _align = value; + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); + } +} + +void GList::setVerticalAlign(cocos2d::TextVAlignment value) +{ + if (_verticalAlign != value) + { + _verticalAlign = value; + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); + } +} + +void GList::setAutoResizeItem(bool value) +{ + if (_autoResizeItem != value) + { + _autoResizeItem = value; + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); + } +} + +GObject* GList::getFromPool(const std::string& url) +{ + GObject* ret; + if (url.length() == 0) + ret = _pool->getObject(_defaultItem); + else + ret = _pool->getObject(url); + if (ret != nullptr) + ret->setVisible(true); + return ret; +} + +void GList::returnToPool(GObject* obj) +{ + _pool->returnObject(obj); +} + +GObject* GList::addItemFromPool(const std::string& url) +{ + GObject* obj = getFromPool(url); + + return addChild(obj); +} + +GObject* GList::addChildAt(GObject* child, int index) +{ + GComponent::addChildAt(child, index); + if (dynamic_cast(child)) + { + GButton* button = (GButton*)child; + button->setSelected(false); + button->setChangeStateOnClick(false); + } + + child->addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GList::onItemTouchBegin, this), EventTag(this)); + child->addClickListener(CC_CALLBACK_1(GList::onClickItem, this), EventTag(this)); + child->addEventListener(UIEventType::RightClick, CC_CALLBACK_1(GList::onClickItem, this), EventTag(this)); + + return child; +} + +void GList::removeChildAt(int index) +{ + GObject* child = _children.at(index); + child->removeClickListener(EventTag(this)); + child->removeEventListener(UIEventType::TouchBegin, EventTag(this)); + child->removeEventListener(UIEventType::RightClick, EventTag(this)); + + GComponent::removeChildAt(index); +} + +void GList::removeChildToPoolAt(int index) +{ + returnToPool(getChildAt(index)); + removeChildAt(index); +} + +void GList::removeChildToPool(GObject* child) +{ + returnToPool(child); + removeChild(child); +} + +void GList::removeChildrenToPool() +{ + removeChildrenToPool(0, -1); +} + +void GList::removeChildrenToPool(int beginIndex, int endIndex) +{ + if (endIndex < 0 || endIndex >= _children.size()) + endIndex = (int)_children.size() - 1; + + for (int i = beginIndex; i <= endIndex; ++i) + removeChildToPoolAt(beginIndex); +} + +int GList::getSelectedIndex() const +{ + if (_virtual) + { + int cnt = _realNumItems; + for (int i = 0; i < cnt; i++) + { + const ItemInfo& ii = _virtualItems[i]; + if ((dynamic_cast(ii.obj) != nullptr && ((GButton*)ii.obj)->isSelected()) || (ii.obj == nullptr && ii.selected)) + { + if (_loop) + return i % _numItems; + else + return i; + } + } + } + else + { + int cnt = (int)_children.size(); + for (int i = 0; i < cnt; i++) + { + GButton* obj = _children.at(i)->as(); + if (obj != nullptr && obj->isSelected()) + return i; + } + } + return -1; +} + +void GList::setSelectedIndex(int value) +{ + if (value >= 0 && value < getNumItems()) + { + if (_selectionMode != ListSelectionMode::SINGLE) + clearSelection(); + addSelection(value, false); + } + else + clearSelection(); +} + +void GList::setSelectionController(GController* value) +{ + _selectionController = value; +} + +void GList::getSelection(std::vector& result) const +{ + result.clear(); + if (_virtual) + { + int cnt = _realNumItems; + for (int i = 0; i < cnt; i++) + { + const ItemInfo& ii = _virtualItems[i]; + if ((dynamic_cast(ii.obj) != nullptr && ((GButton*)ii.obj)->isSelected()) || (ii.obj == nullptr && ii.selected)) + { + int j = i; + if (_loop) + { + j = i % _numItems; + if (std::find(result.cbegin(), result.cend(), j) != result.cend()) + continue; + } + result.push_back(j); + } + } + } + else + { + int cnt = (int)_children.size(); + for (int i = 0; i < cnt; i++) + { + GButton* obj = _children.at(i)->as(); + if (obj != nullptr && obj->isSelected()) + result.push_back(i); + } + } +} + +void GList::addSelection(int index, bool scrollItToView) +{ + if (_selectionMode == ListSelectionMode::NONE) + return; + + checkVirtualList(); + + if (_selectionMode == ListSelectionMode::SINGLE) + clearSelection(); + + if (scrollItToView) + scrollToView(index); + + _lastSelectedIndex = index; + GButton* obj = nullptr; + if (_virtual) + { + ItemInfo& ii = _virtualItems[index]; + if (ii.obj != nullptr) + obj = ii.obj->as(); + ii.selected = true; + } + else + obj = getChildAt(index)->as(); + + if (obj != nullptr && !obj->isSelected()) + { + obj->setSelected(true); + updateSelectionController(index); + } +} + +void GList::removeSelection(int index) +{ + if (_selectionMode == ListSelectionMode::NONE) + return; + + GButton* obj = nullptr; + if (_virtual) + { + ItemInfo& ii = _virtualItems[index]; + if (ii.obj != nullptr) + obj = ii.obj->as(); + ii.selected = false; + } + else + obj = getChildAt(index)->as(); + + if (obj != nullptr) + obj->setSelected(false); +} + +void GList::clearSelection() +{ + if (_virtual) + { + int cnt = _realNumItems; + for (int i = 0; i < cnt; i++) + { + ItemInfo& ii = _virtualItems[i]; + if (dynamic_cast(ii.obj)) + ((GButton*)ii.obj)->setSelected(false); + ii.selected = false; + } + } + else + { + int cnt = (int)_children.size(); + for (int i = 0; i < cnt; i++) + { + GButton* obj = _children.at(i)->as(); + if (obj != nullptr) + obj->setSelected(false); + } + } +} + +void GList::clearSelectionExcept(GObject* g) +{ + if (_virtual) + { + int cnt = _realNumItems; + for (int i = 0; i < cnt; i++) + { + ItemInfo& ii = _virtualItems[i]; + if (ii.obj != g) + { + if (dynamic_cast(ii.obj)) + ((GButton*)ii.obj)->setSelected(false); + ii.selected = false; + } + } + } + else + { + int cnt = (int)_children.size(); + for (int i = 0; i < cnt; i++) + { + GButton* obj = _children.at(i)->as(); + if (obj != nullptr && obj != g) + obj->setSelected(false); + } + } +} + +void GList::selectAll() +{ + checkVirtualList(); + + int last = -1; + if (_virtual) + { + int cnt = _realNumItems; + for (int i = 0; i < cnt; i++) + { + ItemInfo& ii = _virtualItems[i]; + if (dynamic_cast(ii.obj) && !((GButton*)ii.obj)->isSelected()) + { + ((GButton*)ii.obj)->setSelected(true); + last = i; + } + ii.selected = true; + } + } + else + { + int cnt = (int)_children.size(); + for (int i = 0; i < cnt; i++) + { + GButton* obj = _children.at(i)->as(); + if (obj != nullptr && !obj->isSelected()) + { + obj->setSelected(true); + last = i; + } + } + } + + if (last != -1) + updateSelectionController(last); +} + +void GList::selectReverse() +{ + checkVirtualList(); + + int last = -1; + if (_virtual) + { + int cnt = _realNumItems; + for (int i = 0; i < cnt; i++) + { + ItemInfo& ii = _virtualItems[i]; + if (dynamic_cast(ii.obj)) + { + ((GButton*)ii.obj)->setSelected(!((GButton*)ii.obj)->isSelected()); + if (((GButton*)ii.obj)->isSelected()) + last = i; + } + ii.selected = !ii.selected; + } + } + else + { + int cnt = (int)_children.size(); + for (int i = 0; i < cnt; i++) + { + GButton* obj = _children.at(i)->as(); + if (obj != nullptr) + { + obj->setSelected(!obj->isSelected()); + if (obj->isSelected()) + last = i; + } + } + } + + if (last != -1) + updateSelectionController(last); +} + +void GList::handleArrowKey(int dir) +{ + int index = getSelectedIndex(); + if (index == -1) + return; + + switch (dir) + { + case 1: //up + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_VERTICAL) + { + index--; + if (index >= 0) + { + clearSelection(); + addSelection(index, true); + } + } + else if (_layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) + { + GObject* current = _children.at(index); + int k = 0; + int i; + for (i = index - 1; i >= 0; i--) + { + GObject* obj = _children.at(i); + if (obj->getY() != current->getY()) + { + current = obj; + break; + } + k++; + } + for (; i >= 0; i--) + { + GObject* obj = _children.at(i); + if (obj->getY() != current->getY()) + { + clearSelection(); + addSelection(i + k + 1, true); + break; + } + } + } + break; + + case 3: //right + if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) + { + index++; + if (index < _children.size()) + { + clearSelection(); + addSelection(index, true); + } + } + else if (_layout == ListLayoutType::FLOW_VERTICAL) + { + GObject* current = _children.at(index); + int k = 0; + int cnt = (int)_children.size(); + int i; + for (i = index + 1; i < cnt; i++) + { + GObject* obj = _children.at(i); + if (obj->getX() != current->getX()) + { + current = obj; + break; + } + k++; + } + for (; i < cnt; i++) + { + GObject* obj = _children.at(i); + if (obj->getX() != current->getX()) + { + clearSelection(); + addSelection(i - k - 1, true); + break; + } + } + } + break; + + case 5: //down + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_VERTICAL) + { + index++; + if (index < _children.size()) + { + clearSelection(); + addSelection(index, true); + } + } + else if (_layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) + { + GObject* current = _children.at(index); + int k = 0; + int cnt = (int)_children.size(); + int i; + for (i = index + 1; i < cnt; i++) + { + GObject* obj = _children.at(i); + if (obj->getY() != current->getY()) + { + current = obj; + break; + } + k++; + } + for (; i < cnt; i++) + { + GObject* obj = _children.at(i); + if (obj->getY() != current->getY()) + { + clearSelection(); + addSelection(i - k - 1, true); + break; + } + } + } + break; + + case 7: //left + if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_HORIZONTAL || _layout == ListLayoutType::PAGINATION) + { + index--; + if (index >= 0) + { + clearSelection(); + addSelection(index, true); + } + } + else if (_layout == ListLayoutType::FLOW_VERTICAL) + { + GObject* current = _children.at(index); + int k = 0; + int i; + for (i = index - 1; i >= 0; i--) + { + GObject* obj = _children.at(i); + if (obj->getX() != current->getX()) + { + current = obj; + break; + } + k++; + } + for (; i >= 0; i--) + { + GObject* obj = _children.at(i); + if (obj->getX() != current->getX()) + { + clearSelection(); + addSelection(i + k + 1, true); + break; + } + } + } + break; + } +} + +void GList::onItemTouchBegin(EventContext* context) +{ + GButton* item = (GButton*)context->getSender(); + if (_selectionMode == ListSelectionMode::NONE) + return; + + _selectionHandled = false; + + if (UIConfig::defaultScrollTouchEffect && (_scrollPane != nullptr || (_parent != nullptr && _parent->getScrollPane() != nullptr))) + return; + + if (_selectionMode == ListSelectionMode::SINGLE) + { + setSelectionOnEvent(item, context->getInput()); + } + else + { + if (!item->isSelected()) + setSelectionOnEvent(item, context->getInput()); + } +} + +void GList::onClickItem(EventContext* context) +{ + GButton* item = (GButton*)context->getSender(); + if (!_selectionHandled) + setSelectionOnEvent(item, context->getInput()); + _selectionHandled = false; + + if (_scrollPane != nullptr && scrollItemToViewOnClick) + _scrollPane->scrollToView(item, true); + + dispatchItemEvent(item, context); +} + +void GList::dispatchItemEvent(GObject* item, EventContext* context) +{ + dispatchEvent(context->getType() == UIEventType::Click ? UIEventType::ClickItem : UIEventType::RightClickItem, item); +} + +void GList::setSelectionOnEvent(GObject* item, InputEvent* evt) +{ + if (!(dynamic_cast(item)) || _selectionMode == ListSelectionMode::NONE) + return; + + _selectionHandled = true; + bool dontChangeLastIndex = false; + GButton* button = (GButton*)item; + int index = childIndexToItemIndex(getChildIndex(item)); + + if (_selectionMode == ListSelectionMode::SINGLE) + { + if (!button->isSelected()) + { + clearSelectionExcept(button); + button->setSelected(true); + } + } + else + { + if (evt->isShiftDown()) + { + if (!button->isSelected()) + { + if (_lastSelectedIndex != -1) + { + int min = MIN(_lastSelectedIndex, index); + int max = MAX(_lastSelectedIndex, index); + max = MIN(max, getNumItems() - 1); + if (_virtual) + { + for (int i = min; i <= max; i++) + { + ItemInfo& ii = _virtualItems[i]; + if (dynamic_cast(ii.obj)) + ((GButton*)ii.obj)->setSelected(true); + ii.selected = true; + } + } + else + { + for (int i = min; i <= max; i++) + { + GButton* obj = getChildAt(i)->as(); + if (obj != nullptr && !obj->isSelected()) + obj->setSelected(true); + } + } + + dontChangeLastIndex = true; + } + else + { + button->setSelected(true); + } + } + } + else if (evt->isCtrlDown() || _selectionMode == ListSelectionMode::MULTIPLE_SINGLECLICK) + { + button->setSelected(!button->isSelected()); + } + else + { + if (!button->isSelected()) + { + clearSelectionExcept(button); + button->setSelected(true); + } + else + clearSelectionExcept(button); + } + } + + if (!dontChangeLastIndex) + _lastSelectedIndex = index; + + if (button->isSelected()) + updateSelectionController(index); +} + +void GList::resizeToFit(int itemCount, int minSize) +{ + ensureBoundsCorrect(); + + int curCount = getNumItems(); + if (itemCount > curCount) + itemCount = curCount; + + if (_virtual) + { + int lineCount = ceil((float)itemCount / _curLineItemCount); + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + setViewHeight(lineCount * _itemSize.y + MAX(0, lineCount - 1) * _lineGap); + else + setViewWidth(lineCount * _itemSize.x + MAX(0, lineCount - 1) * _columnGap); + } + else if (itemCount == 0) + { + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + setViewHeight(minSize); + else + setViewWidth(minSize); + } + else + { + int i = itemCount - 1; + GObject* obj = nullptr; + while (i >= 0) + { + obj = getChildAt(i); + if (!foldInvisibleItems || obj->isVisible()) + break; + i--; + } + if (i < 0) + { + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + setViewHeight(minSize); + else + setViewWidth(minSize); + } + else + { + float size; + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + { + size = obj->getY() + obj->getHeight(); + if (size < minSize) + size = minSize; + setViewHeight(size); + } + else + { + size = obj->getX() + obj->getWidth(); + if (size < minSize) + size = minSize; + setViewWidth(size); + } + } + } +} + +int GList::getFirstChildInView() +{ + return childIndexToItemIndex(GComponent::getFirstChildInView()); +} + +void GList::handleSizeChanged() +{ + GComponent::handleSizeChanged(); + + setBoundsChangedFlag(); + if (_virtual) + setVirtualListChangedFlag(true); +} + +void GList::handleControllerChanged(GController* c) +{ + GComponent::handleControllerChanged(c); + + if (_selectionController == c) + setSelectedIndex(c->getSelectedIndex()); +} + +void GList::updateSelectionController(int index) +{ + if (_selectionController != nullptr && !_selectionController->changing && index < _selectionController->getPageCount()) + { + GController* c = _selectionController; + _selectionController = nullptr; + c->setSelectedIndex(index); + _selectionController = c; + } +} + +void GList::scrollToView(int index, bool ani, bool setFirst) +{ + if (_virtual) + { + if (_numItems == 0) + return; + + checkVirtualList(); + + CCASSERT(index >= 0 && index < (int)_virtualItems.size(), "Invalid child index"); + + if (_loop) + index = floor(_firstIndex / _numItems) * _numItems + index; + + Rect rect; + ItemInfo& ii = _virtualItems[index]; + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + { + float pos = 0; + for (int i = _curLineItemCount - 1; i < index; i += _curLineItemCount) + pos += _virtualItems[i].size.y + _lineGap; + rect.setRect(0, pos, _itemSize.x, ii.size.y); + } + else if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_VERTICAL) + { + float pos = 0; + for (int i = _curLineItemCount - 1; i < index; i += _curLineItemCount) + pos += _virtualItems[i].size.x + _columnGap; + rect.setRect(pos, 0, ii.size.x, _itemSize.y); + } + else + { + int page = index / (_curLineItemCount * _curLineItemCount2); + rect.setRect(page * getViewWidth() + (index % _curLineItemCount) * (ii.size.x + _columnGap), + (index / _curLineItemCount) % _curLineItemCount2 * (ii.size.y + _lineGap), + ii.size.x, ii.size.y); + } + + setFirst = true; + if (_scrollPane != nullptr) + _scrollPane->scrollToView(rect, ani, setFirst); + else if (_parent != nullptr && _parent->getScrollPane() != nullptr) + _parent->getScrollPane()->scrollToView(transformRect(rect, _parent), ani, setFirst); + } + else + { + GObject* obj = getChildAt(index); + if (_scrollPane != nullptr) + _scrollPane->scrollToView(obj, ani, setFirst); + else if (_parent != nullptr && _parent->getScrollPane() != nullptr) + _parent->getScrollPane()->scrollToView(obj, ani, setFirst); + } +} + +int GList::childIndexToItemIndex(int index) +{ + if (!_virtual) + return index; + + if (_layout == ListLayoutType::PAGINATION) + { + for (int i = _firstIndex; i < _realNumItems; i++) + { + if (_virtualItems[i].obj != nullptr) + { + index--; + if (index < 0) + return i; + } + } + + return index; + } + else + { + index += _firstIndex; + if (_loop && _numItems > 0) + index = index % _numItems; + + return index; + } +} + +int GList::itemIndexToChildIndex(int index) +{ + if (!_virtual) + return index; + + if (_layout == ListLayoutType::PAGINATION) + { + return getChildIndex(_virtualItems[index].obj); + } + else + { + if (_loop && _numItems > 0) + { + int j = _firstIndex % _numItems; + if (index >= j) + index = index - j; + else + index = _numItems - j + index; + } + else + index -= _firstIndex; + + return index; + } +} + +void GList::setVirtual() +{ + setVirtual(false); +} + +void GList::setVirtualAndLoop() +{ + setVirtual(true); +} + +void GList::setVirtual(bool loop) +{ + if (!_virtual) + { + CCASSERT(_scrollPane != nullptr, "FairyGUI: Virtual list must be scrollable!"); + + if (loop) + { + CCASSERT(_layout != ListLayoutType::FLOW_HORIZONTAL && _layout != ListLayoutType::FLOW_VERTICAL, + "FairyGUI: Loop list is not supported for FlowHorizontal or FlowVertical layout!"); + + _scrollPane->setBouncebackEffect(false); + } + + _virtual = true; + _loop = loop; + removeChildrenToPool(); + + if (_itemSize.x == 0 || _itemSize.y == 0) + { + GObject* obj = getFromPool(); + CCASSERT(obj != nullptr, "FairyGUI: Virtual List must have a default list item resource."); + _itemSize = obj->getSize(); + _itemSize.x = ceil(_itemSize.x); + _itemSize.y = ceil(_itemSize.y); + returnToPool(obj); + } + + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + { + _scrollPane->setScrollStep(_itemSize.y); + if (_loop) + _scrollPane->_loop = 2; + } + else + { + _scrollPane->setScrollStep(_itemSize.x); + if (_loop) + _scrollPane->_loop = 1; + } + + addEventListener(UIEventType::Scroll, CC_CALLBACK_1(GList::onScroll, this)); + setVirtualListChangedFlag(true); + } +} + +int GList::getNumItems() +{ + if (_virtual) + return _numItems; + else + return (int)_children.size(); +} + +void GList::setNumItems(int value) +{ + if (_virtual) + { + CCASSERT(itemRenderer != nullptr, "FairyGUI: Set itemRenderer first!"); + + _numItems = value; + if (_loop) + _realNumItems = _numItems * 6; + else + _realNumItems = _numItems; + + int oldCount = (int)_virtualItems.size(); + if (_realNumItems > oldCount) + { + for (int i = oldCount; i < _realNumItems; i++) + { + ItemInfo ii; + ii.size = _itemSize; + + _virtualItems.push_back(ii); + } + } + else + { + for (int i = _realNumItems; i < oldCount; i++) + _virtualItems[i].selected = false; + } + + if (_virtualListChanged != 0) + CALL_LATER_CANCEL(GList, doRefreshVirtualList); + + //����ˢ�� + doRefreshVirtualList(); + } + else + { + int cnt = (int)_children.size(); + if (value > cnt) + { + for (int i = cnt; i < value; i++) + { + if (itemProvider == nullptr) + addItemFromPool(); + else + addItemFromPool(itemProvider(i)); + } + } + else + { + removeChildrenToPool(value, cnt); + } + + if (itemRenderer != nullptr) + { + for (int i = 0; i < value; i++) + itemRenderer(i, getChildAt(i)); + } + } +} + +void GList::refreshVirtualList() +{ + CCASSERT(_virtual, "FairyGUI: not virtual list"); + + setVirtualListChangedFlag(false); +} + +cocos2d::Vec2 GList::getSnappingPosition(const cocos2d::Vec2& pt) +{ + if (_virtual) + { + Vec2 ret = pt; + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + { + int index = getIndexOnPos1(ret.y, false); + if (index < (int)_virtualItems.size() && pt.y - ret.y > _virtualItems[index].size.y / 2 && index < _realNumItems) + ret.y += _virtualItems[index].size.y + _lineGap; + } + else if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_VERTICAL) + { + int index = getIndexOnPos2(ret.x, false); + if (index < (int)_virtualItems.size() && pt.x - ret.x > _virtualItems[index].size.x / 2 && index < _realNumItems) + ret.x += _virtualItems[index].size.x + _columnGap; + } + else + { + int index = getIndexOnPos3(ret.x, false); + if (index < (int)_virtualItems.size() && pt.x - ret.x > _virtualItems[index].size.x / 2 && index < _realNumItems) + ret.x += _virtualItems[index].size.x + _columnGap; + } + + return ret; + } + else + return GComponent::getSnappingPosition(pt); +} + +void GList::checkVirtualList() +{ + if (_virtualListChanged != 0) + { + doRefreshVirtualList(); + CALL_LATER_CANCEL(GList, doRefreshVirtualList); + } +} + +void GList::setVirtualListChangedFlag(bool layoutChanged) +{ + if (layoutChanged) + _virtualListChanged = 2; + else if (_virtualListChanged == 0) + _virtualListChanged = 1; + + CALL_LATER(GList, doRefreshVirtualList); +} + +void GList::doRefreshVirtualList() +{ + bool layoutChanged = _virtualListChanged == 2; + _virtualListChanged = 0; + _eventLocked = true; + + if (layoutChanged) + { + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::SINGLE_ROW) + _curLineItemCount = 1; + else if (_layout == ListLayoutType::FLOW_HORIZONTAL) + { + if (_columnCount > 0) + _curLineItemCount = _columnCount; + else + { + _curLineItemCount = floor((_scrollPane->getViewSize().width + _columnGap) / (_itemSize.x + _columnGap)); + if (_curLineItemCount <= 0) + _curLineItemCount = 1; + } + } + else if (_layout == ListLayoutType::FLOW_VERTICAL) + { + if (_lineCount > 0) + _curLineItemCount = _lineCount; + else + { + _curLineItemCount = floor((_scrollPane->getViewSize().height + _lineGap) / (_itemSize.y + _lineGap)); + if (_curLineItemCount <= 0) + _curLineItemCount = 1; + } + } + else //pagination + { + if (_columnCount > 0) + _curLineItemCount = _columnCount; + else + { + _curLineItemCount = floor((_scrollPane->getViewSize().width + _columnGap) / (_itemSize.x + _columnGap)); + if (_curLineItemCount <= 0) + _curLineItemCount = 1; + } + + if (_lineCount > 0) + _curLineItemCount2 = _lineCount; + else + { + _curLineItemCount2 = floor((_scrollPane->getViewSize().height + _lineGap) / (_itemSize.y + _lineGap)); + if (_curLineItemCount2 <= 0) + _curLineItemCount2 = 1; + } + } + } + float ch = 0, cw = 0; + if (_realNumItems > 0) + { + int len = ceil((float)_realNumItems / _curLineItemCount) * _curLineItemCount; + int len2 = MIN(_curLineItemCount, _realNumItems); + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + { + for (int i = 0; i < len; i += _curLineItemCount) + ch += _virtualItems[i].size.y + _lineGap; + if (ch > 0) + ch -= _lineGap; + + if (_autoResizeItem) + cw = _scrollPane->getViewSize().width; + else + { + for (int i = 0; i < len2; i++) + cw += _virtualItems[i].size.x + _columnGap; + if (cw > 0) + cw -= _columnGap; + } + } + else if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_VERTICAL) + { + for (int i = 0; i < len; i += _curLineItemCount) + cw += _virtualItems[i].size.x + _columnGap; + if (cw > 0) + cw -= _columnGap; + + if (_autoResizeItem) + ch = _scrollPane->getViewSize().height; + else + { + for (int i = 0; i < len2; i++) + ch += _virtualItems[i].size.y + _lineGap; + if (ch > 0) + ch -= _lineGap; + } + } + else + { + int pageCount = ceil((float)len / (_curLineItemCount * _curLineItemCount2)); + cw = pageCount * getViewWidth(); + ch = getViewHeight(); + } + } + + handleAlign(cw, ch); + _scrollPane->setContentSize(cw, ch); + + _eventLocked = false; + + handleScroll(true); +} + +void GList::onScroll(EventContext* context) +{ + handleScroll(false); +} + +int GList::getIndexOnPos1(float& pos, bool forceUpdate) +{ + if (_realNumItems < _curLineItemCount) + { + pos = 0; + return 0; + } + + if (numChildren() > 0 && !forceUpdate) + { + float pos2 = getChildAt(0)->getY(); + if (pos2 + (_lineGap > 0 ? 0 : -_lineGap) > pos) + { + for (int i = _firstIndex - _curLineItemCount; i >= 0; i -= _curLineItemCount) + { + pos2 -= (_virtualItems[i].size.y + _lineGap); + if (pos2 <= pos) + { + pos = pos2; + return i; + } + } + + pos = 0; + return 0; + } + else + { + float testGap = _lineGap > 0 ? _lineGap : 0; + for (int i = _firstIndex; i < _realNumItems; i += _curLineItemCount) + { + float pos3 = pos2 + _virtualItems[i].size.y; + if (pos3 + testGap > pos) + { + pos = pos2; + return i; + } + pos2 = pos3 + _lineGap; + } + + pos = pos2; + return _realNumItems - _curLineItemCount; + } + } + else + { + float pos2 = 0; + float testGap = _lineGap > 0 ? _lineGap : 0; + for (int i = 0; i < _realNumItems; i += _curLineItemCount) + { + float pos3 = pos2 + _virtualItems[i].size.y; + if (pos3 + testGap > pos) + { + pos = pos2; + return i; + } + pos2 = pos3 + _lineGap; + } + + pos = pos2; + return _realNumItems - _curLineItemCount; + } +} + +int GList::getIndexOnPos2(float& pos, bool forceUpdate) +{ + if (_realNumItems < _curLineItemCount) + { + pos = 0; + return 0; + } + + if (numChildren() > 0 && !forceUpdate) + { + float pos2 = getChildAt(0)->getX(); + if (pos2 + (_columnGap > 0 ? 0 : -_columnGap) > pos) + { + for (int i = _firstIndex - _curLineItemCount; i >= 0; i -= _curLineItemCount) + { + pos2 -= (_virtualItems[i].size.x + _columnGap); + if (pos2 <= pos) + { + pos = pos2; + return i; + } + } + + pos = 0; + return 0; + } + else + { + float testGap = _columnGap > 0 ? _columnGap : 0; + for (int i = _firstIndex; i < _realNumItems; i += _curLineItemCount) + { + float pos3 = pos2 + _virtualItems[i].size.x; + if (pos3 + testGap > pos) + { + pos = pos2; + return i; + } + pos2 = pos3 + _columnGap; + } + + pos = pos2; + return _realNumItems - _curLineItemCount; + } + } + else + { + float pos2 = 0; + float testGap = _columnGap > 0 ? _columnGap : 0; + for (int i = 0; i < _realNumItems; i += _curLineItemCount) + { + float pos3 = pos2 + _virtualItems[i].size.x; + if (pos3 + testGap > pos) + { + pos = pos2; + return i; + } + pos2 = pos3 + _columnGap; + } + + pos = pos2; + return _realNumItems - _curLineItemCount; + } +} + +int GList::getIndexOnPos3(float& pos, bool forceUpdate) +{ + if (_realNumItems < _curLineItemCount) + { + pos = 0; + return 0; + } + + float viewWidth = getViewWidth(); + int page = floor(pos / viewWidth); + int startIndex = page * (_curLineItemCount * _curLineItemCount2); + float pos2 = page * viewWidth; + float testGap = _columnGap > 0 ? _columnGap : 0; + for (int i = 0; i < _curLineItemCount; i++) + { + float pos3 = pos2 + _virtualItems[startIndex + i].size.x; + if (pos3 + testGap > pos) + { + pos = pos2; + return startIndex + i; + } + pos2 = pos3 + _columnGap; + } + + pos = pos2; + return startIndex + _curLineItemCount - 1; +} + +void GList::handleScroll(bool forceUpdate) +{ + if (_eventLocked) + return; + + if (_layout == ListLayoutType::SINGLE_COLUMN || _layout == ListLayoutType::FLOW_HORIZONTAL) + { + int enterCounter = 0; + while (handleScroll1(forceUpdate)) + { + enterCounter++; + forceUpdate = false; + if (enterCounter > 20) + { + CCLOG("FairyGUI: list will never be filled as the item renderer function always returns a different size."); + break; + } + } + handleArchOrder1(); + } + else if (_layout == ListLayoutType::SINGLE_ROW || _layout == ListLayoutType::FLOW_VERTICAL) + { + int enterCounter = 0; + while (handleScroll2(forceUpdate)) + { + enterCounter++; + forceUpdate = false; + if (enterCounter > 20) + { + CCLOG("FairyGUI: list will never be filled as the item renderer function always returns a different size."); + break; + } + } + handleArchOrder2(); + } + else + { + handleScroll3(forceUpdate); + } + + _boundsChanged = false; +} + +bool GList::handleScroll1(bool forceUpdate) +{ + float pos = _scrollPane->getScrollingPosY(); + float max = pos + _scrollPane->getViewSize().height; + bool end = max == _scrollPane->getContentSize().height; + + int newFirstIndex = getIndexOnPos1(pos, forceUpdate); + if (newFirstIndex == _firstIndex && !forceUpdate) + return false; + + int oldFirstIndex = _firstIndex; + _firstIndex = newFirstIndex; + int curIndex = newFirstIndex; + bool forward = oldFirstIndex > newFirstIndex; + int childCount = numChildren(); + int lastIndex = oldFirstIndex + childCount - 1; + int reuseIndex = forward ? lastIndex : oldFirstIndex; + float curX = 0, curY = pos; + bool needRender; + float deltaSize = 0; + float firstItemDeltaSize = 0; + std::string url = _defaultItem; + int partSize = (int)((_scrollPane->getViewSize().width - _columnGap * (_curLineItemCount - 1)) / _curLineItemCount); + + _itemInfoVer++; + while (curIndex < _realNumItems && (end || curY < max)) + { + ItemInfo& ii = _virtualItems[curIndex]; + + if (ii.obj == nullptr || forceUpdate) + { + if (itemProvider != nullptr) + { + url = itemProvider(curIndex % _numItems); + if (url.size() == 0) + url = _defaultItem; + url = UIPackage::normalizeURL(url); + } + + if (ii.obj != nullptr && ii.obj->getResourceURL().compare(url) != 0) + { + if (dynamic_cast(ii.obj)) + ii.selected = ((GButton*)ii.obj)->isSelected(); + removeChildToPool(ii.obj); + ii.obj = nullptr; + } + } + + if (ii.obj == nullptr) + { + if (forward) + { + for (int j = reuseIndex; j >= oldFirstIndex; j--) + { + ItemInfo& ii2 = _virtualItems[j]; + if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer && ii2.obj->getResourceURL().compare(url) == 0) + { + if (dynamic_cast(ii2.obj)) + ii2.selected = ((GButton*)ii2.obj)->isSelected(); + ii.obj = ii2.obj; + ii2.obj = nullptr; + if (j == reuseIndex) + reuseIndex--; + break; + } + } + } + else + { + for (int j = reuseIndex; j <= lastIndex; j++) + { + ItemInfo& ii2 = _virtualItems[j]; + if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer && ii2.obj->getResourceURL().compare(url) == 0) + { + if (dynamic_cast(ii2.obj)) + ii2.selected = ((GButton*)ii2.obj)->isSelected(); + ii.obj = ii2.obj; + ii2.obj = nullptr; + if (j == reuseIndex) + reuseIndex++; + break; + } + } + } + + if (ii.obj != nullptr) + { + setChildIndex(ii.obj, forward ? curIndex - newFirstIndex : numChildren()); + } + else + { + ii.obj = _pool->getObject(url); + if (forward) + addChildAt(ii.obj, curIndex - newFirstIndex); + else + addChild(ii.obj); + } + if (dynamic_cast(ii.obj)) + ((GButton*)ii.obj)->setSelected(ii.selected); + + needRender = true; + } + else + needRender = forceUpdate; + + if (needRender) + { + if (_autoResizeItem && (_layout == ListLayoutType::SINGLE_COLUMN || _columnCount > 0)) + ii.obj->setSize(partSize, ii.obj->getHeight(), true); + + itemRenderer(curIndex % _numItems, ii.obj); + if (curIndex % _curLineItemCount == 0) + { + deltaSize += ceil(ii.obj->getHeight()) - ii.size.y; + if (curIndex == newFirstIndex && oldFirstIndex > newFirstIndex) + { + firstItemDeltaSize = ceil(ii.obj->getHeight()) - ii.size.y; + } + } + ii.size.x = ceil(ii.obj->getWidth()); + ii.size.y = ceil(ii.obj->getHeight()); + } + + ii.updateFlag = _itemInfoVer; + ii.obj->setPosition(curX, curY); + if (curIndex == newFirstIndex) + max += ii.size.y; + + curX += ii.size.x + _columnGap; + + if (curIndex % _curLineItemCount == _curLineItemCount - 1) + { + curX = 0; + curY += ii.size.y + _lineGap; + } + curIndex++; + } + + for (int i = 0; i < childCount; i++) + { + ItemInfo& ii = _virtualItems[oldFirstIndex + i]; + if (ii.updateFlag != _itemInfoVer && ii.obj != nullptr) + { + if (dynamic_cast(ii.obj)) + ii.selected = ((GButton*)ii.obj)->isSelected(); + removeChildToPool(ii.obj); + ii.obj = nullptr; + } + } + + childCount = (int)_children.size(); + for (int i = 0; i < childCount; i++) + { + GObject* obj = _virtualItems[newFirstIndex + i].obj; + if (_children.at(i) != obj) + setChildIndex(obj, i); + } + + if (deltaSize != 0 || firstItemDeltaSize != 0) + _scrollPane->changeContentSizeOnScrolling(0, deltaSize, 0, firstItemDeltaSize); + + if (curIndex > 0 && numChildren() > 0 && _container->getPositionY2() <= 0 && getChildAt(0)->getY() > -_container->getPositionY2()) + return true; + else + return false; +} + +bool GList::handleScroll2(bool forceUpdate) +{ + float pos = _scrollPane->getScrollingPosX(); + float max = pos + _scrollPane->getViewSize().width; + bool end = pos == _scrollPane->getContentSize().width; + + int newFirstIndex = getIndexOnPos2(pos, forceUpdate); + if (newFirstIndex == _firstIndex && !forceUpdate) + return false; + + int oldFirstIndex = _firstIndex; + _firstIndex = newFirstIndex; + int curIndex = newFirstIndex; + bool forward = oldFirstIndex > newFirstIndex; + int childCount = numChildren(); + int lastIndex = oldFirstIndex + childCount - 1; + int reuseIndex = forward ? lastIndex : oldFirstIndex; + float curX = pos, curY = 0; + bool needRender; + float deltaSize = 0; + float firstItemDeltaSize = 0; + string url = _defaultItem; + int partSize = (int)((_scrollPane->getViewSize().height - _lineGap * (_curLineItemCount - 1)) / _curLineItemCount); + + _itemInfoVer++; + while (curIndex < _realNumItems && (end || curX < max)) + { + ItemInfo& ii = _virtualItems[curIndex]; + + if (ii.obj == nullptr || forceUpdate) + { + if (itemProvider != nullptr) + { + url = itemProvider(curIndex % _numItems); + if (url.size() == 0) + url = _defaultItem; + url = UIPackage::normalizeURL(url); + } + + if (ii.obj != nullptr && ii.obj->getResourceURL().compare(url) != 0) + { + if (dynamic_cast(ii.obj)) + ii.selected = ((GButton*)ii.obj)->isSelected(); + removeChildToPool(ii.obj); + ii.obj = nullptr; + } + } + + if (ii.obj == nullptr) + { + if (forward) + { + for (int j = reuseIndex; j >= oldFirstIndex; j--) + { + ItemInfo& ii2 = _virtualItems[j]; + if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer && ii2.obj->getResourceURL().compare(url) == 0) + { + if (dynamic_cast(ii2.obj)) + ii2.selected = ((GButton*)ii2.obj)->isSelected(); + ii.obj = ii2.obj; + ii2.obj = nullptr; + if (j == reuseIndex) + reuseIndex--; + break; + } + } + } + else + { + for (int j = reuseIndex; j <= lastIndex; j++) + { + ItemInfo& ii2 = _virtualItems[j]; + if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer && ii2.obj->getResourceURL().compare(url) == 0) + { + if (dynamic_cast(ii2.obj)) + ii2.selected = ((GButton*)ii2.obj)->isSelected(); + ii.obj = ii2.obj; + ii2.obj = nullptr; + if (j == reuseIndex) + reuseIndex++; + break; + } + } + } + + if (ii.obj != nullptr) + { + setChildIndex(ii.obj, forward ? curIndex - newFirstIndex : numChildren()); + } + else + { + ii.obj = _pool->getObject(url); + if (forward) + addChildAt(ii.obj, curIndex - newFirstIndex); + else + addChild(ii.obj); + } + if (dynamic_cast(ii.obj)) + ((GButton*)ii.obj)->setSelected(ii.selected); + + needRender = true; + } + else + needRender = forceUpdate; + + if (needRender) + { + if (_autoResizeItem && (_layout == ListLayoutType::SINGLE_ROW || _lineCount > 0)) + ii.obj->setSize(ii.obj->getWidth(), partSize, true); + + itemRenderer(curIndex % _numItems, ii.obj); + if (curIndex % _curLineItemCount == 0) + { + deltaSize += ceil(ii.obj->getWidth()) - ii.size.x; + if (curIndex == newFirstIndex && oldFirstIndex > newFirstIndex) + { + firstItemDeltaSize = ceil(ii.obj->getWidth()) - ii.size.x; + } + } + ii.size.x = ceil(ii.obj->getWidth()); + ii.size.y = ceil(ii.obj->getHeight()); + } + + ii.updateFlag = _itemInfoVer; + ii.obj->setPosition(curX, curY); + if (curIndex == newFirstIndex) + max += ii.size.x; + + curY += ii.size.y + _lineGap; + + if (curIndex % _curLineItemCount == _curLineItemCount - 1) + { + curY = 0; + curX += ii.size.x + _columnGap; + } + curIndex++; + } + + for (int i = 0; i < childCount; i++) + { + ItemInfo& ii = _virtualItems[oldFirstIndex + i]; + if (ii.updateFlag != _itemInfoVer && ii.obj != nullptr) + { + if (dynamic_cast(ii.obj)) + ii.selected = ((GButton*)ii.obj)->isSelected(); + removeChildToPool(ii.obj); + ii.obj = nullptr; + } + } + + childCount = (int)_children.size(); + for (int i = 0; i < childCount; i++) + { + GObject* obj = _virtualItems[newFirstIndex + i].obj; + if (_children.at(i) != obj) + setChildIndex(obj, i); + } + + if (deltaSize != 0 || firstItemDeltaSize != 0) + _scrollPane->changeContentSizeOnScrolling(deltaSize, 0, firstItemDeltaSize, 0); + + if (curIndex > 0 && numChildren() > 0 && _container->getPositionX() <= 0 && getChildAt(0)->getX() > -_container->getPositionX()) + return true; + else + return false; +} + +void GList::handleScroll3(bool forceUpdate) +{ + float pos = _scrollPane->getScrollingPosX(); + + int newFirstIndex = getIndexOnPos3(pos, forceUpdate); + if (newFirstIndex == _firstIndex && !forceUpdate) + return; + + int oldFirstIndex = _firstIndex; + _firstIndex = newFirstIndex; + + int reuseIndex = oldFirstIndex; + int virtualItemCount = (int)_virtualItems.size(); + int pageSize = _curLineItemCount * _curLineItemCount2; + int startCol = newFirstIndex % _curLineItemCount; + float viewWidth = getViewWidth(); + int page = (int)(newFirstIndex / pageSize); + int startIndex = page * pageSize; + int lastIndex = startIndex + pageSize * 2; + bool needRender; + string url = _defaultItem; + int partWidth = (int)((_scrollPane->getViewSize().width - _columnGap * (_curLineItemCount - 1)) / _curLineItemCount); + int partHeight = (int)((_scrollPane->getViewSize().height - _lineGap * (_curLineItemCount2 - 1)) / _curLineItemCount2); + _itemInfoVer++; + + for (int i = startIndex; i < lastIndex; i++) + { + if (i >= _realNumItems) + continue; + + int col = i % _curLineItemCount; + if (i - startIndex < pageSize) + { + if (col < startCol) + continue; + } + else + { + if (col > startCol) + continue; + } + + ItemInfo& ii = _virtualItems[i]; + ii.updateFlag = _itemInfoVer; + } + + GObject* lastObj = nullptr; + int insertIndex = 0; + for (int i = startIndex; i < lastIndex; i++) + { + if (i >= _realNumItems) + continue; + + ItemInfo& ii = _virtualItems[i]; + if (ii.updateFlag != _itemInfoVer) + continue; + + if (ii.obj == nullptr) + { + reuseIndex = reuseIndex < 0 ? 0 : reuseIndex; + while (reuseIndex < virtualItemCount) + { + ItemInfo& ii2 = _virtualItems[reuseIndex]; + if (ii2.obj != nullptr && ii2.updateFlag != _itemInfoVer) + { + if (dynamic_cast(ii2.obj)) + ii2.selected = ((GButton*)ii2.obj)->isSelected(); + ii.obj = ii2.obj; + ii2.obj = nullptr; + break; + } + reuseIndex++; + } + + if (insertIndex == -1) + insertIndex = getChildIndex(lastObj) + 1; + + if (ii.obj == nullptr) + { + if (itemProvider != nullptr) + { + url = itemProvider(i % _numItems); + if (url.size() == 0) + url = _defaultItem; + url = UIPackage::normalizeURL(url); + } + + ii.obj = _pool->getObject(url); + addChildAt(ii.obj, insertIndex); + } + else + { + insertIndex = setChildIndexBefore(ii.obj, insertIndex); + } + insertIndex++; + + if (dynamic_cast(ii.obj)) + ((GButton*)ii.obj)->setSelected(ii.selected); + + needRender = true; + } + else + { + needRender = forceUpdate; + insertIndex = -1; + lastObj = ii.obj; + } + + if (needRender) + { + if (_autoResizeItem) + { + if (_curLineItemCount == _columnCount && _curLineItemCount2 == _lineCount) + ii.obj->setSize(partWidth, partHeight, true); + else if (_curLineItemCount == _columnCount) + ii.obj->setSize(partWidth, ii.obj->getHeight(), true); + else if (_curLineItemCount2 == _lineCount) + ii.obj->setSize(ii.obj->getWidth(), partHeight, true); + } + + itemRenderer(i % _numItems, ii.obj); + ii.size.x = ceil(ii.obj->getWidth()); + ii.size.y = ceil(ii.obj->getHeight()); + } + } + + float borderX = (startIndex / pageSize) * viewWidth; + float xx = borderX; + float yy = 0; + float lineHeight = 0; + for (int i = startIndex; i < lastIndex; i++) + { + if (i >= _realNumItems) + continue; + + ItemInfo& ii = _virtualItems[i]; + if (ii.updateFlag == _itemInfoVer) + ii.obj->setPosition(xx, yy); + + if (ii.size.y > lineHeight) + lineHeight = ii.size.y; + if (i % _curLineItemCount == _curLineItemCount - 1) + { + xx = borderX; + yy += lineHeight + _lineGap; + lineHeight = 0; + + if (i == startIndex + pageSize - 1) + { + borderX += viewWidth; + xx = borderX; + yy = 0; + } + } + else + xx += ii.size.x + _columnGap; + } + + for (int i = reuseIndex; i < virtualItemCount; i++) + { + ItemInfo& ii = _virtualItems[i]; + if (ii.updateFlag != _itemInfoVer && ii.obj != nullptr) + { + if (dynamic_cast(ii.obj)) + ii.selected = ((GButton*)ii.obj)->isSelected(); + removeChildToPool(ii.obj); + ii.obj = nullptr; + } + } +} + +void GList::handleArchOrder1() +{ + if (_childrenRenderOrder == ChildrenRenderOrder::ARCH) + { + float mid = _scrollPane->getPosY() + getViewHeight() / 2; + float minDist = FLT_MAX, dist; + int apexIndex = 0; + int cnt = numChildren(); + for (int i = 0; i < cnt; i++) + { + GObject* obj = getChildAt(i); + if (!foldInvisibleItems || obj->isVisible()) + { + dist = abs(mid - obj->getY() - obj->getHeight() / 2); + if (dist < minDist) + { + minDist = dist; + apexIndex = i; + } + } + } + setApexIndex(apexIndex); + } +} + +void GList::handleArchOrder2() +{ + if (_childrenRenderOrder == ChildrenRenderOrder::ARCH) + { + float mid = _scrollPane->getPosX() + getViewWidth() / 2; + float minDist = FLT_MAX, dist; + int apexIndex = 0; + int cnt = numChildren(); + for (int i = 0; i < cnt; i++) + { + GObject* obj = getChildAt(i); + if (!foldInvisibleItems || obj->isVisible()) + { + dist = abs(mid - obj->getX() - obj->getWidth() / 2); + if (dist < minDist) + { + minDist = dist; + apexIndex = i; + } + } + } + setApexIndex(apexIndex); + } +} + +void GList::handleAlign(float contentWidth, float contentHeight) +{ + Vec2 newOffset(0, 0); + + float viewHeight = getViewHeight(); + float viewWidth = getViewWidth(); + if (contentHeight < viewHeight) + { + if (_verticalAlign == TextVAlignment::CENTER) + newOffset.y = (int)((viewHeight - contentHeight) / 2); + else if (_verticalAlign == TextVAlignment::BOTTOM) + newOffset.y = viewHeight - contentHeight; + } + + if (contentWidth < viewWidth) + { + if (_align == TextHAlignment::CENTER) + newOffset.x = (int)((viewWidth - contentWidth) / 2); + else if (_align == TextHAlignment::RIGHT) + newOffset.x = viewWidth - contentWidth; + } + + if (!newOffset.equals(_alignOffset)) + { + _alignOffset = newOffset; + if (_scrollPane != nullptr) + _scrollPane->adjustMaskContainer(); + else + _container->setPosition2(_margin.left + _alignOffset.x, _margin.top + _alignOffset.y); + } +} + +void GList::updateBounds() +{ + if (_virtual) + return; + + int cnt = (int)_children.size(); + int i; + int j = 0; + GObject* child; + float curX = 0; + float curY = 0; + float cw, ch; + float maxWidth = 0; + float maxHeight = 0; + float viewWidth = getViewWidth(); + float viewHeight = getViewHeight(); + + if (_layout == ListLayoutType::SINGLE_COLUMN) + { + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + if (curY != 0) + curY += _lineGap; + child->setY(curY); + if (_autoResizeItem) + child->setSize(viewWidth, child->getHeight(), true); + curY += ceil(child->getHeight()); + if (child->getWidth() > maxWidth) + maxWidth = child->getWidth(); + } + ch = curY; + if (ch <= viewHeight && _autoResizeItem && _scrollPane != nullptr && _scrollPane->_displayInDemand && _scrollPane->_vtScrollBar != nullptr) + { + viewWidth += _scrollPane->_vtScrollBar->getWidth(); + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + child->setSize(viewWidth, child->getHeight(), true); + if (child->getWidth() > maxWidth) + maxWidth = child->getWidth(); + } + } + cw = ceil(maxWidth); + } + else if (_layout == ListLayoutType::SINGLE_ROW) + { + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + if (curX != 0) + curX += _columnGap; + child->setX(curX); + if (_autoResizeItem) + child->setSize(child->getWidth(), viewHeight, true); + curX += ceil(child->getWidth()); + if (child->getHeight() > maxHeight) + maxHeight = child->getHeight(); + } + cw = curX; + if (cw <= viewWidth && _autoResizeItem && _scrollPane != nullptr && _scrollPane->_displayInDemand && _scrollPane->_hzScrollBar != nullptr) + { + viewHeight += _scrollPane->_hzScrollBar->getHeight(); + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + child->setSize(child->getWidth(), viewHeight, true); + if (child->getHeight() > maxHeight) + maxHeight = child->getHeight(); + } + } + ch = ceil(maxHeight); + } + else if (_layout == ListLayoutType::FLOW_HORIZONTAL) + { + if (_autoResizeItem && _columnCount > 0) + { + float lineSize = 0; + int lineStart = 0; + float ratio; + + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + lineSize += child->sourceSize.width; + j++; + if (j == _columnCount || i == cnt - 1) + { + ratio = (viewWidth - lineSize - (j - 1) * _columnGap) / lineSize; + curX = 0; + for (j = lineStart; j <= i; j++) + { + child = getChildAt(j); + if (foldInvisibleItems && !child->isVisible()) + continue; + + child->setPosition(curX, curY); + + if (j < i) + { + child->setSize(child->sourceSize.width + round(child->sourceSize.width * ratio), child->getHeight(), true); + curX += ceil(child->getWidth()) + _columnGap; + } + else + { + child->setSize(viewWidth - curX, child->getHeight(), true); + } + if (child->getHeight() > maxHeight) + maxHeight = child->getHeight(); + } + //new line + curY += ceil(maxHeight) + _lineGap; + maxHeight = 0; + j = 0; + lineStart = i + 1; + lineSize = 0; + } + } + ch = curY + ceil(maxHeight); + cw = viewWidth; + } + else + { + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + if (curX != 0) + curX += _columnGap; + + if ((_columnCount != 0 && j >= _columnCount) || (_columnCount == 0 && curX + child->getWidth() > viewWidth && maxHeight != 0)) + { + //new line + curX = 0; + curY += ceil(maxHeight) + _lineGap; + maxHeight = 0; + j = 0; + } + child->setPosition(curX, curY); + curX += ceil(child->getWidth()); + if (curX > maxWidth) + maxWidth = curX; + if (child->getHeight() > maxHeight) + maxHeight = child->getHeight(); + j++; + } + ch = curY + ceil(maxHeight); + cw = ceil(maxWidth); + } + } + else if (_layout == ListLayoutType::FLOW_VERTICAL) + { + if (_autoResizeItem && _lineCount > 0) + { + float lineSize = 0; + int lineStart = 0; + float ratio; + + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + lineSize += child->sourceSize.height; + j++; + if (j == _lineCount || i == cnt - 1) + { + ratio = (viewHeight - lineSize - (j - 1) * _lineGap) / lineSize; + curY = 0; + for (j = lineStart; j <= i; j++) + { + child = getChildAt(j); + if (foldInvisibleItems && !child->isVisible()) + continue; + + child->setPosition(curX, curY); + + if (j < i) + { + child->setSize(child->getWidth(), child->sourceSize.height + round(child->sourceSize.height * ratio), true); + curY += ceil(child->getHeight()) + _lineGap; + } + else + { + child->setSize(child->getWidth(), viewHeight - curY, true); + } + if (child->getWidth() > maxWidth) + maxWidth = child->getWidth(); + } + //new line + curX += ceil(maxWidth) + _columnGap; + maxWidth = 0; + j = 0; + lineStart = i + 1; + lineSize = 0; + } + } + cw = curX + ceil(maxWidth); + ch = viewHeight; + } + else + { + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + if (curY != 0) + curY += _lineGap; + + if ((_lineCount != 0 && j >= _lineCount) || (_lineCount == 0 && curY + child->getHeight() > viewHeight && maxWidth != 0)) + { + curY = 0; + curX += ceil(maxWidth) + _columnGap; + maxWidth = 0; + j = 0; + } + child->setPosition(curX, curY); + curY += child->getHeight(); + if (curY > maxHeight) + maxHeight = curY; + if (child->getWidth() > maxWidth) + maxWidth = child->getWidth(); + j++; + } + cw = curX + ceil(maxWidth); + ch = ceil(maxHeight); + } + } + else //pagination + { + int page = 0; + int k = 0; + float eachHeight = 0; + if (_autoResizeItem && _lineCount > 0) + eachHeight = floor((viewHeight - (_lineCount - 1) * _lineGap) / _lineCount); + + if (_autoResizeItem && _columnCount > 0) + { + float lineSize = 0; + int lineStart = 0; + float ratio; + + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + if (j == 0 && ((_lineCount != 0 && k >= _lineCount) || (_lineCount == 0 && curY + (_lineCount > 0 ? eachHeight : child->getHeight()) > viewHeight))) + { + //new page + page++; + curY = 0; + k = 0; + } + + lineSize += child->sourceSize.width; + j++; + if (j == _columnCount || i == cnt - 1) + { + ratio = (viewWidth - lineSize - (j - 1) * _columnGap) / lineSize; + curX = 0; + for (j = lineStart; j <= i; j++) + { + child = getChildAt(j); + if (foldInvisibleItems && !child->isVisible()) + continue; + + child->setPosition(page * viewWidth + curX, curY); + + if (j < i) + { + child->setSize(child->sourceSize.width + round(child->sourceSize.width * ratio), + _lineCount > 0 ? eachHeight : child->getHeight(), true); + curX += ceil(child->getWidth()) + _columnGap; + } + else + { + child->setSize(viewWidth - curX, _lineCount > 0 ? eachHeight : child->getHeight(), true); + } + if (child->getHeight() > maxHeight) + maxHeight = child->getHeight(); + } + //new line + curY += ceil(maxHeight) + _lineGap; + maxHeight = 0; + j = 0; + lineStart = i + 1; + lineSize = 0; + + k++; + } + } + } + else + { + for (i = 0; i < cnt; i++) + { + child = getChildAt(i); + if (foldInvisibleItems && !child->isVisible()) + continue; + + if (curX != 0) + curX += _columnGap; + + if (_autoResizeItem && _lineCount > 0) + child->setSize(child->getWidth(), eachHeight, true); + + if ((_columnCount != 0 && j >= _columnCount) || (_columnCount == 0 && curX + child->getWidth() > viewWidth && maxHeight != 0)) + { + curX = 0; + curY += maxHeight + _lineGap; + maxHeight = 0; + j = 0; + k++; + + if ((_lineCount != 0 && k >= _lineCount) || (_lineCount == 0 && curY + child->getHeight() > viewHeight && maxWidth != 0)) //new page + { + page++; + curY = 0; + k = 0; + } + } + child->setPosition(page * viewWidth + curX, curY); + curX += ceil(child->getWidth()); + if (curX > maxWidth) + maxWidth = curX; + if (child->getHeight() > maxHeight) + maxHeight = child->getHeight(); + j++; + } + } + ch = page > 0 ? viewHeight : (curY + ceil(maxHeight)); + cw = (page + 1) * viewWidth; + } + + handleAlign(cw, ch); + setBounds(0, 0, cw, ch); +} + +void GList::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GComponent::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 5); + + _layout = (ListLayoutType)buffer->readByte(); + _selectionMode = (ListSelectionMode)buffer->readByte(); + _align = (TextHAlignment)buffer->readByte(); + _verticalAlign = (TextVAlignment)buffer->readByte(); + _lineGap = buffer->readShort(); + _columnGap = buffer->readShort(); + _lineCount = buffer->readShort(); + _columnCount = buffer->readShort(); + _autoResizeItem = buffer->readBool(); + _childrenRenderOrder = (ChildrenRenderOrder)buffer->readByte(); + _apexIndex = buffer->readShort(); + + if (buffer->readBool()) + { + _margin.top = buffer->readInt(); + _margin.bottom = buffer->readInt(); + _margin.left = buffer->readInt(); + _margin.right = buffer->readInt(); + } + + OverflowType overflow = (OverflowType)buffer->readByte(); + if (overflow == OverflowType::SCROLL) + { + int savedPos = buffer->getPos(); + buffer->seek(beginPos, 7); + setupScroll(buffer); + buffer->setPos(savedPos); + } + else + setupOverflow(overflow); + + if (buffer->readBool()) //clipSoftness + buffer->skip(8); + + if (buffer->version >= 2) + { + scrollItemToViewOnClick = buffer->readBool(); + foldInvisibleItems = buffer->readBool(); + } + + buffer->seek(beginPos, 8); + + _defaultItem = buffer->readS(); + readItems(buffer); +} + +void GList::readItems(ByteBuffer* buffer) +{ + const string* str; + + int itemCount = buffer->readShort(); + for (int i = 0; i < itemCount; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + str = buffer->readSP(); + if (!str || (*str).empty()) + { + str = &_defaultItem; + if ((*str).empty()) + { + buffer->setPos(nextPos); + continue; + } + } + + GObject* obj = getFromPool(*str); + if (obj != nullptr) + { + addChild(obj); + setupItem(buffer, obj); + } + + buffer->setPos(nextPos); + } +} + +void GList::setupItem(ByteBuffer* buffer, GObject* obj) +{ + const string* str; + GButton* btn = dynamic_cast(obj); + + if ((str = buffer->readSP())) + obj->setText(*str); + if ((str = buffer->readSP()) && btn) + btn->setSelectedTitle(*str); + if ((str = buffer->readSP())) + obj->setIcon(*str); + if ((str = buffer->readSP()) && btn) + btn->setSelectedIcon(*str); + if ((str = buffer->readSP())) + obj->name = *str; + + GComponent* gcom = dynamic_cast(obj); + if (gcom != nullptr) + { + int cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + GController* cc = gcom->getController(buffer->readS()); + const std::string& pageId = buffer->readS(); + cc->setSelectedPageId(pageId); + } + + if (buffer->version >= 2) + { + cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + std::string target = buffer->readS(); + ObjectPropID propId = (ObjectPropID)buffer->readShort(); + std::string value = buffer->readS(); + GObject* obj2 = gcom->getChildByPath(target); + if (obj2 != nullptr) + obj2->setProp(propId, Value(value)); + } + } + } +} + +void GList::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GComponent::setup_afterAdd(buffer, beginPos); + + buffer->seek(beginPos, 6); + + int i = buffer->readShort(); + if (i != -1) + _selectionController = _parent->getControllerAt(i); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/GList.h b/extensions/fairygui/GList.h new file mode 100644 index 0000000000..1ad076306f --- /dev/null +++ b/extensions/fairygui/GList.h @@ -0,0 +1,190 @@ +#ifndef __GLIST_H__ +#define __GLIST_H__ + +#include "FairyGUIMacros.h" +#include "GComponent.h" +#include "GObjectPool.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GList : public GComponent +{ +public: + typedef std::function ListItemRenderer; + typedef std::function ListItemProvider; + + GList(); + virtual ~GList(); + + CREATE_FUNC(GList); + + const std::string& getDefaultItem() const { return _defaultItem; } + void setDefaultItem(const std::string& value) { _defaultItem = value; } + + ListLayoutType getLayout() const { return _layout; } + void setLayout(ListLayoutType value); + + int getLineCount() const { return _lineCount; } + void setLineCount(int value); + + int getColumnCount() { return _columnCount; } + void setColumnCount(int value); + + int getColumnGap() const { return _columnGap; } + void setColumnGap(int value); + + int getLineGap() const { return _lineGap; } + void setLineGap(int value); + + cocos2d::TextHAlignment getAlign() const { return _align; } + void setAlign(cocos2d::TextHAlignment value); + + cocos2d::TextVAlignment getVerticalAlign() const { return _verticalAlign; } + void setVerticalAlign(cocos2d::TextVAlignment value); + + bool getAutoResizeItem() const { return _autoResizeItem; } + void setAutoResizeItem(bool value); + + ListSelectionMode getSelectionMode() const { return _selectionMode; } + void setSelectionMode(ListSelectionMode value) { _selectionMode = value; } + + GObjectPool* getItemPool() const { return _pool; } + GObject* getFromPool() { return getFromPool(cocos2d::STD_STRING_EMPTY); } + GObject* getFromPool(const std::string& url); + void returnToPool(GObject* obj); + GObject* addItemFromPool() { return addItemFromPool(cocos2d::STD_STRING_EMPTY); } + GObject* addItemFromPool(const std::string& url); + + GObject* addChildAt(GObject* child, int index) override; + void removeChildAt(int index) override; + void removeChildToPoolAt(int index); + void removeChildToPool(GObject* child); + void removeChildrenToPool(); + void removeChildrenToPool(int beginIndex, int endIndex); + + int getSelectedIndex() const; + void setSelectedIndex(int value); + + void getSelection(std::vector& result) const; + void addSelection(int index, bool scrollItToView); + void removeSelection(int index); + void clearSelection(); + void selectAll(); + void selectReverse(); + + void handleArrowKey(int dir); + + void resizeToFit(int itemCount) { resizeToFit(itemCount, 0); } + void resizeToFit(int itemCount, int minSize); + + virtual int getFirstChildInView() override; + + void scrollToView(int index, bool ani = false, bool setFirst = false); + + GController* getSelectionController() const { return _selectionController; } + void setSelectionController(GController* value); + + void setVirtual(); + void setVirtualAndLoop(); + bool isVirtual() { return _virtual; } + void refreshVirtualList(); + + int getNumItems(); + void setNumItems(int value); + + int childIndexToItemIndex(int index); + int itemIndexToChildIndex(int index); + + virtual cocos2d::Vec2 getSnappingPosition(const cocos2d::Vec2& pt) override; + + ListItemRenderer itemRenderer; + ListItemProvider itemProvider; + bool scrollItemToViewOnClick; + bool foldInvisibleItems; + +protected: + virtual void handleControllerChanged(GController* c) override; + virtual void handleSizeChanged() override; + virtual void updateBounds() override; + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + + virtual void dispatchItemEvent(GObject* item, EventContext* context); + virtual void readItems(ByteBuffer* buffer); + virtual void setupItem(ByteBuffer* buffer, GObject* obj); + +private: + void clearSelectionExcept(GObject* g); + void setSelectionOnEvent(GObject* item, InputEvent* evt); + + void onItemTouchBegin(EventContext* context); + void onClickItem(EventContext* context); + + void updateSelectionController(int index); + + void setVirtual(bool loop); + void checkVirtualList(); + void setVirtualListChangedFlag(bool layoutChanged); + CALL_LATER_FUNC(GList, doRefreshVirtualList); + + void onScroll(EventContext* context); + + int getIndexOnPos1(float& pos, bool forceUpdate); + int getIndexOnPos2(float& pos, bool forceUpdate); + int getIndexOnPos3(float& pos, bool forceUpdate); + + void handleScroll(bool forceUpdate); + bool handleScroll1(bool forceUpdate); + bool handleScroll2(bool forceUpdate); + void handleScroll3(bool forceUpdate); + + void handleArchOrder1(); + void handleArchOrder2(); + + void handleAlign(float contentWidth, float contentHeight); + + ListLayoutType _layout; + int _lineCount; + int _columnCount; + int _lineGap; + int _columnGap; + cocos2d::TextHAlignment _align; + cocos2d::TextVAlignment _verticalAlign; + bool _autoResizeItem; + ListSelectionMode _selectionMode; + std::string _defaultItem; + GController* _selectionController; + + GObjectPool* _pool; + bool _selectionHandled; + int _lastSelectedIndex; + + //Virtual List support + bool _virtual; + bool _loop; + int _numItems; + int _realNumItems; + int _firstIndex; //the top left index + int _curLineItemCount; //item count in one line + int _curLineItemCount2; //item count in vertical direction,only pagination layout + cocos2d::Vec2 _itemSize; + int _virtualListChanged; //1-content changed, 2-size changed + bool _eventLocked; + uint32_t _itemInfoVer; + + struct ItemInfo + { + cocos2d::Vec2 size; + GObject* obj; + uint32_t updateFlag; + bool selected; + + ItemInfo(); + }; + std::vector _virtualItems; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GLoader.cpp b/extensions/fairygui/GLoader.cpp new file mode 100644 index 0000000000..4a4d006480 --- /dev/null +++ b/extensions/fairygui/GLoader.cpp @@ -0,0 +1,603 @@ +#include "GLoader.h" +#include "GComponent.h" +#include "GMovieClip.h" +#include "UIPackage.h" +#include "display/FUISprite.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GLoader::GLoader() + : _autoSize(false), + _align(TextHAlignment::LEFT), + _verticalAlign(TextVAlignment::TOP), + _fill(LoaderFillType::NONE), + _shrinkOnly(false), + _updatingLayout(false), + _contentItem(nullptr), + _contentStatus(0), + _content(nullptr), + _content2(nullptr), + _playAction(nullptr), + _playing(true), + _frame(0) +{ +} + +GLoader::~GLoader() +{ + CC_SAFE_RELEASE(_playAction); + CC_SAFE_RELEASE(_content); + CC_SAFE_RELEASE(_content2); +} + +void GLoader::handleInit() +{ + _content = FUISprite::create(); + _content->retain(); + _content->setAnchorPoint(Vec2::ZERO); + _content->setCascadeOpacityEnabled(true); + + FUIContainer* c = FUIContainer::create(); + c->retain(); + c->gOwner = this; + + _displayObject = c; + _displayObject->addChild(_content); +} + +void GLoader::setURL(const std::string& value) +{ + if (_url.compare(value) == 0) + return; + + _url = value; + loadContent(); + updateGear(7); +} + +void GLoader::setAlign(cocos2d::TextHAlignment value) +{ + if (_align != value) + { + _align = value; + updateLayout(); + } +} + +void GLoader::setVerticalAlign(cocos2d::TextVAlignment value) +{ + if (_verticalAlign != value) + { + _verticalAlign = value; + updateLayout(); + } +} + +void GLoader::setAutoSize(bool value) +{ + if (_autoSize != value) + { + _autoSize = value; + updateLayout(); + } +} + +void GLoader::setFill(LoaderFillType value) +{ + if (_fill != value) + { + _fill = value; + updateLayout(); + } +} + +void GLoader::setShrinkOnly(bool value) +{ + if (_shrinkOnly != value) + { + _shrinkOnly = value; + updateLayout(); + } +} + +const cocos2d::Size & GLoader::getContentSize() +{ + return _content->getContentSize(); +} + +cocos2d::Color3B GLoader::getColor() const +{ + return _content->getColor(); +} + +void GLoader::setColor(const cocos2d::Color3B& value) +{ + _content->setColor(value); +} + +void GLoader::setPlaying(bool value) +{ + if (_playing != value) + { + _playing = value; + if (_playAction) + { + if (_playing) + _content->runAction(_playAction); + else + _content->stopAction(_playAction); + } + updateGear(5); + } +} + +int GLoader::getFrame() const +{ + return _frame; +} + +void GLoader::setFrame(int value) +{ + if (_frame != value) + { + _frame = value; + if (_playAction) + _playAction->setFrame(value); + updateGear(5); + } +} + +FillMethod GLoader::getFillMethod() const +{ + return _content->getFillMethod(); +} + +void GLoader::setFillMethod(FillMethod value) +{ + _content->setFillMethod(value); +} + +FillOrigin GLoader::getFillOrigin() const +{ + return _content->getFillOrigin(); +} + +void GLoader::setFillOrigin(FillOrigin value) +{ + _content->setFillOrigin(value); +} + +bool GLoader::isFillClockwise() const +{ + return _content->isFillClockwise(); +} + +void GLoader::setFillClockwise(bool value) +{ + _content->setFillClockwise(value); +} + +float GLoader::getFillAmount() const +{ + return _content->getFillAmount(); +} + +void GLoader::setFillAmount(float value) +{ + _content->setFillAmount(value); +} + +void GLoader::loadContent() +{ + clearContent(); + + if (_url.length() == 0) + return; + + if (_url.compare(0, 5, "ui://") == 0) + loadFromPackage(); + else + { + _contentStatus = 3; + loadExternal(); + } +} + +void GLoader::loadFromPackage() +{ + _contentItem = UIPackage::getItemByURL(_url); + + if (_contentItem != nullptr) + { + _contentItem = _contentItem->getBranch(); + sourceSize.width = _contentItem->width; + sourceSize.height = _contentItem->height; + _contentItem = _contentItem->getHighResolution(); + _contentItem->load(); + + if (_contentItem->type == PackageItemType::IMAGE) + { + _contentStatus = 1; + _content->initWithSpriteFrame(_contentItem->spriteFrame); + if (_contentItem->scale9Grid) + ((FUISprite*)_content)->setScale9Grid(_contentItem->scale9Grid); + updateLayout(); + } + else if (_contentItem->type == PackageItemType::MOVIECLIP) + { + _contentStatus = 2; + if (_playAction == nullptr) + { + _playAction = ActionMovieClip::create(_contentItem->animation, _contentItem->repeatDelay); + _playAction->retain(); + } + else + _playAction->setAnimation(_contentItem->animation, _contentItem->repeatDelay); + if (_playing) + _content->runAction(_playAction); + else + _playAction->setFrame(_frame); + + updateLayout(); + } + else if (_contentItem->type == PackageItemType::COMPONENT) + { + GObject* obj = UIPackage::createObjectFromURL(_url); + if (obj == nullptr) + setErrorState(); + else if (dynamic_cast(obj) == nullptr) + { + setErrorState(); + } + else + { + _content2 = obj->as(); + _content2->retain(); + _content2->addEventListener(UIEventType::SizeChange, [this](EventContext*) { + if (!_updatingLayout) + updateLayout(); + }); + _displayObject->addChild(_content2->displayObject()); + updateLayout(); + } + } + else + { + if (_autoSize) + setSize(_contentItem->width, _contentItem->height); + + setErrorState(); + } + } + else + setErrorState(); +} + +void GLoader::loadExternal() +{ + auto tex = Director::getInstance()->getTextureCache()->addImage(_url); + if (tex) + { + auto sf = SpriteFrame::createWithTexture(tex, Rect(Vec2::ZERO, tex->getContentSize())); + onExternalLoadSuccess(sf); + } + else + onExternalLoadFailed(); +} + +void GLoader::freeExternal(cocos2d::SpriteFrame* spriteFrame) +{ +} + +void GLoader::onExternalLoadSuccess(cocos2d::SpriteFrame* spriteFrame) +{ + _contentStatus = 4; + _content->setSpriteFrame(spriteFrame); + sourceSize = spriteFrame->getRectInPixels().size; + updateLayout(); +} + +void GLoader::onExternalLoadFailed() +{ + setErrorState(); +} + +void GLoader::clearContent() +{ + clearErrorState(); + + if (_contentStatus == 4) + freeExternal(_content->getSpriteFrame()); + + if (_contentStatus == 2) + { + _playAction->setAnimation(nullptr); + _content->stopAction(_playAction); + } + + if (_content2 != nullptr) + { + _displayObject->removeChild(_content2->displayObject()); + CC_SAFE_RELEASE(_content2); + } + ((FUISprite*)_content)->clearContent(); + + _contentItem = nullptr; + _contentStatus = 0; +} + +void GLoader::updateLayout() +{ + if (_content2 == nullptr && _contentStatus == 0) + { + if (_autoSize) + { + _updatingLayout = true; + setSize(50, 30); + _updatingLayout = false; + } + return; + } + + Size contentSize = sourceSize; + + if (_autoSize) + { + _updatingLayout = true; + if (contentSize.width == 0) + contentSize.width = 50; + if (contentSize.height == 0) + contentSize.height = 30; + setSize(contentSize.width, contentSize.height); + _updatingLayout = false; + + if (_size.equals(contentSize)) + { + if (_content2 != nullptr) + { + _content2->setScale(1, 1); + _content2->setPosition(0, -_size.height); + } + else + { + _content->setScale(1, 1); + _content->setAnchorPoint(Vec2::ZERO); + _content->setPosition(0, 0); + } + return; + } + } + + float sx = 1, sy = 1; + if (_fill != LoaderFillType::NONE) + { + sx = _size.width / sourceSize.width; + sy = _size.height / sourceSize.height; + + if (sx != 1 || sy != 1) + { + if (_fill == LoaderFillType::SCALE_MATCH_HEIGHT) + sx = sy; + else if (_fill == LoaderFillType::SCALE_MATCH_WIDTH) + sy = sx; + else if (_fill == LoaderFillType::SCALE) + { + if (sx > sy) + sx = sy; + else + sy = sx; + } + else if (_fill == LoaderFillType::SCALE_NO_BORDER) + { + if (sx > sy) + sy = sx; + else + sx = sy; + } + + if (_shrinkOnly) + { + if (sx > 1) + sx = 1; + if (sy > 1) + sy = 1; + } + contentSize.width = floor(sourceSize.width * sx); + contentSize.height = floor(sourceSize.height * sy); + } + } + + if (_content2 != nullptr) + { + _content2->setScale(sx, sy); + } + else + { + if (_contentItem != nullptr) + { + if (_contentItem->scale9Grid) + { + _content->setScale(1, 1); + _content->setContentSize(contentSize); + } + else if (_contentItem->scaleByTile) + { + _content->setScale(1, 1); + _content->setContentSize(sourceSize); + _content->setTextureRect(Rect(Vec2::ZERO, contentSize)); + } + else + { + _content->setContentSize(sourceSize); + _content->setScale(sx, sy); + } + } + else + { + _content->setContentSize(sourceSize); + _content->setScale(sx, sy); + } + _content->setAnchorPoint(Vec2::ZERO); + } + + float nx; + float ny; + if (_align == TextHAlignment::CENTER) + nx = floor((_size.width - contentSize.width) / 2); + else if (_align == TextHAlignment::RIGHT) + nx = floor(_size.width - contentSize.width); + else + nx = 0; + + if (_content2 != nullptr) + { + if (_verticalAlign == TextVAlignment::CENTER) + ny = floor(-contentSize.height - (_size.height - contentSize.height) / 2); + else if (_verticalAlign == TextVAlignment::BOTTOM) + ny = -contentSize.height; + else + ny = -_size.height; + + _content2->setPosition(nx, ny); + } + else + { + if (_verticalAlign == TextVAlignment::CENTER) + ny = floor((_size.height - contentSize.height) / 2); + else if (_verticalAlign == TextVAlignment::BOTTOM) + ny = 0; + else + ny = _size.height - contentSize.height; + + _content->setPosition(nx, ny); + } +} + +void GLoader::setErrorState() +{ +} + +void GLoader::clearErrorState() +{ +} + +void GLoader::handleSizeChanged() +{ + GObject::handleSizeChanged(); + + if (!_updatingLayout) + updateLayout(); +} + +void GLoader::handleGrayedChanged() +{ + GObject::handleGrayedChanged(); + + ((FUISprite*)_content)->setGrayed(_finalGrayed); + if (_content2 != nullptr) + _content2->setGrayed(_finalGrayed); +} + +cocos2d::Value GLoader::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getColor())); + case ObjectPropID::Playing: + return Value(isPlaying()); + case ObjectPropID::Frame: + return Value(getFrame()); + case ObjectPropID::TimeScale: + if (_playAction) + return Value(_playAction->getTimeScale()); + else + return Value(1); + default: + return GObject::getProp(propId); + } +} + +void GLoader::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + case ObjectPropID::Playing: + setPlaying(value.asBool()); + break; + case ObjectPropID::Frame: + setFrame(value.asInt()); + break; + case ObjectPropID::TimeScale: + if (_playAction) + _playAction->setTimeScale(value.asFloat()); + break; + case ObjectPropID::DeltaTime: + if (_playAction) + _playAction->advance(value.asFloat()); + break; + default: + GObject::setProp(propId, value); + break; + } +} + +void GLoader::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 5); + + _url = buffer->readS(); + _align = (TextHAlignment)buffer->readByte(); + _verticalAlign = (TextVAlignment)buffer->readByte(); + _fill = (LoaderFillType)buffer->readByte(); + _shrinkOnly = buffer->readBool(); + _autoSize = buffer->readBool(); + buffer->readBool(); //_showErrorSign + _playing = buffer->readBool(); + _frame = buffer->readInt(); + + if (buffer->readBool()) + setColor((Color3B)buffer->readColor()); + int fillMethod = buffer->readByte(); + if (fillMethod != 0) + { + _content->setFillMethod((FillMethod)fillMethod); + _content->setFillOrigin((FillOrigin)buffer->readByte()); + _content->setFillClockwise(buffer->readBool()); + _content->setFillAmount(buffer->readFloat()); + } + + if (_url.length() > 0) + loadContent(); +} + +GObject* GLoader::hitTest(const Vec2& worldPoint, const Camera* camera) +{ + if (!_touchable || !_displayObject->isVisible() || !_displayObject->getParent()) + return nullptr; + + if (_content2 != nullptr) + { + GObject* obj = _content2->hitTest(worldPoint, camera); + if (obj != nullptr) + return obj; + } + + Rect rect; + rect.size = _size; + //if (isScreenPointInRect(worldPoint, camera, _displayObject->getWorldToNodeTransform(), rect, nullptr)) + if (rect.containsPoint(_displayObject->convertToNodeSpace(worldPoint))) + return this; + else + return nullptr; +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/GLoader.h b/extensions/fairygui/GLoader.h new file mode 100644 index 0000000000..73d4a07af1 --- /dev/null +++ b/extensions/fairygui/GLoader.h @@ -0,0 +1,110 @@ +#ifndef __GLOADER_H__ +#define __GLOADER_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GObject.h" + +NS_FGUI_BEGIN + +class GComponent; +class ActionMovieClip; +class FUISprite; + +class GLoader : public GObject +{ +public: + GLoader(); + virtual ~GLoader(); + + CREATE_FUNC(GLoader); + + const std::string& getURL() const { return _url; } + void setURL(const std::string& value); + + virtual const std::string& getIcon() const override { return _url; } + virtual void setIcon(const std::string& value) override { setURL(value); } + + cocos2d::TextHAlignment getAlign() const { return _align; } + void setAlign(cocos2d::TextHAlignment value); + + cocos2d::TextVAlignment getVerticalAlign() const { return _verticalAlign; } + void setVerticalAlign(cocos2d::TextVAlignment value); + + bool getAutoSize() const { return _autoSize; } + void setAutoSize(bool value); + + LoaderFillType getFill() const { return _fill; } + void setFill(LoaderFillType value); + + bool isShrinkOnly() const { return _shrinkOnly; } + void setShrinkOnly(bool value); + + const cocos2d::Size& getContentSize(); + + cocos2d::Color3B getColor() const; + void setColor(const cocos2d::Color3B& value); + + bool isPlaying() const { return _playing; } + void setPlaying(bool value); + + int getFrame() const; + void setFrame(int value); + + FillMethod getFillMethod() const; + void setFillMethod(FillMethod value); + + FillOrigin getFillOrigin() const; + void setFillOrigin(FillOrigin value); + + bool isFillClockwise() const; + void setFillClockwise(bool value); + + float getFillAmount() const; + void setFillAmount(float value); + + GComponent* getComponent() const { return _content2; } + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void handleInit() override; + virtual void handleSizeChanged() override; + virtual void handleGrayedChanged() override; + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + virtual GObject* hitTest(const cocos2d::Vec2 & worldPoint, const cocos2d::Camera * camera) override; + + virtual void loadExternal(); + virtual void freeExternal(cocos2d::SpriteFrame* spriteFrame); + void onExternalLoadSuccess(cocos2d::SpriteFrame* spriteFrame); + void onExternalLoadFailed(); + +private: + void loadContent(); + void loadFromPackage(); + void clearContent(); + void updateLayout(); + void setErrorState(); + void clearErrorState(); + + std::string _url; + cocos2d::TextHAlignment _align; + cocos2d::TextVAlignment _verticalAlign; + bool _autoSize; + LoaderFillType _fill; + bool _shrinkOnly; + bool _updatingLayout; + PackageItem* _contentItem; + int _contentStatus; + bool _playing; + int _frame; + + FUISprite* _content; + GComponent* _content2; + ActionMovieClip* _playAction; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GLoader3D.cpp b/extensions/fairygui/GLoader3D.cpp new file mode 100644 index 0000000000..2b24a19ecd --- /dev/null +++ b/extensions/fairygui/GLoader3D.cpp @@ -0,0 +1,470 @@ +#include "GLoader3D.h" +#include "GComponent.h" +#include "GMovieClip.h" +#include "UIPackage.h" +#include "display/FUISprite.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +#include "spine/spine-cocos2dx.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GLoader3D::GLoader3D() + : _autoSize(false), + _align(TextHAlignment::LEFT), + _verticalAlign(TextVAlignment::TOP), + _fill(LoaderFillType::NONE), + _shrinkOnly(false), + _updatingLayout(false), + _contentItem(nullptr), + _content(nullptr), + _container(nullptr), + _playing(true), + _frame(0), + _loop(false), + _color(255, 255, 255) +{ +} + +GLoader3D::~GLoader3D() +{ + CC_SAFE_RELEASE(_content); + CC_SAFE_RELEASE(_container); +} + +void GLoader3D::handleInit() +{ + FUIContainer* c = FUIContainer::create(); + c->retain(); + c->gOwner = this; + _displayObject = c; + + _container = FUIContainer::create(); + _container->retain(); + _container->setAnchorPoint(Vec2(0, 1)); + _displayObject->addChild(_container); +} + +void GLoader3D::setURL(const std::string& value) +{ + if (_url.compare(value) == 0) + return; + + _url = value; + loadContent(); + updateGear(7); +} + +void GLoader3D::setAlign(cocos2d::TextHAlignment value) +{ + if (_align != value) + { + _align = value; + updateLayout(); + } +} + +void GLoader3D::setVerticalAlign(cocos2d::TextVAlignment value) +{ + if (_verticalAlign != value) + { + _verticalAlign = value; + updateLayout(); + } +} + +void GLoader3D::setAutoSize(bool value) +{ + if (_autoSize != value) + { + _autoSize = value; + updateLayout(); + } +} + +void GLoader3D::setFill(LoaderFillType value) +{ + if (_fill != value) + { + _fill = value; + updateLayout(); + } +} + +void GLoader3D::setShrinkOnly(bool value) +{ + if (_shrinkOnly != value) + { + _shrinkOnly = value; + updateLayout(); + } +} + +cocos2d::Color3B GLoader3D::getColor() const +{ + return _color; +} + +void GLoader3D::setColor(const cocos2d::Color3B& value) +{ + _color = value; + if (_content != nullptr) + _content->setColor(_color); +} + +void GLoader3D::setPlaying(bool value) +{ + if (_playing != value) + { + _playing = value; + updateGear(5); + } +} + +int GLoader3D::getFrame() const +{ + return _frame; +} + +void GLoader3D::setFrame(int value) +{ + if (_frame != value) + { + _frame = value; + updateGear(5); + } +} + +void GLoader3D::setAnimationName(const std::string& value) +{ + _animationName = value; + onChange(); +} + +void GLoader3D::setSkinName(const std::string& value) +{ + _skinName = value; + onChange(); +} + +void GLoader3D::setLoop(bool value) +{ + _loop = value; + onChange(); +} + +void GLoader3D::setContent(cocos2d::Node* value) +{ + setURL(STD_STRING_EMPTY); + + _content = value; + if (_content != nullptr) + { + _content->retain(); + _container->addChild(value); + } +} + +void GLoader3D::loadContent() +{ + clearContent(); + + if (_url.length() == 0) + return; + + if (_url.compare(0, 5, "ui://") == 0) + loadFromPackage(); + else + loadExternal(); +} + +void GLoader3D::loadFromPackage() +{ + _contentItem = UIPackage::getItemByURL(_url); + + if (_contentItem != nullptr) + { + _contentItem = _contentItem->getBranch(); + sourceSize.width = _contentItem->width; + sourceSize.height = _contentItem->height; + _contentItem = _contentItem->getHighResolution(); + _contentItem->load(); + + if (_contentItem->type == PackageItemType::SPINE) + { + size_t pos = _contentItem->file.find_last_of('.'); + std::string atlasFile = _contentItem->file.substr(0, pos + 1).append("atlas"); + if (!ToolSet::isFileExist(atlasFile)) + atlasFile = _contentItem->file.substr(0, pos + 1).append("atlas.txt"); + spine::SkeletonAnimation* skeletonAni; + if (FileUtils::getInstance()->getFileExtension(_contentItem->file) == ".skel") + skeletonAni = spine::SkeletonAnimation::createWithBinaryFile(_contentItem->file, atlasFile); + else + skeletonAni = spine::SkeletonAnimation::createWithJsonFile(_contentItem->file, atlasFile); + skeletonAni->setPosition(_contentItem->skeletonAnchor->x, -_contentItem->skeletonAnchor->y); + skeletonAni->retain(); + + _content = skeletonAni; + _container->addChild(_content); + + onChangeSpine(); + } + else + { + if (_autoSize) + setSize(_contentItem->width, _contentItem->height); + + setErrorState(); + } + } + else + setErrorState(); +} + +void GLoader3D::onChange() +{ + onChangeSpine(); +} + +void GLoader3D::onChangeSpine() +{ + spine::SkeletonAnimation* skeletonAni = dynamic_cast(_content); + if (skeletonAni == nullptr) + return; + + spine::AnimationState* state = skeletonAni->getState(); + + spine::Animation* aniToUse = !_animationName.empty() ? skeletonAni->findAnimation(_animationName) : nullptr; + if (aniToUse != nullptr) + { + spine::TrackEntry* entry = state->getCurrent(0); + if (entry == nullptr || strcmp(entry->getAnimation()->getName().buffer(), _animationName.c_str()) != 0 + || entry->isComplete() && !entry->getLoop()) + entry = state->setAnimation(0, aniToUse, _loop); + else + entry->setLoop(_loop); + + if (_playing) + entry->setTimeScale(1); + else + { + entry->setTimeScale(0); + entry->setTrackTime(MathUtil::lerp(0, entry->getAnimationEnd() - entry->getAnimationStart(), _frame / 100.0f)); + } + } + else + state->clearTrack(0); + + skeletonAni->setSkin(_skinName); +} + +void GLoader3D::loadExternal() +{ +} + +void GLoader3D::freeExternal(cocos2d::SpriteFrame* spriteFrame) +{ +} + +void GLoader3D::onExternalLoadSuccess(cocos2d::SpriteFrame* spriteFrame) +{ +} + +void GLoader3D::onExternalLoadFailed() +{ + setErrorState(); +} + +void GLoader3D::clearContent() +{ + clearErrorState(); + _contentItem = nullptr; +} + +void GLoader3D::updateLayout() +{ + Size contentSize = sourceSize; + + if (_autoSize) + { + _updatingLayout = true; + if (contentSize.width == 0) + contentSize.width = 50; + if (contentSize.height == 0) + contentSize.height = 30; + setSize(contentSize.width, contentSize.height); + _updatingLayout = false; + + if (_size.equals(contentSize)) + { + _container->setScale(1, 1); + _container->setAnchorPoint(Vec2::ZERO); + _container->setPosition(0, 0); + return; + } + } + + float sx = 1, sy = 1; + if (_fill != LoaderFillType::NONE) + { + sx = _size.width / sourceSize.width; + sy = _size.height / sourceSize.height; + + if (sx != 1 || sy != 1) + { + if (_fill == LoaderFillType::SCALE_MATCH_HEIGHT) + sx = sy; + else if (_fill == LoaderFillType::SCALE_MATCH_WIDTH) + sy = sx; + else if (_fill == LoaderFillType::SCALE) + { + if (sx > sy) + sx = sy; + else + sy = sx; + } + else if (_fill == LoaderFillType::SCALE_NO_BORDER) + { + if (sx > sy) + sy = sx; + else + sx = sy; + } + + if (_shrinkOnly) + { + if (sx > 1) + sx = 1; + if (sy > 1) + sy = 1; + } + contentSize.width = floor(sourceSize.width * sx); + contentSize.height = floor(sourceSize.height * sy); + } + } + + _container->setScale(sx, sy); + _container->setAnchorPoint(Vec2::ZERO); + + float nx; + float ny; + if (_align == TextHAlignment::CENTER) + nx = floor((_size.width - contentSize.width) / 2); + else if (_align == TextHAlignment::RIGHT) + nx = floor(_size.width - contentSize.width); + else + nx = 0; + + if (_verticalAlign == TextVAlignment::CENTER) + ny = floor((_size.height - contentSize.height) / 2); + else if (_verticalAlign == TextVAlignment::BOTTOM) + ny = 0; + else + ny = _size.height - contentSize.height; + + _container->setPosition(nx, ny); +} + +void GLoader3D::setErrorState() +{ +} + +void GLoader3D::clearErrorState() +{ +} + +void GLoader3D::handleSizeChanged() +{ + GObject::handleSizeChanged(); + + if (!_updatingLayout) + updateLayout(); +} + +void GLoader3D::handleGrayedChanged() +{ + GObject::handleGrayedChanged(); +} + +cocos2d::Value GLoader3D::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getColor())); + case ObjectPropID::Playing: + return Value(isPlaying()); + case ObjectPropID::Frame: + return Value(getFrame()); + case ObjectPropID::TimeScale: + return Value(1); + default: + return GObject::getProp(propId); + } +} + +void GLoader3D::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + case ObjectPropID::Playing: + setPlaying(value.asBool()); + break; + case ObjectPropID::Frame: + setFrame(value.asInt()); + break; + case ObjectPropID::TimeScale: + break; + case ObjectPropID::DeltaTime: + break; + default: + GObject::setProp(propId, value); + break; + } +} + +void GLoader3D::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 5); + + _url = buffer->readS(); + _align = (TextHAlignment)buffer->readByte(); + _verticalAlign = (TextVAlignment)buffer->readByte(); + _fill = (LoaderFillType)buffer->readByte(); + _shrinkOnly = buffer->readBool(); + _autoSize = buffer->readBool(); + _animationName = buffer->readS(); + _skinName = buffer->readS(); + _playing = buffer->readBool(); + _frame = buffer->readInt(); + _loop = buffer->readBool(); + + if (buffer->readBool()) + setColor((Color3B)buffer->readColor()); + + if (_url.length() > 0) + loadContent(); +} + +GObject* GLoader3D::hitTest(const Vec2& worldPoint, const Camera* camera) +{ + if (!_touchable || !_displayObject->isVisible() || !_displayObject->getParent()) + return nullptr; + + Rect rect; + rect.size = _size; + //if (isScreenPointInRect(worldPoint, camera, _displayObject->getWorldToNodeTransform(), rect, nullptr)) + if (rect.containsPoint(_displayObject->convertToNodeSpace(worldPoint))) + return this; + else + return nullptr; +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/GLoader3D.h b/extensions/fairygui/GLoader3D.h new file mode 100644 index 0000000000..55ed67e5c7 --- /dev/null +++ b/extensions/fairygui/GLoader3D.h @@ -0,0 +1,110 @@ +#ifndef __GLOADER3D_H__ +#define __GLOADER3D_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GObject.h" + +NS_FGUI_BEGIN + +class GComponent; +class FUIContainer; + +class GLoader3D : public GObject +{ +public: + GLoader3D(); + virtual ~GLoader3D(); + + CREATE_FUNC(GLoader3D); + + const std::string& getURL() const { return _url; } + void setURL(const std::string& value); + + virtual const std::string& getIcon() const override { return _url; } + virtual void setIcon(const std::string& value) override { setURL(value); } + + cocos2d::TextHAlignment getAlign() const { return _align; } + void setAlign(cocos2d::TextHAlignment value); + + cocos2d::TextVAlignment getVerticalAlign() const { return _verticalAlign; } + void setVerticalAlign(cocos2d::TextVAlignment value); + + bool getAutoSize() const { return _autoSize; } + void setAutoSize(bool value); + + LoaderFillType getFill() const { return _fill; } + void setFill(LoaderFillType value); + + bool isShrinkOnly() const { return _shrinkOnly; } + void setShrinkOnly(bool value); + + const cocos2d::Node* getContent() { return _content; } + void setContent(cocos2d::Node* value); + + cocos2d::Color3B getColor() const; + void setColor(const cocos2d::Color3B& value); + + bool isPlaying() const { return _playing; } + void setPlaying(bool value); + + int getFrame() const; + void setFrame(int value); + + const std::string& getAnimationName() const { return _animationName; } + void setAnimationName(const std::string& value); + + const std::string& getSkinName() const { return _skinName; } + void setSkinName(const std::string& value); + + bool getLoop() const { return _loop; } + void setLoop(bool value); + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void handleInit() override; + virtual void handleSizeChanged() override; + virtual void handleGrayedChanged() override; + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + virtual GObject* hitTest(const cocos2d::Vec2 & worldPoint, const cocos2d::Camera * camera) override; + + virtual void loadExternal(); + virtual void freeExternal(cocos2d::SpriteFrame* spriteFrame); + void onExternalLoadSuccess(cocos2d::SpriteFrame* spriteFrame); + void onExternalLoadFailed(); + + void onChange(); + void onChangeSpine(); + +private: + void loadContent(); + void loadFromPackage(); + void clearContent(); + void updateLayout(); + void setErrorState(); + void clearErrorState(); + + std::string _url; + cocos2d::TextHAlignment _align; + cocos2d::TextVAlignment _verticalAlign; + bool _autoSize; + LoaderFillType _fill; + bool _shrinkOnly; + bool _updatingLayout; + PackageItem* _contentItem; + bool _playing; + int _frame; + bool _loop; + std::string _animationName; + std::string _skinName; + cocos2d::Color3B _color; + + FUIContainer* _container; + cocos2d::Node* _content; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GMovieClip.cpp b/extensions/fairygui/GMovieClip.cpp new file mode 100644 index 0000000000..390d7cc87b --- /dev/null +++ b/extensions/fairygui/GMovieClip.cpp @@ -0,0 +1,483 @@ +#include "GMovieClip.h" +#include "PackageItem.h" +#include "display/FUISprite.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GMovieClip::GMovieClip() + : _playAction(nullptr), + _content(nullptr), + _playing(true) +{ + _sizeImplType = 1; + _touchDisabled = true; +} + +GMovieClip::~GMovieClip() +{ + CC_SAFE_RELEASE(_playAction); +} + +void GMovieClip::handleInit() +{ + _content = FUISprite::create(); + _content->retain(); + + _playAction = ActionMovieClip::create(nullptr); + _playAction->retain(); + + _displayObject = _content; +} + +void GMovieClip::setPlaySettings(int start, int end, int times, int endAt, std::function completeCallback) +{ + _playAction->setPlaySettings(start, end, times, endAt, completeCallback); +} + +void GMovieClip::setPlaying(bool value) +{ + if (_playing != value) + { + _playing = value; + if (_playing) + _content->runAction(_playAction); + else + _content->stopAction(_playAction); + } +} + +int GMovieClip::getFrame() const +{ + return _playAction->getFrame(); +} +void GMovieClip::setFrame(int value) +{ + _playAction->setFrame(value); +} + +float GMovieClip::getTimeScale() const +{ + return _playAction->getTimeScale(); +} +void GMovieClip::setTimeScale(float value) +{ + _playAction->setTimeScale(value); +} + +void GMovieClip::advance(float time) +{ + _playAction->advance(time); +} + +FlipType GMovieClip::getFlip() const +{ + if (_content->isFlippedX() && _content->isFlippedY()) + return FlipType::BOTH; + else if (_content->isFlippedX()) + return FlipType::HORIZONTAL; + else if (_content->isFlippedY()) + return FlipType::VERTICAL; + else + return FlipType::NONE; +} + +void GMovieClip::setFlip(FlipType value) +{ + _content->setFlippedX(value == FlipType::HORIZONTAL || value == FlipType::BOTH); + _content->setFlippedY(value == FlipType::VERTICAL || value == FlipType::BOTH); +} + +void GMovieClip::setColor(const cocos2d::Color3B& value) +{ + _content->setColor(value); +} + +void GMovieClip::handleGrayedChanged() +{ + GObject::handleGrayedChanged(); + + ((FUISprite*)_content)->setGrayed(_finalGrayed); +} + +cocos2d::Value GMovieClip::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getColor())); + case ObjectPropID::Playing: + return Value(isPlaying()); + case ObjectPropID::Frame: + return Value(getFrame()); + case ObjectPropID::TimeScale: + return Value(_playAction->getTimeScale()); + default: + return GObject::getProp(propId); + } +} + +void GMovieClip::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + case ObjectPropID::Playing: + setPlaying(value.asBool()); + break; + case ObjectPropID::Frame: + setFrame(value.asInt()); + break; + case ObjectPropID::TimeScale: + _playAction->setTimeScale(value.asFloat()); + break; + case ObjectPropID::DeltaTime: + _playAction->advance(value.asFloat()); + break; + default: + GObject::setProp(propId, value); + break; + } +} + +void GMovieClip::constructFromResource() +{ + PackageItem* contentItem = _packageItem->getBranch(); + sourceSize.width = contentItem->width; + sourceSize.height = contentItem->height; + initSize = sourceSize; + + contentItem = contentItem->getHighResolution(); + contentItem->load(); + + _playAction->setAnimation(contentItem->animation, contentItem->repeatDelay, contentItem->swing); + _content->runAction(_playAction); + + setSize(sourceSize.width, sourceSize.height); +} + +void GMovieClip::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 5); + + if (buffer->readBool()) + setColor((Color3B)buffer->readColor()); + setFlip((FlipType)buffer->readByte()); + setFrame(buffer->readInt()); + setPlaying(buffer->readBool()); +} + +ActionMovieClip::ActionMovieClip() : _animation(nullptr), +_frame(0), +_frameElapsed(0), +_repeatedCount(0), +_repeatDelay(0), +_start(0), +_end(0), +_times(0), +_endAt(0), +_status(0), +_displayFrame(-1), +_swing(false), +_reversed(false), +_timeScale(1) +{ +} + +ActionMovieClip::~ActionMovieClip() +{ + CC_SAFE_RELEASE(_animation); +} + +ActionMovieClip* ActionMovieClip::create(cocos2d::Animation* animation, float repeatDelay, bool swing) +{ + ActionMovieClip* action = new (std::nothrow) ActionMovieClip(); + if (action) + { + action->setAnimation(animation, repeatDelay, swing); + action->autorelease(); + return action; + } + + delete action; + return nullptr; +} + +bool ActionMovieClip::isDone() const +{ + return false; +} + +void ActionMovieClip::step(float dt) +{ + if (_animation == nullptr) + return; + + auto frames = _animation->getFrames(); + int frameCount = (int)frames.size(); + if (frameCount == 0) + return; + + if (frameCount == 0 || _status == 3) + return; + + if (_timeScale != 1) + dt *= _timeScale; + + _frameElapsed += dt; + float tt = (frames.at(_frame)->getDelayUnits() + ((_frame == 0 && _repeatedCount > 0) ? _repeatDelay : 0)) * _animation->getDelayPerUnit(); + if (_frame == 0 && _repeatedCount > 0) + tt += _repeatDelay; + if (_frameElapsed < tt) + return; + + _frameElapsed -= tt; + if (_frameElapsed > _animation->getDelayPerUnit()) + _frameElapsed = _animation->getDelayPerUnit(); + + if (_swing) + { + if (_reversed) + { + _frame--; + if (_frame <= 0) + { + _frame = 0; + _repeatedCount++; + _reversed = !_reversed; + } + } + else + { + _frame++; + if (_frame > frameCount - 1) + { + _frame = MAX(0, frameCount - 2); + _repeatedCount++; + _reversed = !_reversed; + } + } + } + else + { + _frame++; + if (_frame > frameCount - 1) + { + _frame = 0; + _repeatedCount++; + } + } + + if (_status == 1) //new loop + { + _frame = _start; + _frameElapsed = 0; + _status = 0; + } + else if (_status == 2) //ending + { + _frame = _endAt; + _frameElapsed = 0; + _status = 3; //ended + + if (_completeCallback) + _completeCallback(); + } + else + { + if (_frame == _end) + { + if (_times > 0) + { + _times--; + if (_times == 0) + _status = 2; //ending + else + _status = 1; //new loop + } + else if (_start != 0) + _status = 1; //new loop + } + } + + drawFrame(); +} + +void ActionMovieClip::startWithTarget(Node* target) +{ + Action::startWithTarget(target); + + drawFrame(); +} + +ActionMovieClip* ActionMovieClip::reverse() const +{ + CC_ASSERT(0); + return nullptr; +} + +ActionMovieClip* ActionMovieClip::clone() const +{ + return ActionMovieClip::create(_animation->clone(), _repeatDelay, _swing); +} + +void ActionMovieClip::setFrame(int value) +{ + if (_animation == nullptr) + return; + + auto frames = _animation->getFrames(); + int frameCount = (int)frames.size(); + + if (value >= frameCount) + value = frameCount - 1; + + _frame = value; + _frameElapsed = 0; + _displayFrame = -1; + drawFrame(); +} + +void ActionMovieClip::advance(float time) +{ + if (_animation == nullptr) + return; + + auto frames = _animation->getFrames(); + int frameCount = (int)frames.size(); + if (frameCount == 0) + return; + + int beginFrame = _frame; + bool beginReversed = _reversed; + float backupTime = time; + while (true) + { + float tt = (frames.at(_frame)->getDelayUnits() + ((_frame == 0 && _repeatedCount > 0) ? _repeatDelay : 0)) * _animation->getDelayPerUnit(); + if (_frame == 0 && _repeatedCount > 0) + tt += _repeatDelay; + if (time < tt) + { + _frameElapsed = 0; + break; + } + + time -= tt; + + if (_swing) + { + if (_reversed) + { + _frame--; + if (_frame <= 0) + { + _frame = 0; + _repeatedCount++; + _reversed = !_reversed; + } + } + else + { + _frame++; + if (_frame > frameCount - 1) + { + _frame = MAX(0, frameCount - 2); + _repeatedCount++; + _reversed = !_reversed; + } + } + } + else + { + _frame++; + if (_frame > frameCount - 1) + { + _frame = 0; + _repeatedCount++; + } + } + + if (_frame == beginFrame && _reversed == beginReversed) + { + float roundTime = backupTime - time; + time -= (int)floor(time / roundTime) * roundTime; + } + } +} + +void ActionMovieClip::setPlaySettings(int start, int end, int times, int endAt, std::function completeCallback) +{ + auto frames = _animation->getFrames(); + int frameCount = (int)frames.size(); + + _start = start; + _end = end; + if (_end == -1 || _end > frameCount - 1) + _end = frameCount - 1; + _times = times; + _endAt = endAt; + if (_endAt == -1) + _endAt = _end; + _status = 0; + _completeCallback = completeCallback; + + setFrame(start); +} + +void ActionMovieClip::setAnimation(cocos2d::Animation* animation, float repeatDelay, bool swing) +{ + if (_animation != animation) + { + CC_SAFE_RETAIN(animation); + CC_SAFE_RELEASE(_animation); + _animation = animation; + } + + if (_animation == nullptr) + return; + + _repeatDelay = repeatDelay; + _swing = swing; + _completeCallback = nullptr; + + auto frames = _animation->getFrames(); + int frameCount = (int)frames.size(); + if (_end == -1 || _end > frameCount - 1) + _end = frameCount - 1; + if (_endAt == -1 || _endAt > frameCount - 1) + _endAt = frameCount - 1; + + if (_frame < 0 || _frame > frameCount - 1) + _frame = frameCount - 1; + + _displayFrame = -1; + _frameElapsed = 0; + _repeatedCount = 0; + _reversed = false; +} + +void ActionMovieClip::drawFrame() +{ + if (_target == nullptr || _animation == nullptr) + return; + + auto frames = _animation->getFrames(); + int frameCount = (int)frames.size(); + + if (_frame == _displayFrame || frameCount == 0) + return; + + _displayFrame = _frame; + AnimationFrame* frame = frames.at(_frame); + //auto blend = static_cast(_target)->getBlendFunc(); + static_cast(_target)->setSpriteFrame(frame->getSpriteFrame()); + //static_cast(_target)->setBlendFunc(blend); +} + +NS_FGUI_END diff --git a/extensions/fairygui/GMovieClip.h b/extensions/fairygui/GMovieClip.h new file mode 100644 index 0000000000..6032e6280a --- /dev/null +++ b/extensions/fairygui/GMovieClip.h @@ -0,0 +1,106 @@ +#ifndef __GMOVIECLIP_H__ +#define __GMOVIECLIP_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GObject.h" + +NS_FGUI_BEGIN + +class ActionMovieClip; + +class GMovieClip : public GObject +{ +public: + GMovieClip(); + virtual ~GMovieClip(); + + CREATE_FUNC(GMovieClip); + + bool isPlaying() const { return _playing; } + void setPlaying(bool value); + + int getFrame() const; + void setFrame(int value); + + float getTimeScale() const; + void setTimeScale(float value); + + void advance(float time); + + FlipType getFlip() const; + void setFlip(FlipType value); + + cocos2d::Color3B getColor() const { return _content->getColor(); } + void setColor(const cocos2d::Color3B& value); + + //from start to end(-1 means ending) repeat times(0 means infinite loop) when all is over, stopping at endAt(-1 means same value of end) + void setPlaySettings(int start = 0, int end = -1, int times = 0, int endAt = -1, std::function completeCallback = nullptr); + + virtual void constructFromResource() override; + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void handleInit() override; + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + virtual void handleGrayedChanged() override; + +private: + cocos2d::Sprite* _content; + ActionMovieClip* _playAction; + bool _playing; +}; + +class ActionMovieClip : public cocos2d::Action +{ +public: + static ActionMovieClip* create(cocos2d::Animation *animation, float repeatDelay = 0, bool swing =false); + ActionMovieClip(); + ~ActionMovieClip(); + + virtual bool isDone() const override; + virtual void step(float dt) override; + virtual void startWithTarget(cocos2d::Node *target) override; + virtual ActionMovieClip* reverse() const override; + virtual ActionMovieClip* clone() const override; + + int getFrame() { return _frame; } + void setFrame(int value); + + float getTimeScale() const { return _timeScale; } + void setTimeScale(float value) { _timeScale = value; } + + void advance(float time); + + void setPlaySettings(int start, int end, int times, int endAt, std::function completeCallback = nullptr); + void setAnimation(cocos2d::Animation *animation, float repeatDelay = 0, bool swing = false); + +private: + void drawFrame(); + + cocos2d::Animation* _animation; + int _frame; + float _frameElapsed; + int _repeatedCount; + bool _reversed; + float _timeScale; + + float _repeatDelay; + bool _swing; + std::function _completeCallback; + int _displayFrame; + + int _start; + int _end; + int _times; + int _endAt; + int _status; //0-none, 1-next loop, 2-ending, 3-ended + + CC_DISALLOW_COPY_AND_ASSIGN(ActionMovieClip); +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GObject.cpp b/extensions/fairygui/GObject.cpp new file mode 100644 index 0000000000..7789e195a8 --- /dev/null +++ b/extensions/fairygui/GObject.cpp @@ -0,0 +1,1026 @@ +#include "GObject.h" +#include "GGroup.h" +#include "GList.h" +#include "GRoot.h" +#include "UIConfig.h" +#include "UIPackage.h" +#include "display/FUISprite.h" +#include "gears/GearDisplay.h" +#include "gears/GearDisplay2.h" +#include "utils/ByteBuffer.h" +#include "utils/WeakPtr.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GObject* GObject::_draggingObject = nullptr; + +static Vec2 sGlobalDragStart; +static Rect sGlobalRect; +static bool sUpdateInDragging; + +GObject::GObject() : _scale{1, 1}, + _sizePercentInGroup(0.0f), + _pivotAsAnchor(false), + _alpha(1.0f), + _rotation(0.0f), + _visible(true), + _internalVisible(true), + _handlingController(false), + _touchable(true), + _grayed(false), + _finalGrayed(false), + _draggable(false), + _dragBounds(nullptr), + _dragTesting(false), + _sortingOrder(0), + _focusable(false), + _pixelSnapping(false), + _group(nullptr), + _parent(nullptr), + _displayObject(nullptr), + _sizeImplType(0), + _underConstruct(false), + _gearLocked(false), + _packageItem(nullptr), + _data(nullptr), + _touchDisabled(false), + _alignToBL(false), + _weakPtrRef(0) +{ + static uint64_t _gInstanceCounter = 1; + _uid = _gInstanceCounter++; + std::stringstream ss; + ss << _uid; + id = ss.str(); + _relations = new Relations(this); + + for (int i = 0; i < 10; i++) + _gears[i] = nullptr; +} + +GObject::~GObject() +{ + removeFromParent(); + + if (_displayObject) + { + _displayObject->removeFromParent(); + CC_SAFE_RELEASE(_displayObject); + } + for (int i = 0; i < 10; i++) + CC_SAFE_DELETE(_gears[i]); + CC_SAFE_DELETE(_relations); + CC_SAFE_DELETE(_dragBounds); + + if (_weakPtrRef > 0) + WeakPtr::markDisposed(this); +} + +bool GObject::init() +{ + handleInit(); + + if (_displayObject != nullptr) + { + _displayObject->setAnchorPoint(Vec2(0, 1)); + _displayObject->setCascadeOpacityEnabled(true); + _displayObject->setOnEnterCallback(CC_CALLBACK_0(GObject::onEnter, this)); + _displayObject->setOnExitCallback(CC_CALLBACK_0(GObject::onExit, this)); + } + return true; +} + +void GObject::setX(float value) +{ + setPosition(value, _position.y); +} + +void GObject::setY(float value) +{ + setPosition(_position.x, value); +} + +void GObject::setPosition(float xv, float yv) +{ + if (_position.x != xv || _position.y != yv) + { + float dx = xv - _position.x; + float dy = yv - _position.y; + _position.x = xv; + _position.y = yv; + + handlePositionChanged(); + + GGroup* g = dynamic_cast(this); + if (g != nullptr) + g->moveChildren(dx, dy); + + updateGear(1); + + if (_parent != nullptr && dynamic_cast(_parent) == nullptr) + { + _parent->setBoundsChangedFlag(); + if (_group != nullptr) + _group->setBoundsChangedFlag(true); + + dispatchEvent(UIEventType::PositionChange); + } + + if (_draggingObject == this && !sUpdateInDragging) + sGlobalRect = localToGlobal(Rect(Vec2::ZERO, _size)); + } +} + +float GObject::getXMin() const +{ + return _pivotAsAnchor ? (_position.x - _size.width * _pivot.x) : _position.x; +} + +void GObject::setXMin(float value) +{ + if (_pivotAsAnchor) + setPosition(value + _size.width * _pivot.x, _position.y); + else + setPosition(value, _position.y); +} + +float GObject::getYMin() const +{ + return _pivotAsAnchor ? (_position.y - _size.height * _pivot.y) : _position.y; +} + +void GObject::setYMin(float value) +{ + if (_pivotAsAnchor) + setPosition(_position.x, value + _size.height * _pivot.y); + else + setPosition(_position.x, value); +} + +void GObject::setPixelSnapping(bool value) +{ + if (_pixelSnapping != value) + { + _pixelSnapping = value; + handlePositionChanged(); + } +} + +void GObject::setSize(float wv, float hv, bool ignorePivot /*= false*/) +{ + if (_rawSize.width != wv || _rawSize.height != hv) + { + _rawSize.width = wv; + _rawSize.height = hv; + if (wv < minSize.width) + wv = minSize.width; + else if (maxSize.width > 0 && wv > maxSize.width) + wv = maxSize.width; + if (hv < minSize.height) + hv = minSize.height; + else if (maxSize.height > 0 && hv > maxSize.height) + hv = maxSize.height; + float dWidth = wv - _size.width; + float dHeight = hv - _size.height; + _size.width = wv; + _size.height = hv; + + handleSizeChanged(); + + if (_pivot.x != 0 || _pivot.y != 0) + { + if (!_pivotAsAnchor) + { + if (!ignorePivot) + setPosition(_position.x - _pivot.x * dWidth, _position.y - _pivot.y * dHeight); + else + handlePositionChanged(); + } + else + handlePositionChanged(); + } + else + handlePositionChanged(); + + GGroup* g = dynamic_cast(this); + if (g != nullptr) + g->resizeChildren(dWidth, dHeight); + + updateGear(2); + + if (_parent != nullptr) + { + _relations->onOwnerSizeChanged(dWidth, dHeight, _pivotAsAnchor || !ignorePivot); + _parent->setBoundsChangedFlag(); + if (_group != nullptr) + _group->setBoundsChangedFlag(); + } + + dispatchEvent(UIEventType::SizeChange); + } +} + +void GObject::setSizeDirectly(float wv, float hv) +{ + _rawSize.width = wv; + _rawSize.height = hv; + if (wv < 0) + wv = 0; + if (hv < 0) + hv = 0; + _size.width = wv; + _size.height = hv; +} + +void GObject::center(bool restraint /*= false*/) +{ + GComponent* r; + if (_parent != nullptr) + r = _parent; + else + r = UIRoot; + + setPosition((int)((r->_size.width - _size.width) / 2), (int)((r->_size.height - _size.height) / 2)); + if (restraint) + { + addRelation(r, RelationType::Center_Center); + addRelation(r, RelationType::Middle_Middle); + } +} + +void GObject::makeFullScreen() +{ + setSize(UIRoot->getWidth(), UIRoot->getHeight()); +} + +void GObject::setPivot(float xv, float yv, bool asAnchor) +{ + if (_pivot.x != xv || _pivot.y != yv || _pivotAsAnchor != asAnchor) + { + _pivot.set(xv, yv); + _pivotAsAnchor = asAnchor; + if (_displayObject != nullptr) + _displayObject->setAnchorPoint(Vec2(_pivot.x, 1 - _pivot.y)); + handlePositionChanged(); + } +} + +void GObject::setScale(float xv, float yv) +{ + if (_scale.x != xv || _scale.y != yv) + { + _scale.x = xv; + _scale.y = yv; + handleScaleChanged(); + + updateGear(2); + } +} + +void GObject::setSkewX(float value) +{ + _displayObject->setRotationSkewX(value); +} + +void GObject::setSkewY(float value) +{ + _displayObject->setRotationSkewY(value); +} + +void GObject::setRotation(float value) +{ + if (_rotation != value) + { + _rotation = value; + _displayObject->setRotation(_rotation); + updateGear(3); + } +} + +void GObject::setAlpha(float value) +{ + if (_alpha != value) + { + _alpha = value; + handleAlphaChanged(); + updateGear(3); + } +} + +void GObject::setGrayed(bool value) +{ + if (_grayed != value || _finalGrayed != value) + { + _grayed = value; + handleGrayedChanged(); + updateGear(3); + } +} + +void GObject::setVisible(bool value) +{ + if (_visible != value) + { + _visible = value; + handleVisibleChanged(); + if (_parent != nullptr) + _parent->setBoundsChangedFlag(); + if (_group != nullptr && _group->isExcludeInvisibles()) + _group->setBoundsChangedFlag(); + } +} + +bool GObject::internalVisible() const +{ + return _internalVisible && (_group == nullptr || _group->internalVisible()); +} + +bool GObject::internalVisible2() const +{ + return _visible && (_group == nullptr || _group->internalVisible2()); +} + +bool GObject::internalVisible3() const +{ + return _visible && _internalVisible; +} + +void GObject::setTouchable(bool value) +{ + _touchable = value; +} + +void GObject::setSortingOrder(int value) +{ + if (value < 0) + value = 0; + if (_sortingOrder != value) + { + int old = _sortingOrder; + _sortingOrder = value; + if (_parent != nullptr) + _parent->childSortingOrderChanged(this, old, _sortingOrder); + } +} + +void GObject::setGroup(GGroup* value) +{ + if (_group != value) + { + if (_group != nullptr) + _group->setBoundsChangedFlag(); + _group = value; + if (_group != nullptr) + _group->setBoundsChangedFlag(); + handleVisibleChanged(); + if (_parent) + _parent->childStateChanged(this); + } +} + +const std::string& GObject::getText() const +{ + return STD_STRING_EMPTY; +} + +void GObject::setText(const std::string& text) +{ +} + +const std::string& GObject::getIcon() const +{ + return STD_STRING_EMPTY; +} + +void GObject::setIcon(const std::string& text) +{ +} + +void GObject::setTooltips(const std::string& value) +{ + _tooltips = value; + if (!_tooltips.empty()) + { + addEventListener(UIEventType::RollOver, CC_CALLBACK_1(GObject::onRollOver, this), EventTag(this)); + addEventListener(UIEventType::RollOut, CC_CALLBACK_1(GObject::onRollOut, this), EventTag(this)); + } +} + +void GObject::onRollOver(EventContext* context) +{ + getRoot()->showTooltips(_tooltips); +} + +void GObject::onRollOut(EventContext* context) +{ + getRoot()->hideTooltips(); +} + +void GObject::setDraggable(bool value) +{ + if (_draggable != value) + { + _draggable = value; + initDrag(); + } +} + +void GObject::setDragBounds(const cocos2d::Rect& value) +{ + if (_dragBounds == nullptr) + _dragBounds = new Rect(); + *_dragBounds = value; +} + +void GObject::startDrag(int touchId) +{ + dragBegin(touchId); +} + +void GObject::stopDrag() +{ + dragEnd(); +} + +std::string GObject::getResourceURL() const +{ + if (_packageItem != nullptr) + return "ui://" + _packageItem->owner->getId() + _packageItem->id; + else + return STD_STRING_EMPTY; +} + +Vec2 GObject::localToGlobal(const Vec2& pt) +{ + Vec2 pt2 = pt; + if (_pivotAsAnchor) + { + pt2.x += _size.width * _pivot.x; + pt2.y += _size.height * _pivot.y; + } + pt2.y = _size.height - pt2.y; + pt2 = _displayObject->convertToWorldSpace(pt2); + return UIRoot->worldToRoot(pt2); +} + +cocos2d::Rect GObject::localToGlobal(const cocos2d::Rect& rect) +{ + Rect ret; + Vec2 v = localToGlobal(rect.origin); + ret.origin.x = v.x; + ret.origin.y = v.y; + v = localToGlobal(Vec2(rect.getMaxX(), rect.getMaxY())); + ret.size.width = v.x - ret.origin.x; + ret.size.height = v.y - ret.origin.y; + return ret; +} + +Vec2 GObject::globalToLocal(const Vec2& pt) +{ + Vec2 pt2 = UIRoot->rootToWorld(pt); + pt2 = _displayObject->convertToNodeSpace(pt2); + pt2.y = _size.height - pt2.y; + if (_pivotAsAnchor) + { + pt2.x -= _size.width * _pivot.x; + pt2.y -= _size.height * _pivot.y; + } + return pt2; +} + +cocos2d::Rect GObject::globalToLocal(const cocos2d::Rect& rect) +{ + Rect ret; + Vec2 v = globalToLocal(rect.origin); + ret.origin.x = v.x; + ret.origin.y = v.y; + v = globalToLocal(Vec2(rect.getMaxX(), rect.getMaxY())); + ret.size.width = v.x - ret.origin.x; + ret.size.height = v.y - ret.origin.y; + return ret; +} + +cocos2d::Rect GObject::transformRect(const cocos2d::Rect& rect, GObject* targetSpace) +{ + if (targetSpace == this) + return rect; + + if (targetSpace == _parent) // optimization + { + return Rect((_position.x + rect.origin.x) * _scale.x, + (_position.y + rect.origin.y) * _scale.y, + rect.size.width * _scale.x, + rect.size.height * _scale.y); + } + else + { + float result[4]{FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX}; + + transformRectPoint(rect.origin, result, targetSpace); + transformRectPoint(Vec2(rect.getMaxX(), rect.origin.y), result, targetSpace); + transformRectPoint(Vec2(rect.origin.x, rect.getMaxY()), result, targetSpace); + transformRectPoint(Vec2(rect.getMaxX(), rect.getMaxY()), result, targetSpace); + + return Rect(result[0], result[1], result[2] - result[0], result[3] - result[1]); + } +} + +void GObject::transformRectPoint(const Vec2& pt, float rect[], GObject* targetSpace) +{ + Vec2 v = localToGlobal(pt); + if (targetSpace != nullptr) + v = targetSpace->globalToLocal(v); + + if (rect[0] > v.x) + rect[0] = v.x; + if (rect[2] < v.x) + rect[2] = v.x; + if (rect[1] > v.y) + rect[1] = v.y; + if (rect[3] < v.y) + rect[3] = v.y; +} + +void GObject::addRelation(GObject* target, RelationType relationType, bool usePercent) +{ + _relations->add(target, relationType, usePercent); +} + +void GObject::removeRelation(GObject* target, RelationType relationType) +{ + _relations->remove(target, relationType); +} + +GearBase* GObject::getGear(int index) +{ + GearBase* gear = _gears[index]; + if (gear == nullptr) + { + gear = GearBase::create(this, index); + _gears[index] = gear; + } + return gear; +} + +void GObject::updateGear(int index) +{ + if (_underConstruct || _gearLocked) + return; + + GearBase* gear = _gears[index]; + if (gear != nullptr && gear->getController() != nullptr) + gear->updateState(); +} + +bool GObject::checkGearController(int index, GController* c) +{ + return _gears[index] != nullptr && _gears[index]->getController() == c; +} + +void GObject::updateGearFromRelations(int index, float dx, float dy) +{ + if (_gears[index] != nullptr) + _gears[index]->updateFromRelations(dx, dy); +} + +uint32_t GObject::addDisplayLock() +{ + GearDisplay* gearDisplay = (GearDisplay*)_gears[0]; + if (gearDisplay != nullptr && gearDisplay->getController() != nullptr) + { + uint32_t ret = gearDisplay->addLock(); + checkGearDisplay(); + + return ret; + } + else + return 0; +} + +void GObject::releaseDisplayLock(uint32_t token) +{ + GearDisplay* gearDisplay = (GearDisplay*)_gears[0]; + if (gearDisplay != nullptr && gearDisplay->getController() != nullptr) + { + gearDisplay->releaseLock(token); + checkGearDisplay(); + } +} + +void GObject::checkGearDisplay() +{ + if (_handlingController) + return; + + bool connected = _gears[0] == nullptr || ((GearDisplay*)_gears[0])->isConnected(); + if (_gears[8] != nullptr) + connected = dynamic_cast(_gears[8])->evaluate(connected); + + if (connected != _internalVisible) + { + _internalVisible = connected; + if (_parent != nullptr) + _parent->childStateChanged(this); + if (_group != nullptr && _group->isExcludeInvisibles()) + _group->setBoundsChangedFlag(); + } +} + +bool GObject::onStage() const +{ + return _displayObject->getScene() != nullptr; +} + +GObject* GObject::findParent() const +{ + if (_parent != nullptr) + return _parent; + + Node* pn = _displayObject->getParent(); + if (pn == nullptr) + return nullptr; + + while (pn != nullptr) + { + FUIContainer* fc = dynamic_cast(pn); + if (fc != nullptr && fc->gOwner) + return fc->gOwner; + + pn = pn->getParent(); + } + + return nullptr; +} + +GRoot* GObject::getRoot() const +{ + GObject* p = (GObject*)this; + while (p->_parent != nullptr) + p = p->_parent; + + GRoot* root = dynamic_cast(p); + if (root != nullptr) + return root; + else + return UIRoot; +} + +void GObject::removeFromParent() +{ + if (_parent != nullptr) + _parent->removeChild(this); +} + +cocos2d::Value GObject::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Text: + return Value(getText()); + case ObjectPropID::Icon: + return Value(getIcon()); + default: + return Value::Null; + } +} + +void GObject::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Text: + return setText(value.asString()); + case ObjectPropID::Icon: + return setIcon(value.asString()); + default: + break; + } +} + +void GObject::constructFromResource() +{ +} + +GObject* GObject::hitTest(const Vec2& worldPoint, const Camera* camera) +{ + if (_touchDisabled || !_touchable || !_displayObject->isVisible() || !_displayObject->getParent()) + return nullptr; + + Rect rect; + rect.size = _size; + //if (isScreenPointInRect(worldPoint, camera, _displayObject->getWorldToNodeTransform(), rect, nullptr)) + if (rect.containsPoint(_displayObject->convertToNodeSpace(worldPoint))) + return this; + else + return nullptr; +} + +void GObject::handleInit() +{ + _displayObject = Node::create(); + _displayObject->retain(); +} + +void GObject::onEnter() +{ + dispatchEvent(UIEventType::Enter); +} + +void GObject::onExit() +{ + dispatchEvent(UIEventType::Exit); +} + +void GObject::handlePositionChanged() +{ + if (_displayObject) + { + Vec2 pt = _position; + pt.y = -pt.y; + if (!_pivotAsAnchor) + { + pt.x += _size.width * _pivot.x; + pt.y -= _size.height * _pivot.y; + } + if (_alignToBL) + { + if (_displayObject->getParent()) + pt.y += _displayObject->getParent()->getContentSize().height; + else if (_parent != nullptr) + pt.y += _parent->_displayObject->getContentSize().height; + } + if (_pixelSnapping) + { + pt.x = (int)pt.x; + pt.y = (int)pt.y; + } + _displayObject->setPosition(pt); + } +} + +void GObject::handleSizeChanged() +{ + if (_displayObject) + { + if (_sizeImplType == 0 || sourceSize.width == 0 || sourceSize.height == 0) + _displayObject->setContentSize(_size); + else + _displayObject->setScale(_scale.x * _size.width / sourceSize.width, _scale.y * _size.height / sourceSize.height); + } +} + +void GObject::handleScaleChanged() +{ + if (_sizeImplType == 0 || sourceSize.width == 0 || sourceSize.height == 0) + _displayObject->setScale(_scale.x, _scale.y); + else + _displayObject->setScale(_scale.x * _size.width / sourceSize.width, _scale.y * _size.height / sourceSize.height); +} + +void GObject::handleAlphaChanged() +{ + _displayObject->setOpacity(_alpha * 255); +} + +void GObject::handleGrayedChanged() +{ + _finalGrayed = (_parent && _parent->_finalGrayed) || _grayed; +} + +void GObject::handleVisibleChanged() +{ + _displayObject->setVisible(internalVisible2()); +} + +void GObject::handleControllerChanged(GController* c) +{ + _handlingController = true; + for (int i = 0; i < 10; i++) + { + GearBase* gear = _gears[i]; + if (gear != nullptr && gear->getController() == c) + gear->apply(); + } + _handlingController = false; + + checkGearDisplay(); +} + +void GObject::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + buffer->seek(beginPos, 0); + buffer->skip(5); + + id = buffer->readS(); + name = buffer->readS(); + float f1 = buffer->readInt(); + float f2 = buffer->readInt(); + setPosition(f1, f2); + + if (buffer->readBool()) + { + initSize.width = buffer->readInt(); + initSize.height = buffer->readInt(); + setSize(initSize.width, initSize.height, true); + } + + if (buffer->readBool()) + { + minSize.width = buffer->readInt(); + maxSize.width = buffer->readInt(); + minSize.height = buffer->readInt(); + maxSize.height = buffer->readInt(); + } + + if (buffer->readBool()) + { + f1 = buffer->readFloat(); + f2 = buffer->readFloat(); + setScale(f1, f2); + } + + if (buffer->readBool()) + { + f1 = buffer->readFloat(); + f2 = buffer->readFloat(); + setSkewX(f1); + setSkewY(f2); + } + + if (buffer->readBool()) + { + f1 = buffer->readFloat(); + f2 = buffer->readFloat(); + setPivot(f1, f2, buffer->readBool()); + } + + f1 = buffer->readFloat(); + if (f1 != 1) + setAlpha(f1); + + f1 = buffer->readFloat(); + if (f1 != 0) + setRotation(f1); + + if (!buffer->readBool()) + setVisible(false); + if (!buffer->readBool()) + setTouchable(false); + if (buffer->readBool()) + setGrayed(true); + buffer->readByte(); //blendMode + buffer->readByte(); //filter + + const std::string& str = buffer->readS(); + if (!str.empty()) + _customData = Value(str); +} + +void GObject::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + buffer->seek(beginPos, 1); + + const std::string& str = buffer->readS(); + if (!str.empty()) + setTooltips(str); + + int groupId = buffer->readShort(); + if (groupId >= 0) + _group = dynamic_cast(_parent->getChildAt(groupId)); + + buffer->seek(beginPos, 2); + + int cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + GearBase* gear = getGear(buffer->readByte()); + gear->setup(buffer); + + buffer->setPos(nextPos); + } +} + +void GObject::initDrag() +{ + if (_draggable) + { + addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GObject::onTouchBegin, this), EventTag(this)); + addEventListener(UIEventType::TouchMove, CC_CALLBACK_1(GObject::onTouchMove, this), EventTag(this)); + addEventListener(UIEventType::TouchEnd, CC_CALLBACK_1(GObject::onTouchEnd, this), EventTag(this)); + } + else + { + removeEventListener(UIEventType::TouchBegin, EventTag(this)); + removeEventListener(UIEventType::TouchMove, EventTag(this)); + removeEventListener(UIEventType::TouchEnd, EventTag(this)); + } +} + +void GObject::dragBegin(int touchId) +{ + if (_draggingObject != nullptr) + { + GObject* tmp = _draggingObject; + _draggingObject->stopDrag(); + _draggingObject = nullptr; + tmp->dispatchEvent(UIEventType::DragEnd); + } + + sGlobalDragStart = UIRoot->getTouchPosition(touchId); + sGlobalRect = localToGlobal(Rect(Vec2::ZERO, _size)); + + _draggingObject = this; + _dragTesting = true; + UIRoot->getInputProcessor()->addTouchMonitor(touchId, this); + + addEventListener(UIEventType::TouchMove, CC_CALLBACK_1(GObject::onTouchMove, this), EventTag(this)); + addEventListener(UIEventType::TouchEnd, CC_CALLBACK_1(GObject::onTouchEnd, this), EventTag(this)); +} + +void GObject::dragEnd() +{ + if (_draggingObject == this) + { + _dragTesting = false; + _draggingObject = nullptr; + } +} + +void GObject::onTouchBegin(EventContext* context) +{ + _dragTouchStartPos = context->getInput()->getPosition(); + _dragTesting = true; + context->captureTouch(); +} + +void GObject::onTouchMove(EventContext* context) +{ + InputEvent* evt = context->getInput(); + + if (_draggingObject != this && _draggable && _dragTesting) + { + int sensitivity; +#ifdef CC_PLATFORM_PC + sensitivity = UIConfig::clickDragSensitivity; +#else + sensitivity = UIConfig::touchDragSensitivity; +#endif + if (std::abs(_dragTouchStartPos.x - evt->getPosition().x) < sensitivity && std::abs(_dragTouchStartPos.y - evt->getPosition().y) < sensitivity) + return; + + _dragTesting = false; + if (!dispatchEvent(UIEventType::DragStart)) + dragBegin(evt->getTouchId()); + } + + if (_draggingObject == this) + { + float xx = evt->getPosition().x - sGlobalDragStart.x + sGlobalRect.origin.x; + float yy = evt->getPosition().y - sGlobalDragStart.y + sGlobalRect.origin.y; + + if (_dragBounds != nullptr) + { + Rect rect = UIRoot->localToGlobal(*_dragBounds); + if (xx < rect.origin.x) + xx = rect.origin.x; + else if (xx + sGlobalRect.size.width > rect.getMaxX()) + { + xx = rect.getMaxX() - sGlobalRect.size.width; + if (xx < rect.origin.x) + xx = rect.origin.x; + } + + if (yy < rect.origin.y) + yy = rect.origin.y; + else if (yy + sGlobalRect.size.height > rect.getMaxY()) + { + yy = rect.getMaxY() - sGlobalRect.size.height; + if (yy < rect.origin.y) + yy = rect.origin.y; + } + } + + Vec2 pt = _parent->globalToLocal(Vec2(xx, yy)); + + sUpdateInDragging = true; + setPosition(round(pt.x), round(pt.y)); + sUpdateInDragging = false; + + dispatchEvent(UIEventType::DragMove); + } +} + +void GObject::onTouchEnd(EventContext* context) +{ + if (_draggingObject == this) + { + _draggingObject = nullptr; + dispatchEvent(UIEventType::DragEnd); + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/GObject.h b/extensions/fairygui/GObject.h new file mode 100644 index 0000000000..76d817e28d --- /dev/null +++ b/extensions/fairygui/GObject.h @@ -0,0 +1,265 @@ +#ifndef __GOBJECT_H__ +#define __GOBJECT_H__ + +#include "Controller.h" +#include "FairyGUIMacros.h" +#include "Relations.h" +#include "cocos2d.h" +#include "event/UIEventDispatcher.h" +#include "gears/GearBase.h" + +NS_FGUI_BEGIN + +class GComponent; +class GGroup; +class ByteBuffer; +class GRoot; +class PackageItem; +class GTreeNode; + +class GObject : public UIEventDispatcher +{ +public: + static GObject* getDraggingObject() { return _draggingObject; } + + GObject(); + virtual ~GObject(); + + CREATE_FUNC(GObject); + + float getX() const { return _position.x; }; + void setX(float value); + float getY() const { return _position.y; }; + void setY(float value); + const cocos2d::Vec2& getPosition() const { return _position; } + void setPosition(float xv, float yv); + float getXMin() const; + void setXMin(float value); + float getYMin() const; + void setYMin(float value); + + bool isPixelSnapping() const { return _pixelSnapping; } + void setPixelSnapping(bool value); + + float getWidth() const { return _size.width; } + void setWidth(float value) { setSize(value, _rawSize.height); } + float getHeight() const { return _size.height; } + void setHeight(float value) { setSize(_rawSize.width, value); } + const cocos2d::Size& getSize() const { return _size; } + void setSize(float wv, float hv, bool ignorePivot = false); + + void center(bool restraint = false); + void makeFullScreen(); + + const cocos2d::Vec2& getPivot() const { return _pivot; } + void setPivot(float xv, float yv, bool asAnchor = false); + bool isPivotAsAnchor() const { return _pivotAsAnchor; } + + float getScaleX() const { return _scale.x; } + void setScaleX(float value) { setScale(value, _scale.y); } + float getScaleY() const { return _scale.y; } + void setScaleY(float value) { setScale(_scale.x, value); } + const cocos2d::Vec2& getScale() const { return _scale; } + void setScale(float xv, float yv); + + float getSkewX() const { return _displayObject->getSkewX(); } + void setSkewX(float value); + + float getSkewY() const { return _displayObject->getSkewY(); } + void setSkewY(float value); + + float getRotation() const { return _rotation; } + void setRotation(float value); + + float getAlpha() const { return _alpha; } + void setAlpha(float value); + + bool isGrayed() const { return _grayed; } + void setGrayed(bool value); + + bool isVisible() const { return _visible; } + void setVisible(bool value); + + bool isTouchable() const { return _touchable; } + void setTouchable(bool value); + + int getSortingOrder() const { return _sortingOrder; } + void setSortingOrder(int value); + + GGroup* getGroup() const { return _group; } + void setGroup(GGroup* value); + + virtual const std::string& getText() const; + virtual void setText(const std::string& text); + + virtual const std::string& getIcon() const; + virtual void setIcon(const std::string& text); + + const std::string& getTooltips() const { return _tooltips; } + void setTooltips(const std::string& value); + + void* getData() const { return _data; }; + void setData(void* value) { _data = value; } + const cocos2d::Value& getCustomData() const { return _customData; } + void setCustomData(const cocos2d::Value& value) { _customData = value; } + + bool isDraggable() const { return _draggable; } + void setDraggable(bool value); + cocos2d::Rect* getDragBounds() const { return _dragBounds; } + void setDragBounds(const cocos2d::Rect& value); + + void startDrag(int touchId = -1); + void stopDrag(); + + std::string getResourceURL() const; + + PackageItem* getPackageItem() const { return _packageItem; } + + cocos2d::Vec2 globalToLocal(const cocos2d::Vec2& pt); + cocos2d::Rect globalToLocal(const cocos2d::Rect& rect); + cocos2d::Vec2 localToGlobal(const cocos2d::Vec2& pt); + cocos2d::Rect localToGlobal(const cocos2d::Rect& rect); + cocos2d::Rect transformRect(const cocos2d::Rect& rect, GObject* targetSpace); + + Relations* relations() { return _relations; } + void addRelation(GObject* target, RelationType relationType, bool usePercent = false); + void removeRelation(GObject* target, RelationType relationType); + + GearBase* getGear(int index); + bool checkGearController(int index, GController* c); + uint32_t addDisplayLock(); + void releaseDisplayLock(uint32_t token); + + GComponent* getParent() const { return _parent; } + GObject* findParent() const; + cocos2d::Node* displayObject() const { return _displayObject; } + GRoot* getRoot() const; + bool onStage() const; + void removeFromParent(); + + void addClickListener(const EventCallback& callback) { addEventListener(UIEventType::Click, callback); } + void addClickListener(const EventCallback& callback, const EventTag& tag) { addEventListener(UIEventType::Click, callback, tag); } + void removeClickListener(const EventTag& tag) { removeEventListener(UIEventType::Click, tag); } + + virtual cocos2d::Value getProp(ObjectPropID propId); + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value); + + virtual void constructFromResource(); + virtual GObject* hitTest(const cocos2d::Vec2& worldPoint, const cocos2d::Camera* camera); + + template + T* as(); + + GTreeNode* treeNode() const { return _treeNode; } + + std::string id; + std::string name; + cocos2d::Size sourceSize; + cocos2d::Size initSize; + cocos2d::Size minSize; + cocos2d::Size maxSize; + + //internal use + bool _underConstruct; + bool _gearLocked; + bool _alignToBL; + +protected: + GComponent* _parent; + cocos2d::Node* _displayObject; + PackageItem* _packageItem; + int _sizeImplType; + bool _touchDisabled; + + virtual void handleInit(); + virtual void handleSizeChanged(); + virtual void handleScaleChanged(); + virtual void handleGrayedChanged(); + virtual void handlePositionChanged(); + virtual void handleControllerChanged(GController* c); + virtual void handleAlphaChanged(); + virtual void handleVisibleChanged(); + + virtual void onEnter(); + virtual void onExit(); + + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos); + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos); + + bool init(); + + void updateGear(int index); + void checkGearDisplay(); + + void setSizeDirectly(float wv, float hv); + + cocos2d::Vec2 _position; + cocos2d::Size _size; + cocos2d::Size _rawSize; + cocos2d::Vec2 _pivot; + cocos2d::Vec2 _scale; + bool _pivotAsAnchor; + float _alpha; + float _rotation; + bool _visible; + bool _touchable; + bool _grayed; + bool _finalGrayed; + +private: + bool internalVisible() const; + bool internalVisible2() const; + bool internalVisible3() const; + void updateGearFromRelations(int index, float dx, float dy); + void transformRectPoint(const cocos2d::Vec2& pt, float rect[], GObject* targetSpace); + + void initDrag(); + void dragBegin(int touchId); + void dragEnd(); + void onTouchBegin(EventContext* context); + void onTouchMove(EventContext* context); + void onTouchEnd(EventContext* context); + void onRollOver(EventContext* context); + void onRollOut(EventContext* context); + + bool _internalVisible; + bool _handlingController; + bool _draggable; + int _sortingOrder; + bool _focusable; + std::string _tooltips; + bool _pixelSnapping; + GGroup* _group; + float _sizePercentInGroup; + Relations* _relations; + GearBase* _gears[10]; + void* _data; + cocos2d::Value _customData; + cocos2d::Vec2 _dragTouchStartPos; + cocos2d::Rect* _dragBounds; + bool _dragTesting; + GTreeNode* _treeNode; + + uint64_t _uid; + size_t _weakPtrRef; + + static GObject* _draggingObject; + + friend class GComponent; + friend class GGroup; + friend class RelationItem; + friend class UIObjectFactory; + friend class WeakPtr; + friend class UIPackage; + friend class GTree; +}; + +template +inline T* GObject::as() +{ + return dynamic_cast(this); +} + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GObjectPool.cpp b/extensions/fairygui/GObjectPool.cpp new file mode 100644 index 0000000000..786641ffcb --- /dev/null +++ b/extensions/fairygui/GObjectPool.cpp @@ -0,0 +1,41 @@ +#include "GObjectPool.h" +#include "GObject.h" +#include "UIPackage.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GObjectPool::GObjectPool() +{ +} + +GObjectPool::~GObjectPool() +{ +} + +GObject* GObjectPool::getObject(const std::string & url) +{ + std::string url2 = UIPackage::normalizeURL(url); + if (url2.length() == 0) + return nullptr; + + GObject* ret; + Vector& arr = _pool[url2]; + if (!arr.empty()) + { + ret = arr.back(); + ret->retain(); + arr.popBack(); + ret->autorelease(); + } + else + ret = UIPackage::createObjectFromURL(url2); + return ret; +} + +void GObjectPool::returnObject(GObject* obj) +{ + _pool[obj->getResourceURL()].pushBack(obj); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/GObjectPool.h b/extensions/fairygui/GObjectPool.h new file mode 100644 index 0000000000..cd48917d0a --- /dev/null +++ b/extensions/fairygui/GObjectPool.h @@ -0,0 +1,26 @@ +#ifndef __GOBJECTPOOL_H__ +#define __GOBJECTPOOL_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; + +class GObjectPool +{ +public: + GObjectPool(); + ~GObjectPool(); + + GObject* getObject(const std::string& url); + void returnObject(GObject* obj); + +private: + std::unordered_map> _pool; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GProgressBar.cpp b/extensions/fairygui/GProgressBar.cpp new file mode 100644 index 0000000000..5702e7481a --- /dev/null +++ b/extensions/fairygui/GProgressBar.cpp @@ -0,0 +1,234 @@ +#include "GProgressBar.h" +#include "GImage.h" +#include "GLoader.h" +#include "PackageItem.h" +#include "tween/GTween.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GProgressBar::GProgressBar() + : _min(0), + _max(100), + _value(0), + _titleType(ProgressTitleType::PERCENT), + _titleObject(nullptr), + _barObjectH(nullptr), + _barObjectV(nullptr), + _barMaxWidth(0), + _barMaxHeight(0), + _barMaxWidthDelta(0), + _barMaxHeightDelta(0), + _barStartX(0), + _barStartY(0) +{ +} + +GProgressBar::~GProgressBar() +{ +} + +void GProgressBar::setTitleType(ProgressTitleType value) +{ + if (_titleType != value) + { + _titleType = value; + update(_value); + } +} + +void GProgressBar::setMin(double value) +{ + if (_min != value) + { + _min = value; + update(_value); + } +} + +void GProgressBar::setMax(double value) +{ + if (_max != value) + { + _max = value; + update(_value); + } +} + +void GProgressBar::setValue(double value) +{ + if (_value != value) + { + GTween::kill(this, TweenPropType::Progress, false); + + _value = value; + update(_value); + } +} + +void GProgressBar::tweenValue(double value, float duration) +{ + double oldValule; + + GTweener* twener = GTween::getTween(this, TweenPropType::Progress); + if (twener != nullptr) + { + oldValule = twener->value.d; + twener->kill(false); + } + else + oldValule = _value; + + _value = value; + GTween::toDouble(oldValule, _value, duration) + ->setEase(EaseType::Linear) + ->setTarget(this, TweenPropType::Progress); +} + +void GProgressBar::update(double newValue) +{ + float percent; + if (_max == _min) + percent = 0; + else + percent = clampf((newValue - _min) / (_max - _min), 0, 1); + + if (_titleObject != nullptr) + { + std::ostringstream oss; + switch (_titleType) + { + case ProgressTitleType::PERCENT: + oss << floor(percent * 100) << "%"; + break; + + case ProgressTitleType::VALUE_MAX: + oss << floor(newValue) << "/" << floor(_max); + break; + + case ProgressTitleType::VALUE: + oss << floor(newValue); + break; + + case ProgressTitleType::MAX: + oss << floor(_max); + break; + } + _titleObject->setText(oss.str()); + } + + float fullWidth = this->getWidth() - _barMaxWidthDelta; + float fullHeight = this->getHeight() - _barMaxHeightDelta; + if (!_reverse) + { + if (_barObjectH != nullptr) + { + if (!setFillAmount(_barObjectH, percent)) + _barObjectH->setWidth(round(fullWidth * percent)); + } + if (_barObjectV != nullptr) + { + if (!setFillAmount(_barObjectV, percent)) + _barObjectV->setHeight(round(fullHeight * percent)); + } + } + else + { + if (_barObjectH != nullptr) + { + if (!setFillAmount(_barObjectH, 1 - percent)) + { + _barObjectH->setWidth(round(fullWidth * percent)); + _barObjectH->setX(_barStartX + (fullWidth - _barObjectH->getWidth())); + } + } + if (_barObjectV != nullptr) + { + if (!setFillAmount(_barObjectV, 1 - percent)) + { + _barObjectV->setHeight(round(fullHeight * percent)); + _barObjectV->setY(_barStartY + (fullHeight - _barObjectV->getHeight())); + } + } + } +} + +bool GProgressBar::setFillAmount(GObject* bar, float amount) +{ + GImage* image = nullptr; + GLoader* loader = nullptr; + + if ((image = dynamic_cast(bar)) != nullptr && image->getFillMethod() != FillMethod::None) + image->setFillAmount(amount); + else if ((loader = dynamic_cast(bar)) != nullptr && loader->getFillMethod() != FillMethod::None) + loader->setFillAmount(amount); + else + return false; + + return true; +} + +void GProgressBar::handleSizeChanged() +{ + GComponent::handleSizeChanged(); + + if (_barObjectH != nullptr) + _barMaxWidth = getWidth() - _barMaxWidthDelta; + if (_barObjectV != nullptr) + _barMaxHeight = getHeight() - _barMaxHeightDelta; + + if (!_underConstruct) + update(_value); +} + +void GProgressBar::constructExtension(ByteBuffer* buffer) +{ + buffer->seek(0, 6); + + _titleType = (ProgressTitleType)buffer->readByte(); + _reverse = buffer->readBool(); + + _titleObject = getChild("title"); + _barObjectH = getChild("bar"); + _barObjectV = getChild("bar_v"); + + if (_barObjectH != nullptr) + { + _barMaxWidth = _barObjectH->getWidth(); + _barMaxWidthDelta = getWidth() - _barMaxWidth; + _barStartX = _barObjectH->getX(); + } + if (_barObjectV != nullptr) + { + _barMaxHeight = _barObjectV->getHeight(); + _barMaxHeightDelta = getHeight() - _barMaxHeight; + _barStartY = _barObjectV->getY(); + } +} + +void GProgressBar::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GComponent::setup_afterAdd(buffer, beginPos); + + if (!buffer->seek(beginPos, 6)) + { + update(_value); + return; + } + + if ((ObjectType)buffer->readByte() != _packageItem->objectType) + { + update(_value); + return; + } + + _value = buffer->readInt(); + _max = buffer->readInt(); + if (buffer->version >= 2) + _min = buffer->readInt(); + + update(_value); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/GProgressBar.h b/extensions/fairygui/GProgressBar.h new file mode 100644 index 0000000000..47a1dd1029 --- /dev/null +++ b/extensions/fairygui/GProgressBar.h @@ -0,0 +1,60 @@ +#ifndef __GPROGRESSBAR_H__ +#define __GPROGRESSBAR_H__ + +#include "FairyGUIMacros.h" +#include "GComponent.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GProgressBar : public GComponent +{ +public: + GProgressBar(); + virtual ~GProgressBar(); + + CREATE_FUNC(GProgressBar); + + ProgressTitleType getTitleType() const { return _titleType; } + void setTitleType(ProgressTitleType value); + + double getMin() const { return _min; } + void setMin(double value); + + double getMax() const { return _max; } + void setMax(double value); + + double getValue() const { return _value; } + void setValue(double value); + + void tweenValue(double value, float duration); + void update(double newValue); + +protected: + virtual void handleSizeChanged() override; + virtual void constructExtension(ByteBuffer* buffer) override; + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + + bool setFillAmount(GObject* bar, float amount); + +private: + double _min; + double _max; + double _value; + ProgressTitleType _titleType; + bool _reverse; + + GObject* _titleObject; + GObject* _barObjectH; + GObject* _barObjectV; + float _barMaxWidth; + float _barMaxHeight; + float _barMaxWidthDelta; + float _barMaxHeightDelta; + float _barStartX; + float _barStartY; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GRichTextField.cpp b/extensions/fairygui/GRichTextField.cpp new file mode 100644 index 0000000000..21b142f4cb --- /dev/null +++ b/extensions/fairygui/GRichTextField.cpp @@ -0,0 +1,114 @@ +#include "GRichTextField.h" +#include "utils/UBBParser.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GRichTextField::GRichTextField() : + _richText(nullptr), + _updatingSize(false) +{ +} + +GRichTextField::~GRichTextField() +{ +} + +void GRichTextField::handleInit() +{ + _richText = FUIRichText::create(); + _richText->retain(); + _richText->setCascadeOpacityEnabled(true); + + _displayObject = _richText; +} + +void GRichTextField::applyTextFormat() +{ + _richText->applyTextFormat(); + updateGear(4); + if (!_underConstruct) + updateSize(); +} + +void GRichTextField::setAutoSize(AutoSizeType value) +{ + _autoSize = value; + switch (value) + { + case AutoSizeType::NONE: + _richText->setOverflow(Label::Overflow::CLAMP); + break; + case AutoSizeType::BOTH: + _richText->setOverflow(Label::Overflow::NONE); + break; + case AutoSizeType::HEIGHT: + _richText->setOverflow(Label::Overflow::RESIZE_HEIGHT); + break; + case AutoSizeType::SHRINK: + _richText->setOverflow(Label::Overflow::SHRINK); + break; + } + + _richText->setDimensions(_size.width, _size.height); + if (!_underConstruct) + updateSize(); +} + +void GRichTextField::setSingleLine(bool value) +{ +} + +void GRichTextField::setTextFieldText() +{ + if (_ubbEnabled) + { + std::string parsedText = UBBParser::getInstance()->parse(_text.c_str()); + if (_templateVars != nullptr) + parsedText = parseTemplate(parsedText.c_str()); + _richText->setText(parsedText); + } + else + { + if (_templateVars != nullptr) + _richText->setText(parseTemplate(_text.c_str())); + else + _richText->setText(_text); + } +} + +void GRichTextField::updateSize() +{ + if (_updatingSize) + return; + + _updatingSize = true; + + Size sz = _richText->getContentSize(); + if (_autoSize == AutoSizeType::BOTH) + setSize(sz.width, sz.height); + else if (_autoSize == AutoSizeType::HEIGHT) + setHeight(sz.height); + + _updatingSize = false; +} + +void GRichTextField::handleSizeChanged() +{ + if (_updatingSize) + return; + + if (_autoSize != AutoSizeType::BOTH) + { + _richText->setDimensions(_size.width, _size.height); + + if (_autoSize == AutoSizeType::HEIGHT) + { + if (!_text.empty()) + setSizeDirectly(_size.width, _richText->getContentSize().height); + } + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/GRichTextField.h b/extensions/fairygui/GRichTextField.h new file mode 100644 index 0000000000..6b81d3f973 --- /dev/null +++ b/extensions/fairygui/GRichTextField.h @@ -0,0 +1,41 @@ +#ifndef __GRICHTEXTFIELD_H__ +#define __GRICHTEXTFIELD_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GTextField.h" +#include "display/FUIRichText.h" + +NS_FGUI_BEGIN + +class GRichTextField : public GTextField +{ +public: + GRichTextField(); + virtual ~GRichTextField(); + + CREATE_FUNC(GRichTextField); + + virtual void setAutoSize(AutoSizeType value) override; + + virtual bool isSingleLine() const override { return false; } + virtual void setSingleLine(bool value) override; + + virtual TextFormat* getTextFormat() const override { return _richText->getTextFormat(); } + virtual void applyTextFormat() override; + +protected: + virtual void handleInit() override; + virtual void handleSizeChanged() override; + + virtual void setTextFieldText() override; + virtual void updateSize() override; + +private: + FUIRichText* _richText; + bool _updatingSize; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GRoot.cpp b/extensions/fairygui/GRoot.cpp new file mode 100644 index 0000000000..0d89de818c --- /dev/null +++ b/extensions/fairygui/GRoot.cpp @@ -0,0 +1,569 @@ +#include "GRoot.h" +#include "AudioEngine.h" +#include "UIConfig.h" +#include "UIPackage.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +#if COCOS2D_VERSION < 0x00040000 +using namespace cocos2d::experimental; +#endif + +GRoot* GRoot::_inst = nullptr; +bool GRoot::_soundEnabled = true; +float GRoot::_soundVolumeScale = 1.0f; +int GRoot::contentScaleLevel = 0; + +GRoot* GRoot::create(Scene* scene, int zOrder) +{ + GRoot* pRet = new (std::nothrow) GRoot(); + if (pRet && pRet->initWithScene(scene, zOrder)) + { + pRet->autorelease(); + return pRet; + } + else + { + delete pRet; + pRet = nullptr; + return nullptr; + } +} + +GRoot::GRoot() : _windowSizeListener(nullptr), + _inputProcessor(nullptr), + _modalLayer(nullptr), + _modalWaitPane(nullptr), + _tooltipWin(nullptr), + _defaultTooltipWin(nullptr) +{ +} + +GRoot::~GRoot() +{ + delete _inputProcessor; + CC_SAFE_RELEASE(_modalWaitPane); + CC_SAFE_RELEASE(_defaultTooltipWin); + CC_SAFE_RELEASE(_modalLayer); + CALL_LATER_CANCEL(GRoot, doShowTooltipsWin); + + if (_windowSizeListener) + Director::getInstance()->getEventDispatcher()->removeEventListener(_windowSizeListener); +} + +void GRoot::showWindow(Window* win) +{ + addChild(win); + adjustModalLayer(); +} + +void GRoot::hideWindow(Window* win) +{ + win->hide(); +} + +void GRoot::hideWindowImmediately(Window* win) +{ + if (win->getParent() == this) + removeChild(win); + + adjustModalLayer(); +} + +void GRoot::bringToFront(Window* win) +{ + int cnt = numChildren(); + int i; + if (_modalLayer->getParent() != nullptr && !win->isModal()) + i = getChildIndex(_modalLayer) - 1; + else + i = cnt - 1; + + for (; i >= 0; i--) + { + GObject* g = getChildAt(i); + if (g == win) + return; + if (dynamic_cast(g)) + break; + } + + if (i >= 0) + setChildIndex(win, i); +} + +void GRoot::closeAllExceptModals() +{ + Vector map(_children); + + for (const auto& child : map) + { + if (dynamic_cast(child) && !((Window*)child)->isModal()) + hideWindowImmediately((Window*)child); + } +} + +void GRoot::closeAllWindows() +{ + Vector map(_children); + + for (const auto& child : map) + { + if (dynamic_cast(child)) + hideWindowImmediately((Window*)child); + } +} + +Window* GRoot::getTopWindow() +{ + int cnt = numChildren(); + for (int i = cnt - 1; i >= 0; i--) + { + GObject* child = getChildAt(i); + if (dynamic_cast(child)) + { + return (Window*)child; + } + } + + return nullptr; +} + +GGraph* GRoot::getModalLayer() +{ + if (_modalLayer == nullptr) + createModalLayer(); + + return _modalLayer; +} + +void GRoot::createModalLayer() +{ + _modalLayer = GGraph::create(); + _modalLayer->retain(); + _modalLayer->drawRect(getWidth(), getHeight(), 0, Color4F::WHITE, UIConfig::modalLayerColor); + _modalLayer->addRelation(this, RelationType::Size); +} + +void GRoot::adjustModalLayer() +{ + if (_modalLayer == nullptr) + createModalLayer(); + + int cnt = numChildren(); + + if (_modalWaitPane != nullptr && _modalWaitPane->getParent() != nullptr) + setChildIndex(_modalWaitPane, cnt - 1); + + for (int i = cnt - 1; i >= 0; i--) + { + GObject* child = getChildAt(i); + if (dynamic_cast(child) && ((Window*)child)->isModal()) + { + if (_modalLayer->getParent() == nullptr) + addChildAt(_modalLayer, i); + else + setChildIndexBefore(_modalLayer, i); + return; + } + } + + if (_modalLayer->getParent() != nullptr) + removeChild(_modalLayer); +} + +bool GRoot::hasModalWindow() +{ + return _modalLayer != nullptr && _modalLayer->getParent() != nullptr; +} + +void GRoot::showModalWait() +{ + getModalWaitingPane(); + if (_modalWaitPane) + addChild(_modalWaitPane); +} + +void GRoot::closeModalWait() +{ + if (_modalWaitPane != nullptr && _modalWaitPane->getParent() != nullptr) + removeChild(_modalWaitPane); +} + +GObject* GRoot::getModalWaitingPane() +{ + if (!UIConfig::globalModalWaiting.empty()) + { + if (_modalWaitPane == nullptr) + { + _modalWaitPane = UIPackage::createObjectFromURL(UIConfig::globalModalWaiting); + _modalWaitPane->setSortingOrder(INT_MAX); + _modalWaitPane->retain(); + } + + _modalWaitPane->setSize(getWidth(), getHeight()); + _modalWaitPane->addRelation(this, RelationType::Size); + + return _modalWaitPane; + } + else + return nullptr; +} + +bool GRoot::isModalWaiting() +{ + return (_modalWaitPane != nullptr) && _modalWaitPane->onStage(); +} + +cocos2d::Vec2 GRoot::getTouchPosition(int touchId) +{ + return _inputProcessor->getTouchPosition(touchId); +} + +GObject* GRoot::getTouchTarget() +{ + return _inputProcessor->getRecentInput()->getTarget(); +} + +cocos2d::Vec2 GRoot::worldToRoot(const cocos2d::Vec2 &pt) +{ + cocos2d::Vec2 pos = _displayObject->convertToNodeSpace(pt); + pos.y = getHeight() - pos.y; + return pos; +} + +cocos2d::Vec2 GRoot::rootToWorld(const cocos2d::Vec2 &pt) +{ + cocos2d::Vec2 pos = pt; + pos.y = getHeight() - pos.y; + pos = _displayObject->convertToWorldSpace(pos); + return pos; +} + +void GRoot::showPopup(GObject* popup) +{ + showPopup(popup, nullptr, PopupDirection::AUTO); +} + +void GRoot::showPopup(GObject* popup, GObject* target, PopupDirection dir) +{ + if (!_popupStack.empty()) + hidePopup(popup); + + _popupStack.push_back(WeakPtr(popup)); + + if (target != nullptr) + { + GObject* p = target; + while (p != nullptr) + { + if (p->getParent() == this) + { + if (popup->getSortingOrder() < p->getSortingOrder()) + { + popup->setSortingOrder(p->getSortingOrder()); + } + break; + } + p = p->getParent(); + } + } + + addChild(popup); + adjustModalLayer(); + + if (dynamic_cast(popup) && target == nullptr && dir == PopupDirection::AUTO) + return; + + Vec2 pos = getPoupPosition(popup, target, dir); + popup->setPosition(pos.x, pos.y); +} + +void GRoot::togglePopup(GObject* popup) +{ + togglePopup(popup, nullptr, PopupDirection::AUTO); +} + +void GRoot::togglePopup(GObject* popup, GObject* target, PopupDirection dir) +{ + if (std::find(_justClosedPopups.cbegin(), _justClosedPopups.cend(), popup) != _justClosedPopups.cend()) + return; + + showPopup(popup, target, dir); +} + +void GRoot::hidePopup() +{ + hidePopup(nullptr); +} + +void GRoot::hidePopup(GObject* popup) +{ + if (popup != nullptr) + { + auto it = std::find(_popupStack.cbegin(), _popupStack.cend(), popup); + if (it != _popupStack.cend()) + { + int k = (int)(it - _popupStack.cbegin()); + for (int i = (int)_popupStack.size() - 1; i >= k; i--) + { + closePopup(_popupStack.back().ptr()); + _popupStack.pop_back(); + } + } + } + else + { + for (const auto& it : _popupStack) + closePopup(it.ptr()); + _popupStack.clear(); + } +} + +void GRoot::closePopup(GObject* target) +{ + if (target && target->getParent() != nullptr) + { + if (dynamic_cast(target)) + ((Window*)target)->hide(); + else + removeChild(target); + } +} + +void GRoot::checkPopups() +{ + _justClosedPopups.clear(); + if (!_popupStack.empty()) + { + GObject* mc = _inputProcessor->getRecentInput()->getTarget(); + bool handled = false; + while (mc != this && mc != nullptr) + { + auto it = std::find(_popupStack.cbegin(), _popupStack.cend(), mc); + if (it != _popupStack.cend()) + { + int k = (int)(it - _popupStack.cbegin()); + for (int i = (int)_popupStack.size() - 1; i > k; i--) + { + closePopup(_popupStack.back().ptr()); + _popupStack.pop_back(); + } + handled = true; + break; + } + mc = mc->findParent(); + } + + if (!handled) + { + for (int i = (int)_popupStack.size() - 1; i >= 0; i--) + { + GObject* popup = _popupStack[i].ptr(); + if (popup) + { + _justClosedPopups.push_back(WeakPtr(popup)); + closePopup(popup); + } + } + _popupStack.clear(); + } + } +} + +bool GRoot::hasAnyPopup() +{ + return !_popupStack.empty(); +} + +cocos2d::Vec2 GRoot::getPoupPosition(GObject* popup, GObject* target, PopupDirection dir) +{ + Vec2 pos; + Vec2 size; + if (target != nullptr) + { + pos = target->localToGlobal(Vec2::ZERO); + pos = this->globalToLocal(pos); + size = target->localToGlobal(target->getSize()); + size = this->globalToLocal(size); + size -= pos; + } + else + { + pos = globalToLocal(_inputProcessor->getRecentInput()->getPosition()); + } + float xx, yy; + xx = pos.x; + if (xx + popup->getWidth() > getWidth()) + xx = xx + size.x - popup->getWidth(); + yy = pos.y + size.y; + if ((dir == PopupDirection::AUTO && yy + popup->getHeight() > getHeight()) || dir == PopupDirection::UP) + { + yy = pos.y - popup->getHeight() - 1; + if (yy < 0) + { + yy = 0; + xx += size.x / 2; + } + } + + return Vec2(round(xx), round(yy)); +} + +void GRoot::showTooltips(const std::string& msg) +{ + if (_defaultTooltipWin == nullptr) + { + const std::string& resourceURL = UIConfig::tooltipsWin; + if (resourceURL.empty()) + { + CCLOGWARN("FairyGUI: UIConfig.tooltipsWin not defined"); + return; + } + + _defaultTooltipWin = UIPackage::createObjectFromURL(resourceURL); + _defaultTooltipWin->setTouchable(false); + _defaultTooltipWin->retain(); + } + + _defaultTooltipWin->setText(msg); + showTooltipsWin(_defaultTooltipWin); +} + +void GRoot::showTooltipsWin(GObject* tooltipWin) +{ + hideTooltips(); + + _tooltipWin = tooltipWin; + CALL_LATER(GRoot, doShowTooltipsWin, 0.1f); +} + +void GRoot::doShowTooltipsWin() +{ + if (_tooltipWin == nullptr) + return; + + Vec2 pt = _inputProcessor->getRecentInput()->getPosition(); + float xx = pt.x + 10; + float yy = pt.y + 20; + + pt = globalToLocal(Vec2(xx, yy)); + xx = pt.x; + yy = pt.y; + + if (xx + _tooltipWin->getWidth() > getWidth()) + xx = xx - _tooltipWin->getWidth(); + if (yy + _tooltipWin->getHeight() > getHeight()) + { + yy = yy - _tooltipWin->getHeight() - 1; + if (yy < 0) + yy = 0; + } + + _tooltipWin->setPosition(round(xx), round(yy)); + addChild(_tooltipWin); +} + +void GRoot::hideTooltips() +{ + if (_tooltipWin != nullptr) + { + if (_tooltipWin->getParent() != nullptr) + removeChild(_tooltipWin); + _tooltipWin = nullptr; + } +} + +void GRoot::playSound(const std::string& url, float volumnScale) +{ + if (!_soundEnabled) + return; + + PackageItem* pi = UIPackage::getItemByURL(url); + if (pi) + AudioEngine::play2d(pi->file, false, _soundVolumeScale * volumnScale); +} + +void GRoot::setSoundEnabled(bool value) +{ + _soundEnabled = value; +} + +void GRoot::setSoundVolumeScale(float value) +{ + _soundVolumeScale = value; +} + +void GRoot::onTouchEvent(int eventType) +{ + if (eventType == UIEventType::TouchBegin) + { + if (_tooltipWin != nullptr) + hideTooltips(); + + checkPopups(); + } +} + +void GRoot::handlePositionChanged() +{ + _displayObject->setPosition(0, _size.height); +} + +void GRoot::onEnter() +{ + GComponent::onEnter(); + _inst = this; +} + +void GRoot::onExit() +{ + GComponent::onExit(); + if (_inst == this) + _inst = nullptr; +} + +bool GRoot::initWithScene(cocos2d::Scene* scene, int zOrder) +{ + if (!GComponent::init()) + return false; + + if (_inst == nullptr) + _inst = this; + + _inputProcessor = new InputProcessor(this); + _inputProcessor->setCaptureCallback(CC_CALLBACK_1(GRoot::onTouchEvent, this)); + +#ifdef CC_PLATFORM_PC + _windowSizeListener = Director::getInstance()->getEventDispatcher()->addCustomEventListener(GLViewImpl::EVENT_WINDOW_RESIZED, CC_CALLBACK_0(GRoot::onWindowSizeChanged, this)); +#endif + onWindowSizeChanged(); + + scene->addChild(_displayObject, zOrder); + + return true; +} + +void GRoot::onWindowSizeChanged() +{ + const cocos2d::Size& rs = Director::getInstance()->getOpenGLView()->getDesignResolutionSize(); + setSize(rs.width, rs.height); + + updateContentScaleLevel(); +} + +void GRoot::updateContentScaleLevel() +{ + float ss = Director::getInstance()->getContentScaleFactor(); + if (ss >= 3.5f) + contentScaleLevel = 3; //x4 + else if (ss >= 2.5f) + contentScaleLevel = 2; //x3 + else if (ss >= 1.5f) + contentScaleLevel = 1; //x2 + else + contentScaleLevel = 0; +} + +NS_FGUI_END diff --git a/extensions/fairygui/GRoot.h b/extensions/fairygui/GRoot.h new file mode 100644 index 0000000000..92de573a29 --- /dev/null +++ b/extensions/fairygui/GRoot.h @@ -0,0 +1,102 @@ +#ifndef __GROOT_H__ +#define __GROOT_H__ + +#include "FairyGUIMacros.h" +#include "GComponent.h" +#include "GGraph.h" +#include "Window.h" +#include "cocos2d.h" +#include "event/InputProcessor.h" + +NS_FGUI_BEGIN + +class WeakPtr; + +class GRoot : public GComponent +{ +public: + GRoot(); + virtual ~GRoot(); + + static GRoot* create(cocos2d::Scene* scene, int zOrder = 1000); + static GRoot* getInstance() { return _inst; } + + void showWindow(Window* win); + void hideWindow(Window* win); + void hideWindowImmediately(Window* win); + void bringToFront(Window* win); + void showModalWait(); + void closeModalWait(); + void closeAllExceptModals(); + void closeAllWindows(); + Window* getTopWindow(); + + GObject* getModalWaitingPane(); + GGraph* getModalLayer(); + bool hasModalWindow(); + bool isModalWaiting(); + + InputProcessor* getInputProcessor() const { return _inputProcessor; } + cocos2d::Vec2 getTouchPosition(int touchId); + GObject* getTouchTarget(); + + cocos2d::Vec2 worldToRoot(const cocos2d::Vec2 &pt); + cocos2d::Vec2 rootToWorld(const cocos2d::Vec2 &pt); + + void showPopup(GObject* popup); + void showPopup(GObject* popup, GObject* target, PopupDirection dir); + void togglePopup(GObject* popup); + void togglePopup(GObject* popup, GObject* target, PopupDirection dir); + void hidePopup(); + void hidePopup(GObject* popup); + bool hasAnyPopup(); + cocos2d::Vec2 getPoupPosition(GObject* popup, GObject* target, PopupDirection dir); + + void showTooltips(const std::string& msg); + void showTooltipsWin(GObject* tooltipWin); + void hideTooltips(); + + void playSound(const std::string& url, float volumeScale = 1); + bool isSoundEnabled() const { return _soundEnabled; } + void setSoundEnabled(bool value); + float getSoundVolumeScale() const { return _soundVolumeScale; } + void setSoundVolumeScale(float value); + + static int contentScaleLevel; + +protected: + virtual void handlePositionChanged() override; + virtual void onEnter() override; + virtual void onExit() override; + +private: + bool initWithScene(cocos2d::Scene* scene, int zOrder); + void onWindowSizeChanged(); + void createModalLayer(); + void adjustModalLayer(); + void closePopup(GObject* target); + void checkPopups(); + void onTouchEvent(int eventType); + void updateContentScaleLevel(); + + CALL_LATER_FUNC(GRoot, doShowTooltipsWin); + + cocos2d::EventListener* _windowSizeListener; + InputProcessor* _inputProcessor; + + GGraph* _modalLayer; + GObject* _modalWaitPane; + std::vector _popupStack; + std::vector _justClosedPopups; + GObject* _tooltipWin; + GObject* _defaultTooltipWin; + + static bool _soundEnabled; + static float _soundVolumeScale; + + static GRoot* _inst; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GScrollBar.cpp b/extensions/fairygui/GScrollBar.cpp new file mode 100644 index 0000000000..400f673d72 --- /dev/null +++ b/extensions/fairygui/GScrollBar.cpp @@ -0,0 +1,179 @@ +#include "GScrollBar.h" +#include "PackageItem.h" +#include "ScrollPane.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GScrollBar::GScrollBar() + : _grip(nullptr), + _arrowButton1(nullptr), + _arrowButton2(nullptr), + _bar(nullptr), + _target(nullptr), + _vertical(false), + _scrollPerc(0), + _fixedGripSize(false), + _gripDragging(false) +{ +} + +GScrollBar::~GScrollBar() +{ +} + +void GScrollBar::setScrollPane(ScrollPane* target, bool vertical) +{ + _target = target; + _vertical = vertical; +} + +void GScrollBar::setDisplayPerc(float value) +{ + if (_vertical) + { + if (!_fixedGripSize) + _grip->setHeight(floor(value * _bar->getHeight())); + _grip->setY(round(_bar->getY() + (_bar->getHeight() - _grip->getHeight()) * _scrollPerc)); + } + else + { + if (!_fixedGripSize) + _grip->setWidth(floor(value * _bar->getWidth())); + _grip->setX(round(_bar->getX() + (_bar->getWidth() - _grip->getWidth()) * _scrollPerc)); + } + + _grip->setVisible(value != 0 && value != 1); +} + +void GScrollBar::setScrollPerc(float value) +{ + _scrollPerc = value; + if (_vertical) + _grip->setY(round(_bar->getY() + (_bar->getHeight() - _grip->getHeight()) * _scrollPerc)); + else + _grip->setX(round(_bar->getX() + (_bar->getWidth() - _grip->getWidth()) * _scrollPerc)); +} + +float GScrollBar::getMinSize() +{ + if (_vertical) + return (_arrowButton1 != nullptr ? _arrowButton1->getHeight() : 0) + (_arrowButton2 != nullptr ? _arrowButton2->getHeight() : 0); + else + return (_arrowButton1 != nullptr ? _arrowButton1->getWidth() : 0) + (_arrowButton2 != nullptr ? _arrowButton2->getWidth() : 0); +} + +void GScrollBar::constructExtension(ByteBuffer* buffer) +{ + buffer->seek(0, 6); + + _fixedGripSize = buffer->readBool(); + + _grip = getChild("grip"); + CCASSERT(_grip != nullptr, "FairyGUI: should define grip"); + _bar = getChild("bar"); + CCASSERT(_bar != nullptr, "FairyGUI: should define bar"); + + _arrowButton1 = getChild("arrow1"); + _arrowButton2 = getChild("arrow2"); + + _grip->addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GScrollBar::onGripTouchBegin, this)); + _grip->addEventListener(UIEventType::TouchMove, CC_CALLBACK_1(GScrollBar::onGripTouchMove, this)); + _grip->addEventListener(UIEventType::TouchEnd, CC_CALLBACK_1(GScrollBar::onGripTouchEnd, this)); + + this->addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GScrollBar::onTouchBegin, this)); + + if (_arrowButton1 != nullptr) + _arrowButton1->addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GScrollBar::onArrowButton1Click, this)); + if (_arrowButton2 != nullptr) + _arrowButton2->addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GScrollBar::onArrowButton2Click, this)); +} + +void GScrollBar::onTouchBegin(EventContext* context) +{ + context->stopPropagation(); + + InputEvent* evt = context->getInput(); + Vec2 pt = _grip->globalToLocal(evt->getPosition()); + if (_vertical) + { + if (pt.y < 0) + _target->scrollUp(4, false); + else + _target->scrollDown(4, false); + } + else + { + if (pt.x < 0) + _target->scrollLeft(4, false); + else + _target->scrollRight(4, false); + } +} + +void GScrollBar::onGripTouchBegin(EventContext* context) +{ + if (_bar == nullptr) + return; + + context->stopPropagation(); + context->captureTouch(); + + _gripDragging = true; + _target->updateScrollBarVisible(); + + _dragOffset = globalToLocal(context->getInput()->getPosition()) - _grip->getPosition(); +} + +void GScrollBar::onGripTouchMove(EventContext* context) +{ + Vec2 pt = globalToLocal(context->getInput()->getPosition()); + + if (_vertical) + { + float curY = pt.y - _dragOffset.y; + float diff = _bar->getHeight() - _grip->getHeight(); + if (diff == 0) + _target->setPercY(0); + else + _target->setPercY((curY - _bar->getY()) / diff); + } + else + { + float curX = pt.x - _dragOffset.x; + float diff = _bar->getWidth() - _grip->getWidth(); + if (diff == 0) + _target->setPercX(0); + else + _target->setPercX((curX - _bar->getX()) / diff); + } +} + +void GScrollBar::onGripTouchEnd(EventContext* context) +{ + _gripDragging = false; + _target->updateScrollBarVisible(); +} + +void GScrollBar::onArrowButton1Click(EventContext* context) +{ + context->stopPropagation(); + + if (_vertical) + _target->scrollUp(); + else + _target->scrollLeft(); +} + +void GScrollBar::onArrowButton2Click(EventContext* context) +{ + context->stopPropagation(); + + if (_vertical) + _target->scrollDown(); + else + _target->scrollRight(); +} + +NS_FGUI_END diff --git a/extensions/fairygui/GScrollBar.h b/extensions/fairygui/GScrollBar.h new file mode 100644 index 0000000000..3ef7d406da --- /dev/null +++ b/extensions/fairygui/GScrollBar.h @@ -0,0 +1,51 @@ +#ifndef __GSCROLLBAR_H__ +#define __GSCROLLBAR_H__ + +#include "FairyGUIMacros.h" +#include "GComponent.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GScrollBar : public GComponent +{ +public: + GScrollBar(); + virtual ~GScrollBar(); + + CREATE_FUNC(GScrollBar); + + void setScrollPane(ScrollPane* target, bool vertical); + void setDisplayPerc(float value); + void setScrollPerc(float value); + float getMinSize(); + + bool _gripDragging; + +protected: + virtual void constructExtension(ByteBuffer* buffer) override; + +private: + void onTouchBegin(EventContext* context); + void onGripTouchBegin(EventContext* context); + void onGripTouchMove(EventContext* context); + void onGripTouchEnd(EventContext* context); + void onArrowButton1Click(EventContext* context); + void onArrowButton2Click(EventContext* context); + + GObject* _grip; + GObject* _arrowButton1; + GObject* _arrowButton2; + GObject* _bar; + ScrollPane* _target; + + bool _vertical; + float _scrollPerc; + bool _fixedGripSize; + + cocos2d::Vec2 _dragOffset; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GSlider.cpp b/extensions/fairygui/GSlider.cpp new file mode 100644 index 0000000000..2b09e13d21 --- /dev/null +++ b/extensions/fairygui/GSlider.cpp @@ -0,0 +1,290 @@ +#include "GSlider.h" +#include "PackageItem.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GSlider::GSlider() + : changeOnClick(false), + canDrag(false), + _min(0), + _max(100), + _value(0), + _titleType(ProgressTitleType::PERCENT), + _titleObject(nullptr), + _barObjectH(nullptr), + _barObjectV(nullptr), + _barMaxWidth(0), + _barMaxHeight(0), + _barMaxWidthDelta(0), + _barMaxHeightDelta(0), + _gripObject(nullptr), + _clickPercent(0), + _barStartX(0), + _barStartY(0), + _wholeNumbers(false) +{ +} + +GSlider::~GSlider() +{ +} + +void GSlider::setTitleType(ProgressTitleType value) +{ + if (_titleType != value) + { + _titleType = value; + update(); + } +} + +void GSlider::setMin(double value) +{ + if (_min != value) + { + _min = value; + update(); + } +} + +void GSlider::setMax(double value) +{ + if (_max != value) + { + _max = value; + update(); + } +} + +void GSlider::setValue(double value) +{ + if (_value != value) + { + _value = value; + update(); + } +} + +void GSlider::setWholeNumbers(bool value) +{ + if (_wholeNumbers != value) + { + _wholeNumbers = value; + update(); + } +} + +void GSlider::update() +{ + float percent = MIN(_value / _max, 1); + updateWithPercent(percent, false); +} + +void GSlider::updateWithPercent(float percent, bool manual) +{ + percent = clampf(percent, 0, 1); + if (manual) + { + double newValue = _min + (_max - _min) * percent; + if (newValue < _min) + newValue = _min; + if (newValue > _max) + newValue = _max; + if (_wholeNumbers) + { + newValue = round(newValue); + percent = clampf((newValue - _min) / (_max - _min), 0, 1); + } + + if (newValue != _value) + { + _value = newValue; + if (dispatchEvent(UIEventType::Changed)) + return; + } + } + + if (_titleObject != nullptr) + { + std::ostringstream oss; + switch (_titleType) + { + case ProgressTitleType::PERCENT: + oss << floor(percent * 100) << "%"; + break; + + case ProgressTitleType::VALUE_MAX: + oss << floor(_value) << "/" << floor(_max); + break; + + case ProgressTitleType::VALUE: + oss << _value; + break; + + case ProgressTitleType::MAX: + oss << _max; + break; + } + _titleObject->setText(oss.str()); + } + + float fullWidth = this->getWidth() - _barMaxWidthDelta; + float fullHeight = this->getHeight() - _barMaxHeightDelta; + if (!_reverse) + { + if (_barObjectH != nullptr) + _barObjectH->setWidth(round(fullWidth * percent)); + if (_barObjectV != nullptr) + _barObjectV->setHeight(round(fullHeight * percent)); + } + else + { + if (_barObjectH != nullptr) + { + _barObjectH->setWidth(round(fullWidth * percent)); + _barObjectH->setX(_barStartX + (fullWidth - _barObjectH->getWidth())); + } + if (_barObjectV != nullptr) + { + _barObjectV->setHeight(round(fullHeight * percent)); + _barObjectV->setY(_barStartY + (fullHeight - _barObjectV->getHeight())); + } + } +} + +void GSlider::handleSizeChanged() +{ + GComponent::handleSizeChanged(); + + if (_barObjectH != nullptr) + _barMaxWidth = getWidth() - _barMaxWidthDelta; + if (_barObjectV != nullptr) + _barMaxHeight = getHeight() - _barMaxHeightDelta; + + if (!_underConstruct) + update(); +} + +void GSlider::constructExtension(ByteBuffer* buffer) +{ + _titleType = (ProgressTitleType)buffer->readByte(); + _reverse = buffer->readBool(); + if (buffer->version >= 2) + { + _wholeNumbers = buffer->readBool(); + changeOnClick = buffer->readBool(); + } + + _titleObject = getChild("title"); + _barObjectH = getChild("bar"); + _barObjectV = getChild("bar_v"); + _gripObject = getChild("grip"); + + if (_barObjectH != nullptr) + { + _barMaxWidth = _barObjectH->getWidth(); + _barMaxWidthDelta = getWidth() - _barMaxWidth; + _barStartX = _barObjectH->getX(); + } + if (_barObjectV != nullptr) + { + _barMaxHeight = _barObjectV->getHeight(); + _barMaxHeightDelta = getHeight() - _barMaxHeight; + _barStartY = _barObjectV->getY(); + } + + if (_gripObject != nullptr) + { + _gripObject->addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GSlider::onGripTouchBegin, this)); + _gripObject->addEventListener(UIEventType::TouchMove, CC_CALLBACK_1(GSlider::onGripTouchMove, this)); + } + + addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GSlider::onTouchBegin, this)); +} + +void GSlider::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GComponent::setup_afterAdd(buffer, beginPos); + + if (!buffer->seek(beginPos, 6)) + { + update(); + return; + } + + if ((ObjectType)buffer->readByte() != _packageItem->objectType) + { + update(); + return; + } + + _value = buffer->readInt(); + _max = buffer->readInt(); + if (buffer->version >= 2) + _min = buffer->readInt(); + + update(); +} + +void GSlider::onTouchBegin(EventContext* context) +{ + if (!changeOnClick) + return; + + InputEvent* evt = context->getInput(); + if (evt->getButton() != EventMouse::MouseButton::BUTTON_LEFT) + return; + + Vec2 pt = _gripObject->globalToLocal(evt->getPosition()); + float percent = clampf((_value - _min) / (_max - _min), 0, 1); + float delta = 0; + if (_barObjectH != nullptr) + delta = pt.x / _barMaxWidth; + if (_barObjectV != nullptr) + delta = pt.y / _barMaxHeight; + if (_reverse) + percent -= delta; + else + percent += delta; + updateWithPercent(percent, true); +} + +void GSlider::onGripTouchBegin(EventContext* context) +{ + if (context->getInput()->getButton() != EventMouse::MouseButton::BUTTON_LEFT) + return; + + canDrag = true; + context->stopPropagation(); + context->captureTouch(); + + _clickPos = globalToLocal(context->getInput()->getPosition()); + _clickPercent = clampf((_value - _min) / (_max - _min), 0, 1); +} + +void GSlider::onGripTouchMove(EventContext* context) +{ + if (!canDrag) + return; + + Vec2 pt = globalToLocal(context->getInput()->getPosition()); + + float deltaX = pt.x - _clickPos.x; + float deltaY = pt.y - _clickPos.y; + if (_reverse) + { + deltaX = -deltaX; + deltaY = -deltaY; + } + + float percent; + if (_barObjectH != nullptr) + percent = _clickPercent + deltaX / _barMaxWidth; + else + percent = _clickPercent + deltaY / _barMaxHeight; + updateWithPercent(percent, true); +} + +NS_FGUI_END diff --git a/extensions/fairygui/GSlider.h b/extensions/fairygui/GSlider.h new file mode 100644 index 0000000000..f4dfe573b8 --- /dev/null +++ b/extensions/fairygui/GSlider.h @@ -0,0 +1,72 @@ +#ifndef __GSLIDER_H__ +#define __GSLIDER_H__ + +#include "FairyGUIMacros.h" +#include "GComponent.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GSlider : public GComponent +{ +public: + GSlider(); + virtual ~GSlider(); + + CREATE_FUNC(GSlider); + + ProgressTitleType getTitleType() const { return _titleType; } + void setTitleType(ProgressTitleType value); + + double getMin() const { return _min; } + void setMin(double value); + + double getMax() const { return _max; } + void setMax(double value); + + double getValue() const { return _value; } + void setValue(double value); + + bool getWholeNumbers() const { return _wholeNumbers; } + void setWholeNumbers(bool value); + + bool changeOnClick; + bool canDrag; + +protected: + virtual void handleSizeChanged() override; + virtual void constructExtension(ByteBuffer* buffer) override; + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + + void update(); + void updateWithPercent(float percent, bool manual); + +private: + void onTouchBegin(EventContext* context); + void onGripTouchBegin(EventContext* context); + void onGripTouchMove(EventContext* context); + + double _min; + double _max; + double _value; + ProgressTitleType _titleType; + bool _reverse; + bool _wholeNumbers; + + GObject* _titleObject; + GObject* _barObjectH; + GObject* _barObjectV; + float _barMaxWidth; + float _barMaxHeight; + float _barMaxWidthDelta; + float _barMaxHeightDelta; + GObject* _gripObject; + cocos2d::Vec2 _clickPos; + float _clickPercent; + float _barStartX; + float _barStartY; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GTextField.cpp b/extensions/fairygui/GTextField.cpp new file mode 100644 index 0000000000..a433b940eb --- /dev/null +++ b/extensions/fairygui/GTextField.cpp @@ -0,0 +1,377 @@ +#include "GTextField.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GTextField::GTextField() + : _templateVars(nullptr), + _ubbEnabled(false), + _autoSize(AutoSizeType::BOTH) +{ +} + +GTextField::~GTextField() +{ + CC_SAFE_DELETE(_templateVars); +} + +void GTextField::setText(const std::string& value) +{ + _text = value; + setTextFieldText(); + updateGear(6); + updateSize(); +} + +void GTextField::setUBBEnabled(bool value) +{ + if (_ubbEnabled != value) + { + _ubbEnabled = value; + setTextFieldText(); + updateSize(); + } +} + +void GTextField::setColor(const cocos2d::Color3B& value) +{ + TextFormat* tf = getTextFormat(); + if (tf->color != value) + { + tf->color = value; + applyTextFormat(); + } +} + +void GTextField::setFontSize(float value) +{ + TextFormat* tf = getTextFormat(); + if (tf->fontSize != value) + { + tf->fontSize = value; + applyTextFormat(); + } +} + +void GTextField::setOutlineColor(const cocos2d::Color3B& value) +{ + TextFormat* tf = getTextFormat(); + if (tf->outlineColor != value) + { + tf->outlineColor = value; + applyTextFormat(); + } +} + +void GTextField::setTemplateVars(cocos2d::ValueMap* value) +{ + if (_templateVars == nullptr && value == nullptr) + return; + + if (value == nullptr) + CC_SAFE_DELETE(_templateVars); + else + { + if (_templateVars == nullptr) + _templateVars = new cocos2d::ValueMap(); + *_templateVars = *value; + } + + flushVars(); +} + +GTextField* GTextField::setVar(const std::string& name, const cocos2d::Value& value) +{ + if (_templateVars == nullptr) + _templateVars = new cocos2d::ValueMap(); + + (*_templateVars)[name] = value; + + return this; +} + +void GTextField::flushVars() +{ + setTextFieldText(); + updateSize(); +} + +void GTextField::updateSize() +{ +} + +cocos2d::Value GTextField::getProp(ObjectPropID propId) +{ + switch (propId) + { + case ObjectPropID::Color: + return Value(ToolSet::colorToInt(getColor())); + case ObjectPropID::OutlineColor: + return Value(ToolSet::colorToInt(getOutlineColor())); + case ObjectPropID::FontSize: + return Value(getFontSize()); + default: + return GObject::getProp(propId); + } +} + +void GTextField::setProp(ObjectPropID propId, const cocos2d::Value& value) +{ + switch (propId) + { + case ObjectPropID::Color: + setColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + case ObjectPropID::OutlineColor: + setOutlineColor(ToolSet::intToColor(value.asUnsignedInt())); + break; + case ObjectPropID::FontSize: + setFontSize(value.asInt()); + break; + default: + GObject::setProp(propId, value); + break; + } +} + +void GTextField::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 5); + + TextFormat* tf = getTextFormat(); + + tf->face = buffer->readS(); + tf->fontSize = buffer->readShort(); + tf->color = (Color3B)buffer->readColor(); + tf->align = (TextHAlignment)buffer->readByte(); + tf->verticalAlign = (TextVAlignment)buffer->readByte(); + tf->lineSpacing = buffer->readShort(); + tf->letterSpacing = buffer->readShort(); + _ubbEnabled = buffer->readBool(); + setAutoSize((AutoSizeType)buffer->readByte()); + tf->underline = buffer->readBool(); + tf->italics = buffer->readBool(); + tf->bold = buffer->readBool(); + if (buffer->readBool()) + setSingleLine(true); + if (buffer->readBool()) + { + tf->outlineColor = (Color3B)buffer->readColor(); + tf->outlineSize = buffer->readFloat(); + tf->enableEffect(TextFormat::OUTLINE); + } + + if (buffer->readBool()) + { + tf->shadowColor = (Color3B)buffer->readColor(); + float f1 = buffer->readFloat(); + float f2 = buffer->readFloat(); + tf->shadowOffset = Vec2(f1, -f2); + tf->enableEffect(TextFormat::SHADOW); + } + + if (buffer->readBool()) + _templateVars = new cocos2d::ValueMap(); +} + +void GTextField::setup_afterAdd(ByteBuffer* buffer, int beginPos) +{ + GObject::setup_afterAdd(buffer, beginPos); + + applyTextFormat(); + + buffer->seek(beginPos, 6); + + const std::string& str = buffer->readS(); + if (!str.empty()) + setText(str); +} + +std::string GTextField::parseTemplate(const char* text) +{ + const char* pString = text; + + ssize_t pos; + ssize_t pos2; + std::string tag, attr; + std::string repl; + std::string out; + + while (*pString != '\0') + { + const char* p = strchr(pString, '{'); + if (!p) + { + out.append(pString); + break; + } + + pos = p - pString; + if (pos > 0 && *(p - 1) == '\\') + { + out.append(pString, pos - 1); + out.append("{"); + pString += pos + 1; + continue; + } + + out.append(pString, pos); + pString += pos; + + p = strchr(pString, '}'); + if (!p) + { + out.append(pString); + break; + } + + pos = p - pString; + if (pos == 1) + { + out.append(pString, 0, 2); + pString += 2; + continue; + } + + tag.assign(pString + 1, pos - 1); + + attr.clear(); + repl.clear(); + pos2 = tag.find('='); + if (pos2 != -1) + { + auto it = _templateVars->find(tag.substr(0, pos2)); + if (it != _templateVars->end()) + out.append(it->second.asString()); + else + out.append(tag.substr(pos2 + 1)); + } + else + { + auto it = _templateVars->find(tag); + if (it != _templateVars->end()) + out.append(it->second.asString()); + } + pString += pos + 1; + } + return out; +} + +//--------------------------- + +GBasicTextField::GBasicTextField() : _label(nullptr), + _updatingSize(false) +{ + _touchDisabled = true; +} + +GBasicTextField::~GBasicTextField() +{ +} + +void GBasicTextField::handleInit() +{ + _label = FUILabel::create(); + _label->retain(); + + _displayObject = _label; +} + +void GBasicTextField::applyTextFormat() +{ + _label->applyTextFormat(); + updateGear(4); + if (!_underConstruct) + updateSize(); +} + +void GBasicTextField::setAutoSize(AutoSizeType value) +{ + _autoSize = value; + switch (value) + { + case AutoSizeType::NONE: + _label->setOverflow(Label::Overflow::CLAMP); + break; + case AutoSizeType::BOTH: + _label->setOverflow(Label::Overflow::NONE); + break; + case AutoSizeType::HEIGHT: + _label->setOverflow(Label::Overflow::RESIZE_HEIGHT); + break; + case AutoSizeType::SHRINK: + _label->setOverflow(Label::Overflow::SHRINK); + break; + } + + if (_autoSize == AutoSizeType::BOTH) + _label->setDimensions(0, 0); + else if (_autoSize == AutoSizeType::HEIGHT) + _label->setDimensions(_size.width, 0); + else + _label->setDimensions(_size.width, _size.height); + + if (!_underConstruct) + updateSize(); +} + +void GBasicTextField::setSingleLine(bool value) +{ + _label->enableWrap(!value); + if (!_underConstruct) + updateSize(); +} + +void GBasicTextField::setTextFieldText() +{ + if (_templateVars != nullptr) + _label->setText(parseTemplate(_text.c_str())); + else + _label->setText(_text); +} + +void GBasicTextField::updateSize() +{ + if (_updatingSize) + return; + + _updatingSize = true; + + Size sz = _label->getContentSize(); + if (_autoSize == AutoSizeType::BOTH) + setSize(sz.width, sz.height); + else if (_autoSize == AutoSizeType::HEIGHT) + setHeight(sz.height); + + _updatingSize = false; +} + +void GBasicTextField::handleSizeChanged() +{ + if (_updatingSize) + return; + + if (_autoSize != AutoSizeType::BOTH) + { + _label->setDimensions(_size.width, _size.height); + + if (_autoSize == AutoSizeType::HEIGHT) + { + if (!_text.empty()) + setSizeDirectly(_size.width, _label->getContentSize().height); + } + } +} + +void GBasicTextField::handleGrayedChanged() +{ + GObject::handleGrayedChanged(); + + _label->setGrayed(_finalGrayed); +} + +NS_FGUI_END diff --git a/extensions/fairygui/GTextField.h b/extensions/fairygui/GTextField.h new file mode 100644 index 0000000000..1d1f42207a --- /dev/null +++ b/extensions/fairygui/GTextField.h @@ -0,0 +1,98 @@ +#ifndef __GTEXTFIELD_H__ +#define __GTEXTFIELD_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GObject.h" +#include "display/FUILabel.h" + +NS_FGUI_BEGIN + +class GTextField : public GObject +{ +public: + GTextField(); + virtual ~GTextField(); + + virtual const std::string& getText() const override { return _text; } + void setText(const std::string& value) override; + + bool isUBBEnabled() const { return _ubbEnabled; } + virtual void setUBBEnabled(bool value); + + AutoSizeType getAutoSize() const { return _autoSize; } + virtual void setAutoSize(AutoSizeType value) {}; + + virtual bool isSingleLine() const { return false; } + virtual void setSingleLine(bool value) {}; + + virtual TextFormat* getTextFormat() const = 0; + virtual void applyTextFormat() = 0; + + virtual const cocos2d::Size& getTextSize() { return _displayObject->getContentSize(); } + + cocos2d::Color3B getColor() const { return getTextFormat()->color; } + void setColor(const cocos2d::Color3B& value); + + float getFontSize() const { return getTextFormat()->fontSize; } + void setFontSize(float value); + + cocos2d::Color3B getOutlineColor() const { return getTextFormat()->outlineColor; } + void setOutlineColor(const cocos2d::Color3B& value); + + cocos2d::ValueMap* getTemplateVars() { return _templateVars; } + void setTemplateVars(cocos2d::ValueMap* value); + + GTextField* setVar(const std::string& name, const cocos2d::Value& value); + void flushVars(); + + virtual cocos2d::Value getProp(ObjectPropID propId) override; + virtual void setProp(ObjectPropID propId, const cocos2d::Value& value) override; + +protected: + virtual void setTextFieldText() = 0; + virtual void updateSize(); + + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + virtual void setup_afterAdd(ByteBuffer* buffer, int beginPos) override; + + std::string parseTemplate(const char* text); + + std::string _text; + bool _ubbEnabled; + AutoSizeType _autoSize; + cocos2d::ValueMap* _templateVars; +}; + +class GBasicTextField : public GTextField +{ +public: + GBasicTextField(); + virtual ~GBasicTextField(); + + CREATE_FUNC(GBasicTextField); + + virtual void setAutoSize(AutoSizeType value) override; + + virtual bool isSingleLine() const override { return _label->isWrapEnabled(); } + virtual void setSingleLine(bool value) override; + + virtual TextFormat* getTextFormat() const override { return _label->getTextFormat(); } + virtual void applyTextFormat() override; + +protected: + virtual void handleInit() override; + virtual void handleSizeChanged() override; + virtual void handleGrayedChanged() override; + + virtual void setTextFieldText() override; + virtual void updateSize() override; + +private: + FUILabel* _label; + bool _updatingSize; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GTextInput.cpp b/extensions/fairygui/GTextInput.cpp new file mode 100644 index 0000000000..3a9117675b --- /dev/null +++ b/extensions/fairygui/GTextInput.cpp @@ -0,0 +1,131 @@ +#include "GTextInput.h" +#include "UIPackage.h" +#include "GRoot.h" +#include "ui/UIEditBox/UIEditBox.h" +#include "utils/ByteBuffer.h" +#include "utils/UBBParser.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GTextInput::GTextInput() +{ +} + +GTextInput::~GTextInput() +{ +} + +void GTextInput::handleInit() +{ + _input = FUIInput::create(); + _input->retain(); + _input->setDelegate(this); + + _displayObject = _input; + + this->addEventListener(UIEventType::TouchEnd, [this](EventContext*) { + _input->openKeyboard(); + }); +} + +bool GTextInput::isSingleLine() const +{ + return _input->isSingleLine(); +} + +void GTextInput::setSingleLine(bool value) +{ + _input->setSingleLine(value); +} + +void GTextInput::applyTextFormat() +{ + _input->applyTextFormat(); +} + +void GTextInput::setPrompt(const std::string & value) +{ + if (value.empty()) + _input->setPlaceHolder(value.c_str()); + else + { + UBBParser* parser = UBBParser::getInstance(); + _input->setPlaceHolder(parser->parse(value.c_str(), true).c_str()); + if (!parser->lastColor.empty()) + _input->setPlaceholderFontColor(ToolSet::hexToColor(parser->lastColor.c_str())); + if (!parser->lastFontSize.empty()) + _input->setPlaceholderFontSize(Value(parser->lastFontSize).asInt()); + } +} + +void GTextInput::setPassword(bool value) +{ + _input->setPassword(value); +} + +void GTextInput::setKeyboardType(int value) +{ + _input->setKeyboardType(value); +} + +void GTextInput::setMaxLength(int value) +{ + _input->setMaxLength(value); +} + +void GTextInput::setRestrict(const std::string & value) +{ +} + +void GTextInput::handleSizeChanged() +{ + _input->setContentSize(_size); +} + +void GTextInput::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GTextField::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 4); + + const std::string* str; + if ((str = buffer->readSP())) + setPrompt(*str); + + if ((str = buffer->readSP())) + setRestrict(*str); + + int iv = buffer->readInt(); + if (iv != 0) + setMaxLength(iv); + iv = buffer->readInt(); + if (iv != 0) + setKeyboardType(iv); + if (buffer->readBool()) + setPassword(true); +} + +void GTextInput::setTextFieldText() +{ + if (_templateVars != nullptr) + _input->setText(parseTemplate(_text.c_str())); + else + _input->setText(_text); +} + +void GTextInput::editBoxReturn(cocos2d::ui::EditBox * editBox) +{ + //found that this will trigger even when focus is lost + //if (isSingleLine()) + // dispatchEvent(UIEventType::Submit); +} + +void GTextInput::editBoxTextChanged(cocos2d::ui::EditBox* editBox, const std::string& text) +{ + _text.clear(); + _text.append(_input->getText()); +} + +NS_FGUI_END diff --git a/extensions/fairygui/GTextInput.h b/extensions/fairygui/GTextInput.h new file mode 100644 index 0000000000..8ce76371ce --- /dev/null +++ b/extensions/fairygui/GTextInput.h @@ -0,0 +1,47 @@ +#ifndef __GTEXTINPUT_H__ +#define __GTEXTINPUT_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GTextField.h" +#include "display/FUIInput.h" + +NS_FGUI_BEGIN + +class GTextInput : public GTextField, cocos2d::ui::EditBoxDelegate +{ +public: + GTextInput(); + virtual ~GTextInput(); + + CREATE_FUNC(GTextInput); + + virtual bool isSingleLine() const override; + virtual void setSingleLine(bool value) override; + + virtual TextFormat* getTextFormat() const override { return _input->getTextFormat(); } + virtual void applyTextFormat() override; + + void setPrompt(const std::string& value); + void setPassword(bool value); + void setKeyboardType(int value); + void setMaxLength(int value); + void setRestrict(const std::string& value); + +protected: + virtual void handleInit() override; + virtual void handleSizeChanged() override; + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + + virtual void setTextFieldText() override; + + virtual void editBoxReturn(cocos2d::ui::EditBox* editBox) override; + virtual void editBoxTextChanged(cocos2d::ui::EditBox* editBox, const std::string& text) override; + +private: + FUIInput* _input; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GTree.cpp b/extensions/fairygui/GTree.cpp new file mode 100644 index 0000000000..d6eae8d916 --- /dev/null +++ b/extensions/fairygui/GTree.cpp @@ -0,0 +1,412 @@ +#include "GTree.h" +#include "GButton.h" +#include "GList.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GTree::GTree() + : _rootNode(nullptr), + _indent(30), + _clickToExpand(0), + _expandedStatusInEvt(false) +{ +} + +GTree::~GTree() +{ + CC_SAFE_RELEASE(_rootNode); +} + +void GTree::handleInit() +{ + GList::handleInit(); + + _rootNode = GTreeNode::create(true); + _rootNode->setTree(this); + _rootNode->setExpaned(true); + _rootNode->retain(); +} + +GTreeNode* GTree::getSelectedNode() const +{ + int i = getSelectedIndex(); + if (i != -1) + return getChildAt(i)->_treeNode; + else + return nullptr; +} + +void GTree::getSelectedNodes(std::vector& result) const +{ + std::vector ids; + getSelection(ids); + for (auto& it : ids) + { + GTreeNode* node = getChildAt(it)->_treeNode; + result.push_back(node); + } +} + +void GTree::selectNode(GTreeNode* node, bool scrollItToView) +{ + GTreeNode* parentNode = node->_parent; + while (parentNode != nullptr && parentNode != _rootNode) + { + parentNode->setExpaned(true); + parentNode = parentNode->_parent; + } + if (node->_cell != nullptr) + addSelection(getChildIndex(node->_cell), scrollItToView); +} + +void GTree::unselectNode(GTreeNode* node) +{ + if (node->_cell != nullptr) + removeSelection(getChildIndex(node->_cell)); +} + +void GTree::expandAll(GTreeNode* folderNode) +{ + folderNode->setExpaned(true); + for (auto& it : folderNode->_children) + { + if (it->isFolder()) + expandAll(it); + } +} + +void GTree::collapseAll(GTreeNode* folderNode) +{ + if (folderNode != _rootNode) + folderNode->setExpaned(false); + for (auto& it : folderNode->_children) + { + if (it->isFolder()) + collapseAll(it); + } +} + +void GTree::createCell(GTreeNode* node) +{ + const std::string& url = node->_resURL.empty() ? getDefaultItem() : node->_resURL; + GComponent* child = getItemPool()->getObject(url)->as(); + CCASSERT(child, "Unable to create tree cell"); + child->_treeNode = node; + node->setCell(child); + + GObject* indentObj = node->_cell->getChild("indent"); + if (indentObj != nullptr) + indentObj->setWidth((node->_level - 1) * _indent); + + GController* cc; + + cc = child->getController("expanded"); + if (cc != nullptr) + { + cc->addEventListener(UIEventType::Changed, CC_CALLBACK_1(GTree::onExpandedStateChanged, this)); + cc->setSelectedIndex(node->isExpanded() ? 1 : 0); + } + + cc = child->getController("leaf"); + if (cc != nullptr) + cc->setSelectedIndex(node->isFolder() ? 0 : 1); + + if (node->isFolder()) + child->addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(GTree::onCellTouchBegin, this)); + + if (treeNodeRender != nullptr) + treeNodeRender(node, child); +} + +void GTree::afterInserted(GTreeNode* node) +{ + if (node->_cell == nullptr) + createCell(node); + + int index = getInsertIndexForNode(node); + addChildAt(node->_cell, index); + if (treeNodeRender != nullptr) + treeNodeRender(node, node->_cell); + + if (node->isFolder() && node->isExpanded()) + checkChildren(node, index); +} + +int GTree::getInsertIndexForNode(GTreeNode* node) +{ + GTreeNode* prevNode = node->getPrevSibling(); + if (prevNode == nullptr) + prevNode = node->getParent(); + int insertIndex; + if (prevNode->_cell != nullptr) + insertIndex = getChildIndex(prevNode->_cell) + 1; + else + insertIndex = 0; + int myLevel = node->_level; + int cnt = numChildren(); + for (int i = insertIndex; i < cnt; i++) + { + GTreeNode* testNode = getChildAt(i)->_treeNode; + if (testNode->_level <= myLevel) + break; + + insertIndex++; + } + + return insertIndex; +} + +void GTree::afterRemoved(GTreeNode* node) +{ + removeNode(node); +} + +void GTree::afterExpanded(GTreeNode* node) +{ + if (node == _rootNode) + { + checkChildren(_rootNode, 0); + return; + } + + if (treeNodeWillExpand != nullptr) + treeNodeWillExpand(node, true); + + if (node->_cell == nullptr) + return; + + if (treeNodeRender != nullptr) + treeNodeRender(node, node->_cell); + + GController* cc = node->_cell->getController("expanded"); + if (cc != nullptr) + cc->setSelectedIndex(1); + + if (node->_cell->getParent() != nullptr) + checkChildren(node, getChildIndex(node->_cell)); +} + +void GTree::afterCollapsed(GTreeNode* node) +{ + if (node == _rootNode) + { + checkChildren(_rootNode, 0); + return; + } + + if (treeNodeWillExpand != nullptr) + treeNodeWillExpand(node, false); + + if (node->_cell == nullptr) + return; + + if (treeNodeRender != nullptr) + treeNodeRender(node, node->_cell); + + GController* cc = node->_cell->getController("expanded"); + if (cc != nullptr) + cc->setSelectedIndex(0); + + if (node->_cell->getParent() != nullptr) + hideFolderNode(node); +} + +void GTree::afterMoved(GTreeNode* node) +{ + int startIndex = getChildIndex(node->_cell); + int endIndex; + if (node->isFolder()) + endIndex = getFolderEndIndex(startIndex, node->_level); + else + endIndex = startIndex + 1; + int insertIndex = getInsertIndexForNode(node); + int cnt = endIndex - startIndex; + + if (insertIndex < startIndex) + { + for (int i = 0; i < cnt; i++) + { + GObject* obj = getChildAt(startIndex + i); + setChildIndex(obj, insertIndex + i); + } + } + else + { + for (int i = 0; i < cnt; i++) + { + GObject* obj = getChildAt(startIndex); + setChildIndex(obj, insertIndex); + } + } +} + +int GTree::getFolderEndIndex(int startIndex, int level) +{ + int cnt = numChildren(); + for (int i = startIndex + 1; i < cnt; i++) + { + GTreeNode* node = getChildAt(i)->_treeNode; + if (node->_level <= level) + return i; + } + + return cnt; +} + +int GTree::checkChildren(GTreeNode* folderNode, int index) +{ + int cnt = folderNode->numChildren(); + for (int i = 0; i < cnt; i++) + { + index++; + GTreeNode* node = folderNode->getChildAt(i); + if (node->_cell == nullptr) + createCell(node); + + if (node->_cell->getParent() == nullptr) + addChildAt(node->_cell, index); + + if (node->isFolder() && node->isExpanded()) + index = checkChildren(node, index); + } + + return index; +} + +void GTree::hideFolderNode(GTreeNode* folderNode) +{ + int cnt = folderNode->numChildren(); + for (int i = 0; i < cnt; i++) + { + GTreeNode* node = folderNode->getChildAt(i); + if (node->_cell != nullptr && node->_cell->getParent() != nullptr) + removeChild(node->_cell); + + if (node->isFolder() && node->isExpanded()) + hideFolderNode(node); + } +} + +void GTree::removeNode(GTreeNode* node) +{ + if (node->_cell != nullptr) + { + if (node->_cell->getParent() != nullptr) + removeChild(node->_cell); + getItemPool()->returnObject(node->_cell); + node->_cell->_treeNode = nullptr; + node->_cell = nullptr; + } + + if (node->isFolder()) + { + int cnt = node->numChildren(); + for (int i = 0; i < cnt; i++) + { + GTreeNode* node2 = node->getChildAt(i); + removeNode(node2); + } + } +} + +void GTree::onCellTouchBegin(EventContext* context) +{ + GTreeNode* node = ((GObject*)context->getSender())->_treeNode; + _expandedStatusInEvt = node->isExpanded(); +} + +void GTree::onExpandedStateChanged(EventContext* context) +{ + GController* cc = (GController*)context->getSender(); + GTreeNode* node = cc->getParent()->_treeNode; + node->setExpaned(cc->getSelectedIndex() == 1); +} + +void GTree::dispatchItemEvent(GObject* item, EventContext* context) +{ + if (_clickToExpand != 0) + { + GTreeNode* node = item->_treeNode; + if (node != nullptr && _expandedStatusInEvt == node->isExpanded()) + { + if (_clickToExpand == 2) + { + if (context->getInput()->isDoubleClick()) + node->setExpaned(!node->isExpanded()); + } + else + node->setExpaned(!node->isExpanded()); + } + } + + GList::dispatchItemEvent(item, context); +} + +void GTree::setup_beforeAdd(ByteBuffer* buffer, int beginPos) +{ + GList::setup_beforeAdd(buffer, beginPos); + + buffer->seek(beginPos, 9); + + _indent = buffer->readInt(); + _clickToExpand = buffer->readByte(); +} + +void GTree::readItems(ByteBuffer* buffer) +{ + int nextPos; + std::string str; + bool isFolder; + GTreeNode* lastNode = nullptr; + int level; + int prevLevel = 0; + + int cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + str = buffer->readS(); + if (!str.empty()) + { + str = getDefaultItem(); + if (str.empty()) + { + buffer->setPos(nextPos); + continue; + } + } + + isFolder = buffer->readBool(); + level = buffer->readByte(); + + GTreeNode* node = GTreeNode::create(isFolder, str); + node->setExpaned(true); + if (i == 0) + _rootNode->addChild(node); + else + { + if (level > prevLevel) + lastNode->addChild(node); + else if (level < prevLevel) + { + for (int j = level; j <= prevLevel; j++) + lastNode = lastNode->getParent(); + lastNode->addChild(node); + } + else + lastNode->getParent()->addChild(node); + } + lastNode = node; + prevLevel = level; + + setupItem(buffer, node->_cell); + + buffer->setPos(nextPos); + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/GTree.h b/extensions/fairygui/GTree.h new file mode 100644 index 0000000000..222db93640 --- /dev/null +++ b/extensions/fairygui/GTree.h @@ -0,0 +1,73 @@ +#ifndef __GTREE_H__ +#define __GTREE_H__ + +#include "FairyGUIMacros.h" +#include "GList.h" +#include "GTreeNode.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GList; +class GComponent; + +class GTree : public GList +{ +public: + typedef std::function TreeNodeRenderFunction; + typedef std::function TreeNodeWillExpandFunction; + + GTree(); + virtual ~GTree(); + + CREATE_FUNC(GTree); + + int getIndent() const { return _indent; } + void setIndent(int value) { _indent = value; } + int getClickToExpand() const { return _clickToExpand; } + void setClickToExpand(int value) { _clickToExpand = value; } + + GTreeNode* getRootNode() const { return _rootNode; } + GTreeNode* getSelectedNode() const; + void getSelectedNodes(std::vector& result) const; + void selectNode(GTreeNode* node, bool scrollItToView = false); + void unselectNode(GTreeNode* node); + void expandAll(GTreeNode* folderNode); + void collapseAll(GTreeNode* folderNode); + + TreeNodeRenderFunction treeNodeRender; + TreeNodeWillExpandFunction treeNodeWillExpand; + +protected: + virtual void handleInit() override; + virtual void setup_beforeAdd(ByteBuffer* buffer, int beginPos) override; + virtual void readItems(ByteBuffer* buffer) override; + virtual void dispatchItemEvent(GObject* item, EventContext* context) override; + +private: + void createCell(GTreeNode* node); + void afterInserted(GTreeNode* node); + int getInsertIndexForNode(GTreeNode* node); + void afterRemoved(GTreeNode* node); + void afterExpanded(GTreeNode* node); + void afterCollapsed(GTreeNode* node); + void afterMoved(GTreeNode* node); + int checkChildren(GTreeNode* folderNode, int index); + void hideFolderNode(GTreeNode* folderNode); + void removeNode(GTreeNode* node); + int getFolderEndIndex(int startIndex, int level); + + void onCellTouchBegin(EventContext* context); + void onExpandedStateChanged(EventContext* context); + + int _indent; + GTreeNode* _rootNode; + int _clickToExpand; + bool _expandedStatusInEvt; + + friend class GTreeNode; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/GTreeNode.cpp b/extensions/fairygui/GTreeNode.cpp new file mode 100644 index 0000000000..c20cbbef6b --- /dev/null +++ b/extensions/fairygui/GTreeNode.cpp @@ -0,0 +1,310 @@ +#include "GTreeNode.h" +#include "GComponent.h" +#include "GTree.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GTreeNode* GTreeNode::create(bool isFolder, const std::string& resURL) +{ + GTreeNode* pRet = new (std::nothrow) GTreeNode(); + + if (pRet != nullptr && pRet->init(isFolder, resURL)) + { + pRet->autorelease(); + } + else + { + CC_SAFE_DELETE(pRet); + } + + return pRet; +} + +GTreeNode::GTreeNode() + : _tree(nullptr), + _parent(nullptr), + _cell(nullptr), + _level(0), + _expanded(false), + _isFolder(false) +{ +} + +GTreeNode::~GTreeNode() +{ + for (auto& it : _children) + it->_parent = nullptr; + + _children.clear(); + + if (_parent) + _parent->removeChild(this); + + CC_SAFE_RELEASE(_cell); +} + +bool GTreeNode::init(bool isFolder, const std::string& resURL) +{ + _isFolder = isFolder; + _resURL = resURL; + + return true; +} + +void GTreeNode::setExpaned(bool value) +{ + if (!_isFolder) + return; + + if (_expanded != value) + { + _expanded = value; + if (_tree != nullptr) + { + if (_expanded) + _tree->afterExpanded(this); + else + _tree->afterCollapsed(this); + } + } +} + +const std::string& GTreeNode::getText() const +{ + if (_cell != nullptr) + return _cell->getText(); + else + return STD_STRING_EMPTY; +} + +void GTreeNode::setText(const std::string& value) +{ + if (_cell != nullptr) + return _cell->setText(value); +} + +const std::string& GTreeNode::getIcon() const +{ + if (_cell != nullptr) + return _cell->getIcon(); + else + return STD_STRING_EMPTY; +} + +void GTreeNode::setIcon(const std::string& value) +{ + if (_cell != nullptr) + return _cell->setIcon(value); +} + +GTreeNode* GTreeNode::addChild(GTreeNode* child) +{ + addChildAt(child, (int)_children.size()); + return child; +} + +GTreeNode* GTreeNode::addChildAt(GTreeNode* child, int index) +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + if (child->_parent == this) + { + setChildIndex(child, index); + } + else + { + child->retain(); + if (child->_parent != nullptr) + child->_parent->removeChild(child); + child->_parent = this; + + int cnt = (int)_children.size(); + if (index == cnt) + _children.pushBack(child); + else + _children.insert(index, child); + child->release(); + + child->_level = _level + 1; + child->setTree(_tree); + if ((_tree != nullptr && this == _tree->getRootNode()) || (_cell != nullptr && _cell->getParent() != nullptr && _expanded)) + _tree->afterInserted(child); + } + return child; +} + +void GTreeNode::removeChild(GTreeNode* child) +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + int childIndex = (int)_children.getIndex(child); + if (childIndex != -1) + removeChildAt(childIndex); +} + +void GTreeNode::removeChildAt(int index) +{ + CCASSERT(index >= 0 && index < _children.size(), "Invalid child index"); + + GTreeNode* child = _children.at(index); + child->_parent = nullptr; + + if (_tree != nullptr) + { + child->setTree(nullptr); + _tree->afterRemoved(child); + } + + _children.erase(index); +} + +void GTreeNode::removeChildren(int beginIndex, int endIndex) +{ + if (endIndex < 0 || endIndex >= _children.size()) + endIndex = (int)_children.size() - 1; + + for (int i = beginIndex; i <= endIndex; ++i) + removeChildAt(beginIndex); +} + +GTreeNode* GTreeNode::getChildAt(int index) const +{ + CCASSERT(index >= 0 && index < _children.size(), "Invalid child index"); + + return _children.at(index); +} + +GTreeNode* GTreeNode::getPrevSibling() const +{ + if (_parent == nullptr) + return nullptr; + + ssize_t i = _parent->_children.getIndex((GTreeNode*)this); + if (i <= 0) + return nullptr; + + return _parent->_children.at(i - 1); +} + +GTreeNode* GTreeNode::getNextSibling() const +{ + if (_parent == nullptr) + return nullptr; + + ssize_t i = _parent->_children.getIndex((GTreeNode*)this); + if (i < 0 || i >= _parent->_children.size() - 1) + return nullptr; + + return _parent->_children.at(i + 1); +} + +int GTreeNode::getChildIndex(const GTreeNode* child) const +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + return (int)_children.getIndex((GTreeNode*)child); +} + +void GTreeNode::setChildIndex(GTreeNode* child, int index) +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + int oldIndex = (int)_children.getIndex(child); + CCASSERT(oldIndex != -1, "Not a child of this container"); + + moveChild(child, oldIndex, index); +} + +int GTreeNode::setChildIndexBefore(GTreeNode* child, int index) +{ + CCASSERT(child != nullptr, "Argument must be non-nil"); + + int oldIndex = (int)_children.getIndex(child); + CCASSERT(oldIndex != -1, "Not a child of this container"); + + if (oldIndex < index) + return moveChild(child, oldIndex, index - 1); + else + return moveChild(child, oldIndex, index); +} + +int GTreeNode::moveChild(GTreeNode* child, int oldIndex, int index) +{ + int cnt = (int)_children.size(); + if (index > cnt) + index = cnt; + + if (oldIndex == index) + return oldIndex; + + child->retain(); + _children.erase(oldIndex); + if (index >= cnt) + _children.pushBack(child); + else + _children.insert(index, child); + child->release(); + if ((_tree != nullptr && this == _tree->getRootNode()) || (_cell != nullptr && _cell->getParent() != nullptr && _expanded)) + _tree->afterMoved(child); + + return index; +} + +void GTreeNode::swapChildren(GTreeNode* child1, GTreeNode* child2) +{ + CCASSERT(child1 != nullptr, "Argument1 must be non-nil"); + CCASSERT(child2 != nullptr, "Argument2 must be non-nil"); + + int index1 = (int)_children.getIndex(child1); + int index2 = (int)_children.getIndex(child2); + + CCASSERT(index1 != -1, "Not a child of this container"); + CCASSERT(index2 != -1, "Not a child of this container"); + + swapChildrenAt(index1, index2); +} + +void GTreeNode::swapChildrenAt(int index1, int index2) +{ + GTreeNode* child1 = _children.at(index1); + GTreeNode* child2 = _children.at(index2); + + setChildIndex(child1, index2); + setChildIndex(child2, index1); +} + +int GTreeNode::numChildren() const +{ + return (int)_children.size(); +} + +void GTreeNode::setTree(GTree* value) +{ + _tree = value; + if (_tree != nullptr && _tree->treeNodeWillExpand != nullptr && _expanded) + _tree->treeNodeWillExpand(this, true); + + if (_isFolder) + { + for (auto& child : _children) + { + child->_level = _level + 1; + child->setTree(value); + } + } +} + +void GTreeNode::setCell(GComponent* value) +{ + if (_cell != value) + { + CC_SAFE_RELEASE(_cell); + _cell = value; + CC_SAFE_RETAIN(_cell); + if (_cell) + _cell->setData(this); + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/GTreeNode.h b/extensions/fairygui/GTreeNode.h new file mode 100644 index 0000000000..dc81c8a4d5 --- /dev/null +++ b/extensions/fairygui/GTreeNode.h @@ -0,0 +1,74 @@ +#ifndef __GTREENODE_H__ +#define __GTREENODE_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GTree; +class GComponent; + +class GTreeNode : public cocos2d::Ref +{ +public: + static GTreeNode* create(bool isFolder = false, const std::string& resURL = cocos2d::STD_STRING_EMPTY); + + GTreeNode(); + virtual ~GTreeNode(); + + GTreeNode* getParent() const { return _parent; } + GTree* getTree() const { return _tree; } + GComponent* getCell() const { return _cell; } + const cocos2d::Value& getData() const { return _data; } + void setData(const cocos2d::Value& value) { _data = value; } + bool isExpanded() const { return _expanded; } + void setExpaned(bool value); + bool isFolder() const { return _isFolder; } + const std::string& getText() const; + void setText(const std::string& value); + const std::string& getIcon() const; + void setIcon(const std::string& value); + + GTreeNode* addChild(GTreeNode* child); + GTreeNode* addChildAt(GTreeNode* child, int index); + + void removeChild(GTreeNode* child); + void removeChildAt(int index); + void removeChildren() { removeChildren(0, -1); } + void removeChildren(int beginIndex, int endIndex); + + GTreeNode* getChildAt(int index) const; + GTreeNode* getPrevSibling() const; + GTreeNode* getNextSibling() const; + + int getChildIndex(const GTreeNode* child) const; + void setChildIndex(GTreeNode* child, int index); + int setChildIndexBefore(GTreeNode* child, int index); + void swapChildren(GTreeNode* child1, GTreeNode* child2); + void swapChildrenAt(int index1, int index2); + + int numChildren() const; + +private: + bool init(bool isFolder, const std::string& resURL); + int moveChild(GTreeNode* child, int oldIndex, int index); + void setTree(GTree* value); + void setCell(GComponent* value); + + GTree* _tree; + GTreeNode* _parent; + GComponent* _cell; + int _level; + bool _expanded; + bool _isFolder; + cocos2d::Value _data; + cocos2d::Vector _children; + std::string _resURL; + + friend class GTree; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/Margin.cpp b/extensions/fairygui/Margin.cpp new file mode 100644 index 0000000000..e4a0e510c9 --- /dev/null +++ b/extensions/fairygui/Margin.cpp @@ -0,0 +1,38 @@ +#include "Margin.h" + +NS_FGUI_BEGIN + +const Margin Margin::ZERO = Margin(0, 0, 0, 0); + +Margin::Margin(void) : left(0), top(0), right(0), bottom(0) +{ +} + +Margin::Margin(float l, float t, float r, float b) : left(l), top(t), right(r), bottom(b) +{ +} + +Margin::Margin(const Margin& other) : left(other.left), top(other.top), right(other.right), bottom(other.bottom) +{ +} + +Margin& Margin::operator= (const Margin& other) +{ + setMargin(other.left, other.top, other.right, other.bottom); + return *this; +} + +void Margin::setMargin(float l, float t, float r, float b) +{ + left = l; + top = t; + right = r; + bottom = b; +} + +bool Margin::equals(const Margin &target) const +{ + return (left == target.left && top == target.top && right == target.right && bottom == target.bottom); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/Margin.h b/extensions/fairygui/Margin.h new file mode 100644 index 0000000000..c63d2605a6 --- /dev/null +++ b/extensions/fairygui/Margin.h @@ -0,0 +1,78 @@ +#ifndef __MARGIN_H__ +#define __MARGIN_H__ + +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +//copy from cocos2d::ui::Margin +class Margin +{ +public: + /** + * Left margin. + */ + float left; + /** + * Top margin. + */ + float top; + /** + * Right margin. + */ + float right; + /** + * Bottom margin. + */ + float bottom; + +public: + /** + * Default constructor. + */ + Margin(); + + /** + * Construct a Margin instance with left, top, right and bottom margins. + *@param l Left margin in float. + *@param t Top margin in float. + *@param r Right margin in float. + *@param b Bottom margin in float. + */ + Margin(float l, float t, float r, float b); + + /** + * Copy constructor. + */ + Margin(const Margin& other); + + /** + * Copy assignment operator. + */ + Margin& operator= (const Margin& other); + + /** + * Change margin with left, top, right and bottom margin. + *@param l Left margin in float. + *@param t Top margin in float. + *@param r Right margin in float. + *@param b Bottom margin in float. + */ + void setMargin(float l, float t, float r, float b); + + /** + * Test equality of two margins. + *@param target A Margin instance. + *@return True if two margins are equal, false otherwise. + */ + bool equals(const Margin& target) const; + + /** + * A margin constant with all margins equal zero. + */ + static const Margin ZERO; +}; + +NS_FGUI_END + +#endif \ No newline at end of file diff --git a/extensions/fairygui/PackageItem.cpp b/extensions/fairygui/PackageItem.cpp new file mode 100644 index 0000000000..5d2e53a0dd --- /dev/null +++ b/extensions/fairygui/PackageItem.cpp @@ -0,0 +1,75 @@ +#include "PackageItem.h" +#include "GRoot.h" +#include "UIPackage.h" +#include "display/BitmapFont.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN + +PackageItem::PackageItem() : owner(nullptr), + objectType(ObjectType::COMPONENT), + width(0), + height(0), + rawData(nullptr), + texture(nullptr), + spriteFrame(nullptr), + scale9Grid(nullptr), + scaleByTile(false), + tileGridIndice(0), + animation(nullptr), + repeatDelay(0), + swing(false), + extensionCreator(nullptr), + translated(false), + bitmapFont(nullptr), + branches(nullptr), + highResolution(nullptr) +{ +} + +PackageItem::~PackageItem() +{ + CC_SAFE_DELETE(scale9Grid); + + CC_SAFE_DELETE(rawData); + if (bitmapFont) //bitmapfont will be released by fontatlas + bitmapFont->releaseAtlas(); + bitmapFont = nullptr; + CC_SAFE_RELEASE(animation); + CC_SAFE_RELEASE(texture); + CC_SAFE_RELEASE(spriteFrame); + + CC_SAFE_DELETE(branches); + CC_SAFE_DELETE(highResolution); +} + +void PackageItem::load() +{ + owner->getItemAsset(this); +} + +PackageItem* PackageItem::getBranch() +{ + if (branches != nullptr && owner->_branchIndex != -1) + { + std::string itemId = (*branches)[owner->_branchIndex]; + if (!itemId.empty()) + return owner->getItem(itemId); + } + + return this; +} + +PackageItem* PackageItem::getHighResolution() +{ + if (highResolution != nullptr && GRoot::contentScaleLevel > 0) + { + std::string itemId = (*highResolution)[GRoot::contentScaleLevel - 1]; + if (!itemId.empty()) + return owner->getItem(itemId); + } + + return this; +} + +NS_FGUI_END diff --git a/extensions/fairygui/PackageItem.h b/extensions/fairygui/PackageItem.h new file mode 100644 index 0000000000..63b5dc38e5 --- /dev/null +++ b/extensions/fairygui/PackageItem.h @@ -0,0 +1,69 @@ +#ifndef __PACKAGEITEM_H__ +#define __PACKAGEITEM_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class UIPackage; +class UIObjectFactory; +class GComponent; +class BitmapFont; +class PixelHitTestData; +class ByteBuffer; + +class PackageItem : public cocos2d::Ref +{ +public: + PackageItem(); + virtual ~PackageItem(); + + void load(); + PackageItem* getBranch(); + PackageItem* getHighResolution(); + +public: + UIPackage* owner; + + PackageItemType type; + ObjectType objectType; + std::string id; + std::string name; + int width; + int height; + std::string file; + ByteBuffer* rawData; + std::vector* branches; + std::vector* highResolution; + + //atlas + cocos2d::Texture2D* texture; + + //image + cocos2d::Rect* scale9Grid; + bool scaleByTile; + int tileGridIndice; + cocos2d::SpriteFrame* spriteFrame; + PixelHitTestData* pixelHitTestData; + + //movieclip + cocos2d::Animation* animation; + float delayPerUnit; + float repeatDelay; + bool swing; + + //component + std::function extensionCreator; + bool translated; + + //font + BitmapFont* bitmapFont; + + //skeleton + cocos2d::Vec2* skeletonAnchor; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/PopupMenu.cpp b/extensions/fairygui/PopupMenu.cpp new file mode 100644 index 0000000000..b5de56e5d1 --- /dev/null +++ b/extensions/fairygui/PopupMenu.cpp @@ -0,0 +1,239 @@ +#include "PopupMenu.h" +#include "GRoot.h" +#include "UIPackage.h" +#include "GList.h" +#include "GButton.h" +#include "UIConfig.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +PopupMenu* PopupMenu::create(const std::string & resourceURL) +{ + PopupMenu *pRet = new(std::nothrow) PopupMenu(); + if (pRet && pRet->init(resourceURL)) + { + pRet->autorelease(); + return pRet; + } + else + { + delete pRet; + pRet = nullptr; + return nullptr; + } +} + +PopupMenu::PopupMenu() : + _contentPane(nullptr), + _list(nullptr) +{ +} + +PopupMenu::~PopupMenu() +{ + CC_SAFE_RELEASE(_contentPane); +} + +bool PopupMenu::init(const std::string & resourceURL) +{ + std::string url = resourceURL; + if (url.empty()) + { + url = UIConfig::popupMenu; + if (url.empty()) + { + CCLOGWARN("FairyGUI: UIConfig.popupMenu not defined"); + return false; + } + } + + _contentPane = UIPackage::createObjectFromURL(url)->as(); + _contentPane->retain(); + _contentPane->addEventListener(UIEventType::Enter, CC_CALLBACK_1(PopupMenu::onEnter, this)); + + _list = _contentPane->getChild("list")->as(); + _list->removeChildrenToPool(); + + _list->addRelation(_contentPane, RelationType::Width); + _list->removeRelation(_contentPane, RelationType::Height); + _contentPane->addRelation(_list, RelationType::Height); + + _list->addEventListener(UIEventType::ClickItem, CC_CALLBACK_1(PopupMenu::onClickItem, this)); + + return true; +} + +GButton * PopupMenu::addItem(const std::string & caption, EventCallback callback) +{ + GButton* item = _list->addItemFromPool()->as(); + item->setTitle(caption); + item->setGrayed(false); + GController* c = item->getController("checked"); + if (c != nullptr) + c->setSelectedIndex(0); + item->removeEventListener(UIEventType::ClickMenu); + if (callback) + item->addEventListener(UIEventType::ClickMenu, callback); + + return item; +} + +GButton * PopupMenu::addItemAt(const std::string & caption, int index, EventCallback callback) +{ + GButton* item = _list->getFromPool(_list->getDefaultItem())->as(); + _list->addChildAt(item, index); + + item->setTitle(caption); + item->setGrayed(false); + GController* c = item->getController("checked"); + if (c != nullptr) + c->setSelectedIndex(0); + item->removeEventListener(UIEventType::ClickMenu); + if (callback) + item->addEventListener(UIEventType::ClickMenu, callback); + + return item; +} + +void PopupMenu::addSeperator() +{ + if (UIConfig::popupMenu_seperator.empty()) + { + CCLOGWARN("FairyGUI: UIConfig.popupMenu_seperator not defined"); + return; + } + + _list->addItemFromPool(UIConfig::popupMenu_seperator); +} + +const std::string & PopupMenu::getItemName(int index) const +{ + GButton* item = _list->getChildAt(index)->as(); + return item->name; +} + +void PopupMenu::setItemText(const std::string & name, const std::string & caption) +{ + GButton* item = _list->getChild(name)->as(); + item->setTitle(caption); +} + +void PopupMenu::setItemVisible(const std::string & name, bool visible) +{ + GButton* item = _list->getChild(name)->as(); + if (item->isVisible() != visible) + { + item->setVisible(visible); + _list->setBoundsChangedFlag(); + } +} + +void PopupMenu::setItemGrayed(const std::string & name, bool grayed) +{ + GButton* item = _list->getChild(name)->as(); + item->setGrayed(grayed); +} + +void PopupMenu::setItemCheckable(const std::string & name, bool checkable) +{ + GButton* item = _list->getChild(name)->as(); + GController* c = item->getController("checked"); + if (c != nullptr) + { + if (checkable) + { + if (c->getSelectedIndex() == 0) + c->setSelectedIndex(1); + } + else + c->setSelectedIndex(0); + } +} + +void PopupMenu::setItemChecked(const std::string & name, bool check) +{ + GButton* item = _list->getChild(name)->as(); + GController* c = item->getController("checked"); + if (c != nullptr) + c->setSelectedIndex(check ? 2 : 1); +} + +bool PopupMenu::isItemChecked(const std::string & name) const +{ + GButton* item = _list->getChild(name)->as(); + GController* c = item->getController("checked"); + if (c != nullptr) + return c->getSelectedIndex() == 2; + else + return false; +} + +bool PopupMenu::removeItem(const std::string & name) +{ + GObject* item = _list->getChild(name); + if (item != nullptr) + { + int index = _list->getChildIndex(item); + _list->removeChildToPoolAt(index); + item->removeEventListener(UIEventType::ClickMenu); + + return true; + } + else + return false; +} + +void PopupMenu::clearItems() +{ + int cnt = _list->numChildren(); + for (int i = 0; i < cnt; i++) + _list->getChildAt(i)->removeEventListener(UIEventType::ClickMenu); + _list->removeChildrenToPool(); +} + +int PopupMenu::getItemCount() const +{ + return _list->numChildren(); +} + +void PopupMenu::show(GObject * target, PopupDirection dir) +{ + GRoot* r = target != nullptr ? target->getRoot() : UIRoot; + r->showPopup(_contentPane, dynamic_cast(target) ? nullptr : target, dir); +} + +void PopupMenu::onClickItem(EventContext * context) +{ + GButton* item = ((GObject*)context->getData())->as(); + if (item == nullptr) + return; + + if (item->isGrayed()) + { + _list->setSelectedIndex(-1); + return; + } + + GController* c = item->getController("checked"); + if (c != nullptr && c->getSelectedIndex() != 0) + { + if (c->getSelectedIndex() == 1) + c->setSelectedIndex(2); + else + c->setSelectedIndex(1); + } + + GRoot* r = (GRoot*)_contentPane->getParent(); + r->hidePopup(_contentPane); + + item->dispatchEvent(UIEventType::ClickMenu, context->getData()); +} + +void PopupMenu::onEnter(EventContext * context) +{ + _list->setSelectedIndex(-1); + _list->resizeToFit(INT_MAX, 10); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/PopupMenu.h b/extensions/fairygui/PopupMenu.h new file mode 100644 index 0000000000..da0d1ded80 --- /dev/null +++ b/extensions/fairygui/PopupMenu.h @@ -0,0 +1,55 @@ +#ifndef __POPUPMENU_H__ +#define __POPUPMENU_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "event/UIEventDispatcher.h" + +NS_FGUI_BEGIN + +class GObject; +class GComponent; +class GButton; +class GList; + +class PopupMenu : public cocos2d::Ref +{ +public: + static PopupMenu* create(const std::string& resourceURL); + static PopupMenu* create() { return create(""); } + + PopupMenu(); + virtual ~PopupMenu(); + + GButton* addItem(const std::string& caption, EventCallback callback); + GButton* addItemAt(const std::string& caption, int index, EventCallback callback); + void addSeperator(); + const std::string& getItemName(int index) const; + void setItemText(const std::string& name, const std::string& caption); + void setItemVisible(const std::string& name, bool visible); + void setItemGrayed(const std::string& name, bool grayed); + void setItemCheckable(const std::string& name, bool checkable); + void setItemChecked(const std::string& name, bool check); + bool isItemChecked(const std::string& name) const; + bool removeItem(const std::string& name); + void clearItems(); + int getItemCount() const; + GComponent* getContentPane() const { return _contentPane; } + GList* getList() const { return _list; } + void show() { show(nullptr, PopupDirection::AUTO); } + void show(GObject* target, PopupDirection dir); + +protected: + bool init(const std::string& resourceURL); + + GComponent* _contentPane; + GList* _list; + +private: + void onClickItem(EventContext* context); + void onEnter(EventContext* context); +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/RelationItem.cpp b/extensions/fairygui/RelationItem.cpp new file mode 100644 index 0000000000..89f047fa76 --- /dev/null +++ b/extensions/fairygui/RelationItem.cpp @@ -0,0 +1,655 @@ +#include "RelationItem.h" +#include "GComponent.h" +#include "GGroup.h" +#include "event/UIEventType.h" +#include "utils/WeakPtr.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +RelationItem::RelationItem(GObject* owner) : _target(nullptr) +{ + _owner = owner; +} + +RelationItem::~RelationItem() +{ + releaseRefTarget(_target.ptr()); +} + +void RelationItem::setTarget(GObject* value) +{ + GObject* old = _target.ptr(); + if (old != value) + { + if (old) + releaseRefTarget(old); + _target = value; + if (value) + addRefTarget(value); + } +} + +void RelationItem::add(RelationType relationType, bool usePercent) +{ + if (relationType == RelationType::Size) + { + add(RelationType::Width, usePercent); + add(RelationType::Height, usePercent); + return; + } + + for (auto& it : _defs) + { + if (it.type == relationType) + return; + } + + internalAdd(relationType, usePercent); +} + +void RelationItem::internalAdd(RelationType relationType, bool usePercent) +{ + if (relationType == RelationType::Size) + { + internalAdd(RelationType::Width, usePercent); + internalAdd(RelationType::Height, usePercent); + return; + } + + RelationDef info; + info.percent = usePercent; + info.type = relationType; + info.axis = (relationType <= RelationType::Right_Right || relationType == RelationType::Width || (relationType >= RelationType::LeftExt_Left && relationType <= RelationType::RightExt_Right)) ? 0 : 1; + _defs.push_back(info); + + if (usePercent || relationType == RelationType::Left_Center || relationType == RelationType::Center_Center || relationType == RelationType::Right_Center || relationType == RelationType::Top_Middle || relationType == RelationType::Middle_Middle || relationType == RelationType::Bottom_Middle) + _owner->setPixelSnapping(true); +} + +void RelationItem::remove(RelationType relationType) +{ + if (relationType == RelationType::Size) + { + remove(RelationType::Width); + remove(RelationType::Height); + return; + } + + for (auto it = _defs.begin(); it != _defs.end(); ++it) + { + if (it->type == relationType) + { + _defs.erase(it); + break; + } + } +} + +void RelationItem::copyFrom(const RelationItem& source) +{ + setTarget(source._target.ptr()); + + _defs.clear(); + for (auto& it : source._defs) + _defs.push_back(it); +} + +bool RelationItem::isEmpty() const +{ + return _defs.size() == 0; +} + +void RelationItem::applyOnSelfSizeChanged(float dWidth, float dHeight, bool applyPivot) +{ + if (_target == nullptr || _defs.size() == 0) + return; + + float ox = _owner->_position.x; + float oy = _owner->_position.y; + + for (auto& it : _defs) + { + switch (it.type) + { + case RelationType::Center_Center: + _owner->setX(_owner->_position.x - (0.5 - (applyPivot ? _owner->_pivot.x : 0)) * dWidth); + break; + + case RelationType::Right_Center: + case RelationType::Right_Left: + case RelationType::Right_Right: + _owner->setX(_owner->_position.x - (1 - (applyPivot ? _owner->_pivot.x : 0)) * dWidth); + break; + + case RelationType::Middle_Middle: + _owner->setY(_owner->_position.y - (0.5 - (applyPivot ? _owner->_pivot.y : 0)) * dHeight); + break; + + case RelationType::Bottom_Middle: + case RelationType::Bottom_Top: + case RelationType::Bottom_Bottom: + _owner->setY(_owner->_position.y - (1 - (applyPivot ? _owner->_pivot.y : 0)) * dHeight); + break; + + default: + break; + } + } + + if (ox != _owner->_position.x || oy != _owner->_position.y) + { + ox = _owner->_position.x - ox; + oy = _owner->_position.y - oy; + + _owner->updateGearFromRelations(1, ox, oy); + + if (_owner->_parent != nullptr) + { + const Vector& arr = _owner->_parent->getTransitions(); + for (auto& it : arr) + it->updateFromRelations(_owner->id, ox, oy); + } + } +} + +void RelationItem::applyOnXYChanged(GObject* target, const RelationDef& info, float dx, float dy) +{ + float tmp; + + switch (info.type) + { + case RelationType::Left_Left: + case RelationType::Left_Center: + case RelationType::Left_Right: + case RelationType::Center_Center: + case RelationType::Right_Left: + case RelationType::Right_Center: + case RelationType::Right_Right: + _owner->setX(_owner->_position.x + dx); + break; + + case RelationType::Top_Top: + case RelationType::Top_Middle: + case RelationType::Top_Bottom: + case RelationType::Middle_Middle: + case RelationType::Bottom_Top: + case RelationType::Bottom_Middle: + case RelationType::Bottom_Bottom: + _owner->setY(_owner->_position.y + dy); + break; + + case RelationType::Width: + case RelationType::Height: + break; + + case RelationType::LeftExt_Left: + case RelationType::LeftExt_Right: + if (_owner != target->getParent()) + { + tmp = _owner->getXMin(); + _owner->setWidth(_owner->_rawSize.width - dx); + _owner->setXMin(tmp + dx); + } + else + _owner->setWidth(_owner->_rawSize.width - dx); + break; + + case RelationType::RightExt_Left: + case RelationType::RightExt_Right: + if (_owner != target->getParent()) + { + tmp = _owner->getXMin(); + _owner->setWidth(_owner->_rawSize.width + dx); + _owner->setXMin(tmp); + } + else + _owner->setWidth(_owner->_rawSize.width + dx); + break; + + case RelationType::TopExt_Top: + case RelationType::TopExt_Bottom: + if (_owner != target->getParent()) + { + tmp = _owner->getYMin(); + _owner->setHeight(_owner->_rawSize.height - dy); + _owner->setYMin(tmp + dy); + } + else + _owner->setHeight(_owner->_rawSize.height - dy); + break; + + case RelationType::BottomExt_Top: + case RelationType::BottomExt_Bottom: + if (_owner != target->getParent()) + { + tmp = _owner->getYMin(); + _owner->setHeight(_owner->_rawSize.height + dy); + _owner->setYMin(tmp); + } + else + _owner->setHeight(_owner->_rawSize.height + dy); + break; + + default: + break; + } +} + +void RelationItem::applyOnSizeChanged(GObject* target, const RelationDef& info) +{ + float pos = 0, pivot = 0, delta = 0; + if (info.axis == 0) + { + if (target != _owner->_parent) + { + pos = target->_position.x; + if (target->_pivotAsAnchor) + pivot = target->_pivot.x; + } + + if (info.percent) + { + if (_targetData.z != 0) + delta = target->_size.width / _targetData.z; + } + else + delta = target->_size.width - _targetData.z; + } + else + { + if (target != _owner->_parent) + { + pos = target->_position.y; + if (target->_pivotAsAnchor) + pivot = target->_pivot.y; + } + + if (info.percent) + { + if (_targetData.w != 0) + delta = target->_size.height / _targetData.w; + } + else + delta = target->_size.height - _targetData.w; + } + + float v, tmp; + + switch (info.type) + { + case RelationType::Left_Left: + if (info.percent) + _owner->setXMin(pos + (_owner->getXMin() - pos) * delta); + else if (pivot != 0) + _owner->setX(_owner->_position.x + delta * (-pivot)); + break; + case RelationType::Left_Center: + if (info.percent) + _owner->setXMin(pos + (_owner->getXMin() - pos) * delta); + else + _owner->setX(_owner->_position.x + delta * (0.5f - pivot)); + break; + case RelationType::Left_Right: + if (info.percent) + _owner->setXMin(pos + (_owner->getXMin() - pos) * delta); + else + _owner->setX(_owner->_position.x + delta * (1 - pivot)); + break; + case RelationType::Center_Center: + if (info.percent) + _owner->setXMin(pos + (_owner->getXMin() + _owner->_rawSize.width * 0.5f - pos) * delta - _owner->_rawSize.width * 0.5f); + else + _owner->setX(_owner->_position.x + delta * (0.5f - pivot)); + break; + case RelationType::Right_Left: + if (info.percent) + _owner->setXMin(pos + (_owner->getXMin() + _owner->_rawSize.width - pos) * delta - _owner->_rawSize.width); + else if (pivot != 0) + _owner->setX(_owner->_position.x + delta * (-pivot)); + break; + case RelationType::Right_Center: + if (info.percent) + _owner->setXMin(pos + (_owner->getXMin() + _owner->_rawSize.width - pos) * delta - _owner->_rawSize.width); + else + _owner->setX(_owner->_position.x + delta * (0.5f - pivot)); + break; + case RelationType::Right_Right: + if (info.percent) + _owner->setXMin(pos + (_owner->getXMin() + _owner->_rawSize.width - pos) * delta - _owner->_rawSize.width); + else + _owner->setX(_owner->_position.x + delta * (1 - pivot)); + break; + + case RelationType::Top_Top: + if (info.percent) + _owner->setYMin(pos + (_owner->getYMin() - pos) * delta); + else if (pivot != 0) + _owner->setY(_owner->_position.y + delta * (-pivot)); + break; + case RelationType::Top_Middle: + if (info.percent) + _owner->setYMin(pos + (_owner->getYMin() - pos) * delta); + else + _owner->setY(_owner->_position.y + delta * (0.5f - pivot)); + break; + case RelationType::Top_Bottom: + if (info.percent) + _owner->setYMin(pos + (_owner->getYMin() - pos) * delta); + else + _owner->setY(_owner->_position.y + delta * (1 - pivot)); + break; + case RelationType::Middle_Middle: + if (info.percent) + _owner->setYMin(pos + (_owner->getYMin() + _owner->_rawSize.height * 0.5f - pos) * delta - _owner->_rawSize.height * 0.5f); + else + _owner->setY(_owner->_position.y + delta * (0.5f - pivot)); + break; + case RelationType::Bottom_Top: + if (info.percent) + _owner->setYMin(pos + (_owner->getYMin() + _owner->_rawSize.height - pos) * delta - _owner->_rawSize.height); + else if (pivot != 0) + _owner->setY(_owner->_position.y + delta * (-pivot)); + break; + case RelationType::Bottom_Middle: + if (info.percent) + _owner->setYMin(pos + (_owner->getYMin() + _owner->_rawSize.height - pos) * delta - _owner->_rawSize.height); + else + _owner->setY(_owner->_position.y + delta * (0.5f - pivot)); + break; + case RelationType::Bottom_Bottom: + if (info.percent) + _owner->setYMin(pos + (_owner->getYMin() + _owner->_rawSize.height - pos) * delta - _owner->_rawSize.height); + else + _owner->setY(_owner->_position.y + delta * (1 - pivot)); + break; + + case RelationType::Width: + if (_owner->_underConstruct && _owner == target->_parent) + v = _owner->sourceSize.width - target->initSize.width; + else + v = _owner->_rawSize.width - _targetData.z; + if (info.percent) + v = v * delta; + if (_target == _owner->_parent) + { + if (_owner->_pivotAsAnchor) + { + tmp = _owner->getXMin(); + _owner->setSize(target->_size.width + v, _owner->_rawSize.height, true); + _owner->setXMin(tmp); + } + else + _owner->setSize(target->_size.width + v, _owner->_rawSize.height, true); + } + else + _owner->setWidth(target->_size.width + v); + break; + case RelationType::Height: + if (_owner->_underConstruct && _owner == target->_parent) + v = _owner->sourceSize.height - target->initSize.height; + else + v = _owner->_rawSize.height - _targetData.w; + if (info.percent) + v = v * delta; + if (_target == _owner->_parent) + { + if (_owner->_pivotAsAnchor) + { + tmp = _owner->getYMin(); + _owner->setSize(_owner->_rawSize.width, target->_size.height + v, true); + _owner->setYMin(tmp); + } + else + _owner->setSize(_owner->_rawSize.width, target->_size.height + v, true); + } + else + _owner->setHeight(target->_size.height + v); + break; + + case RelationType::LeftExt_Left: + tmp = _owner->getXMin(); + if (info.percent) + v = pos + (tmp - pos) * delta - tmp; + else + v = delta * (-pivot); + _owner->setWidth(_owner->_rawSize.width - v); + _owner->setXMin(tmp + v); + break; + case RelationType::LeftExt_Right: + tmp = _owner->getXMin(); + if (info.percent) + v = pos + (tmp - pos) * delta - tmp; + else + v = delta * (1 - pivot); + _owner->setWidth(_owner->_rawSize.width - v); + _owner->setXMin(tmp + v); + break; + case RelationType::RightExt_Left: + tmp = _owner->getXMin(); + if (info.percent) + v = pos + (tmp + _owner->_rawSize.width - pos) * delta - (tmp + _owner->_rawSize.width); + else + v = delta * (-pivot); + _owner->setWidth(_owner->_rawSize.width + v); + _owner->setXMin(tmp); + break; + case RelationType::RightExt_Right: + tmp = _owner->getXMin(); + if (info.percent) + { + if (_owner == target->_parent) + { + if (_owner->_underConstruct) + _owner->setWidth(pos + target->_size.width - target->_size.width * pivot + + (_owner->sourceSize.width - pos - target->initSize.width + target->initSize.width * pivot) * delta); + else + _owner->setWidth(pos + (_owner->_rawSize.width - pos) * delta); + } + else + { + v = pos + (tmp + _owner->_rawSize.width - pos) * delta - (tmp + _owner->_rawSize.width); + _owner->setWidth(_owner->_rawSize.width + v); + _owner->setXMin(tmp); + } + } + else + { + if (_owner == target->_parent) + { + if (_owner->_underConstruct) + _owner->setWidth(_owner->sourceSize.width + (target->_size.width - target->initSize.width) * (1 - pivot)); + else + _owner->setWidth(_owner->_rawSize.width + delta * (1 - pivot)); + } + else + { + v = delta * (1 - pivot); + _owner->setWidth(_owner->_rawSize.width + v); + _owner->setXMin(tmp); + } + } + break; + case RelationType::TopExt_Top: + tmp = _owner->getYMin(); + if (info.percent) + v = pos + (tmp - pos) * delta - tmp; + else + v = delta * (-pivot); + _owner->setHeight(_owner->_rawSize.height - v); + _owner->setYMin(tmp + v); + break; + case RelationType::TopExt_Bottom: + tmp = _owner->getYMin(); + if (info.percent) + v = pos + (tmp - pos) * delta - tmp; + else + v = delta * (1 - pivot); + _owner->setHeight(_owner->_rawSize.height - v); + _owner->setYMin(tmp + v); + break; + case RelationType::BottomExt_Top: + tmp = _owner->getYMin(); + if (info.percent) + v = pos + (tmp + _owner->_rawSize.height - pos) * delta - (tmp + _owner->_rawSize.height); + else + v = delta * (-pivot); + _owner->setHeight(_owner->_rawSize.height + v); + _owner->setYMin(tmp); + break; + case RelationType::BottomExt_Bottom: + tmp = _owner->getYMin(); + if (info.percent) + { + if (_owner == target->_parent) + { + if (_owner->_underConstruct) + _owner->setHeight(pos + target->_size.height - target->_size.height * pivot + + (_owner->sourceSize.height - pos - target->initSize.height + target->initSize.height * pivot) * delta); + else + _owner->setHeight(pos + (_owner->_rawSize.height - pos) * delta); + } + else + { + v = pos + (tmp + _owner->_rawSize.height - pos) * delta - (tmp + _owner->_rawSize.height); + _owner->setHeight(_owner->_rawSize.height + v); + _owner->setYMin(tmp); + } + } + else + { + if (_owner == target->_parent) + { + if (_owner->_underConstruct) + _owner->setHeight(_owner->sourceSize.height + (target->_size.height - target->initSize.height) * (1 - pivot)); + else + _owner->setHeight(_owner->_rawSize.height + delta * (1 - pivot)); + } + else + { + v = delta * (1 - pivot); + _owner->setHeight(_owner->_rawSize.height + v); + _owner->setYMin(tmp); + } + } + break; + default: + break; + } +} + +void RelationItem::addRefTarget(GObject* target) +{ + if (!target) + return; + + if (target != _owner->_parent) + target->addEventListener(UIEventType::PositionChange, CC_CALLBACK_1(RelationItem::onTargetXYChanged, this), EventTag(this)); + target->addEventListener(UIEventType::SizeChange, CC_CALLBACK_1(RelationItem::onTargetSizeChanged, this), EventTag(this)); + + _targetData.x = target->_position.x; + _targetData.y = target->_position.y; + _targetData.z = target->_size.width; + _targetData.w = target->_size.height; +} + +void RelationItem::releaseRefTarget(GObject* target) +{ + if (!target) + return; + + target->removeEventListener(UIEventType::PositionChange, EventTag(this)); + target->removeEventListener(UIEventType::SizeChange, EventTag(this)); +} + +void RelationItem::onTargetXYChanged(EventContext* context) +{ + GObject* target = (GObject*)context->getSender(); + if (_owner->relations()->handling != nullptr || (_owner->_group != nullptr && _owner->_group->_updating != 0)) + { + _targetData.x = target->_position.x; + _targetData.y = target->_position.y; + return; + } + + _owner->relations()->handling = target; + + float ox = _owner->_position.x; + float oy = _owner->_position.y; + float dx = target->_position.x - _targetData.x; + float dy = target->_position.y - _targetData.y; + + for (auto& it : _defs) + applyOnXYChanged(target, it, dx, dy); + + _targetData.x = target->_position.x; + _targetData.y = target->_position.y; + + if (ox != _owner->_position.x || oy != _owner->_position.y) + { + ox = _owner->_position.x - ox; + oy = _owner->_position.y - oy; + + _owner->updateGearFromRelations(1, ox, oy); + + if (_owner->_parent != nullptr) + { + const Vector& arr = _owner->_parent->getTransitions(); + for (auto& it : arr) + it->updateFromRelations(_owner->id, ox, oy); + } + } + + _owner->relations()->handling = nullptr; +} + +void RelationItem::onTargetSizeChanged(EventContext* context) +{ + GObject* target = (GObject*)context->getSender(); + if (_owner->relations()->handling != nullptr || (_owner->_group != nullptr && _owner->_group->_updating != 0)) + { + _targetData.z = target->_size.width; + _targetData.w = target->_size.height; + return; + } + + _owner->relations()->handling = target; + + float ox = _owner->_position.x; + float oy = _owner->_position.y; + float ow = _owner->_rawSize.width; + float oh = _owner->_rawSize.height; + + for (auto& it : _defs) + applyOnSizeChanged(target, it); + + _targetData.z = target->_size.width; + _targetData.w = target->_size.height; + + if (ox != _owner->_position.x || oy != _owner->_position.y) + { + ox = _owner->_position.x - ox; + oy = _owner->_position.y - oy; + + _owner->updateGearFromRelations(1, ox, oy); + + if (_owner->_parent != nullptr) + { + const Vector& arr = _owner->_parent->getTransitions(); + for (auto& it : arr) + it->updateFromRelations(_owner->id, ox, oy); + } + } + + if (ow != _owner->_rawSize.width || oh != _owner->_rawSize.height) + { + ow = _owner->_rawSize.width - ow; + oh = _owner->_rawSize.height - oh; + + _owner->updateGearFromRelations(2, ow, oh); + } + + _owner->relations()->handling = nullptr; +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/RelationItem.h b/extensions/fairygui/RelationItem.h new file mode 100644 index 0000000000..c58d25546d --- /dev/null +++ b/extensions/fairygui/RelationItem.h @@ -0,0 +1,95 @@ +#ifndef __RELATIONITEM_H__ +#define __RELATIONITEM_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "utils/WeakPtr.h" + +NS_FGUI_BEGIN + +class GObject; +class EventContext; + +enum class RelationType +{ + Left_Left, + Left_Center, + Left_Right, + Center_Center, + Right_Left, + Right_Center, + Right_Right, + + Top_Top, + Top_Middle, + Top_Bottom, + Middle_Middle, + Bottom_Top, + Bottom_Middle, + Bottom_Bottom, + + Width, + Height, + + LeftExt_Left, + LeftExt_Right, + RightExt_Left, + RightExt_Right, + TopExt_Top, + TopExt_Bottom, + BottomExt_Top, + BottomExt_Bottom, + + Size +}; + +class RelationDef +{ +public: + bool percent; + RelationType type; + int axis; + + RelationDef() {} + + RelationDef(const RelationDef& source) + { + this->percent = source.percent; + this->type = source.type; + this->axis = source.axis; + } +}; + +class RelationItem +{ +public: + RelationItem(GObject* owner); + ~RelationItem(); + + GObject* getTarget() { return _target.ptr(); } + void setTarget(GObject* value); + + void add(RelationType relationType, bool usePercent); + void internalAdd(RelationType relationType, bool usePercent); + void remove(RelationType relationType); + void copyFrom(const RelationItem& source); + bool isEmpty() const; + void applyOnSelfSizeChanged(float dWidth, float dHeight, bool applyPivot); + +private: + void applyOnXYChanged(GObject* target, const RelationDef& info, float dx, float dy); + void applyOnSizeChanged(GObject* target, const RelationDef& info); + void addRefTarget(GObject* target); + void releaseRefTarget(GObject* target); + void onTargetXYChanged(EventContext* context); + void onTargetSizeChanged(EventContext* context); + + GObject* _owner; + WeakPtr _target; + std::vector _defs; + cocos2d::Vec4 _targetData; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/Relations.cpp b/extensions/fairygui/Relations.cpp new file mode 100644 index 0000000000..0371bc9323 --- /dev/null +++ b/extensions/fairygui/Relations.cpp @@ -0,0 +1,145 @@ +#include "Relations.h" +#include "GComponent.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +Relations::Relations(GObject* owner) : + handling(nullptr) +{ + _owner = owner; +} + +Relations::~Relations() +{ + clearAll(); +} + +void Relations::add(GObject * target, RelationType relationType) +{ + add(target, relationType, false); +} + +void Relations::add(GObject * target, RelationType relationType, bool usePercent) +{ + CCASSERT(target, "target is null"); + + for (auto it = _items.begin(); it != _items.end(); ++it) + { + if ((*it)->getTarget() == target) + { + (*it)->add(relationType, usePercent); + return; + } + } + RelationItem* newItem = new RelationItem(_owner); + newItem->setTarget(target); + newItem->add(relationType, usePercent); + _items.push_back(newItem); +} + +void Relations::remove(GObject * target, RelationType relationType) +{ + for (auto it = _items.begin(); it != _items.end(); ) + { + if ((*it)->getTarget() == target) + { + (*it)->remove(relationType); + if ((*it)->isEmpty()) + { + delete (*it); + it = _items.erase(it); + } + else + it++; + } + else + it++; + } +} + +bool Relations::contains(GObject * target) +{ + for (auto it = _items.begin(); it != _items.end(); ++it) + { + if ((*it)->getTarget() == target) + return true; + } + + return false; +} + +void Relations::clearFor(GObject * target) +{ + for (auto it = _items.begin(); it != _items.end(); ) + { + if ((*it)->getTarget() == target) + { + delete (*it); + it = _items.erase(it); + } + else + it++; + } +} + +void Relations::clearAll() +{ + for (auto it = _items.begin(); it != _items.end(); ++it) + delete (*it); + _items.clear(); +} + +void Relations::copyFrom(const Relations & source) +{ + clearAll(); + + for (auto it = source._items.begin(); it != source._items.end(); ++it) + { + RelationItem* item = new RelationItem(_owner); + item->copyFrom(**it); + _items.push_back(item); + } +} + +void Relations::onOwnerSizeChanged(float dWidth, float dHeight, bool applyPivot) +{ + for (auto it = _items.begin(); it != _items.end(); ++it) + (*it)->applyOnSelfSizeChanged(dWidth, dHeight, applyPivot); +} + +bool Relations::isEmpty() const +{ + return _items.size() == 0; +} + +void Relations::setup(ByteBuffer * buffer, bool parentToChild) +{ + int cnt = buffer->readByte(); + GObject* target; + for (int i = 0; i < cnt; i++) + { + int targetIndex = buffer->readShort(); + if (targetIndex == -1) + target = _owner->getParent(); + else if (parentToChild) + target = (dynamic_cast(_owner))->getChildAt(targetIndex); + else + target = _owner->getParent()->getChildAt(targetIndex); + + RelationItem* newItem = new RelationItem(_owner); + newItem->setTarget(target); + _items.push_back(newItem); + + int cnt2 = buffer->readByte(); + for (int j = 0; j < cnt2; j++) + { + RelationType rt = (RelationType)buffer->readByte(); + bool usePercent = buffer->readBool(); + newItem->internalAdd(rt, usePercent); + } + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/Relations.h b/extensions/fairygui/Relations.h new file mode 100644 index 0000000000..4c440300a0 --- /dev/null +++ b/extensions/fairygui/Relations.h @@ -0,0 +1,39 @@ +#ifndef __RELATIONS_H__ +#define __RELATIONS_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "RelationItem.h" + +NS_FGUI_BEGIN + +class GObject; +class ByteBuffer; + +class Relations +{ +public: + Relations(GObject* owner); + ~Relations(); + + void add(GObject* target, RelationType relationType); + void add(GObject* target, RelationType relationType, bool usePercent); + void remove(GObject* target, RelationType relationType); + bool contains(GObject* target); + void clearFor(GObject* target); + void clearAll(); + void copyFrom(const Relations& source); + void onOwnerSizeChanged(float dWidth, float dHeight, bool applyPivot); + bool isEmpty() const; + void setup(ByteBuffer* buffer, bool parentToChild); + + GObject* handling; + +private: + GObject* _owner; + std::vector _items; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/ScrollPane.cpp b/extensions/fairygui/ScrollPane.cpp new file mode 100644 index 0000000000..c084dfb1f1 --- /dev/null +++ b/extensions/fairygui/ScrollPane.cpp @@ -0,0 +1,1789 @@ +#include "ScrollPane.h" +#include "GList.h" +#include "GScrollBar.h" +#include "UIConfig.h" +#include "UIPackage.h" +#include "event/InputProcessor.h" +#include "tween/GTween.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +ScrollPane* ScrollPane::_draggingPane = nullptr; +int ScrollPane::_gestureFlag = 0; + +static const float TWEEN_TIME_GO = 0.5f; //tween time for SetPos(ani) +static const float TWEEN_TIME_DEFAULT = 0.3f; //min tween time for inertial scroll +static const float PULL_RATIO = 0.5f; //pull down/up ratio + +static inline float sp_getField(const Vec2& pt, int axis) { return axis == 0 ? pt.x : pt.y; } +static inline float sp_getField(const cocos2d::Size& sz, int axis) { return axis == 0 ? sz.width : sz.height; } +static void sp_setField(Vec2& pt, int axis, float value) +{ + if (axis == 0) + pt.x = value; + else + pt.y = value; +} +static void sp_incField(Vec2& pt, int axis, float value) +{ + if (axis == 0) + pt.x += value; + else + pt.y += value; +} + +static inline float sp_EaseFunc(float t, float d) +{ + t = t / d - 1; + return t * t * t + 1; //cubicOut +} + +ScrollPane::ScrollPane(GComponent* owner) + : _vtScrollBar(nullptr), + _hzScrollBar(nullptr), + _header(nullptr), + _footer(nullptr), + _pageController(nullptr), + _needRefresh(false), + _refreshBarAxis(0), + _aniFlag(0), + _loop(0), + _headerLockedSize(0), + _footerLockedSize(0), + _vScrollNone(false), + _hScrollNone(false), + _tweening(0), + _xPos(0), + _yPos(0), + _floating(false), + _dontClipMargin(false), + _mouseWheelEnabled(true), + _hover(false) +{ + _owner = owner; + + _scrollStep = UIConfig::defaultScrollStep; + _mouseWheelStep = _scrollStep * 2; + _decelerationRate = UIConfig::defaultScrollDecelerationRate; + _touchEffect = UIConfig::defaultScrollTouchEffect; + _bouncebackEffect = UIConfig::defaultScrollBounceEffect; + _pageSize = Vec2::ONE; + + _maskContainer = FUIContainer::create(); + _maskContainer->setCascadeOpacityEnabled(true); + _owner->displayObject()->addChild(_maskContainer); + + _container = (FUIInnerContainer*)_owner->displayObject()->getChildren().at(0); + _container->setPosition2(0, 0); + _container->removeFromParent(); + _maskContainer->addChild(_container, 1); + + _owner->addEventListener(UIEventType::MouseWheel, CC_CALLBACK_1(ScrollPane::onMouseWheel, this)); + _owner->addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(ScrollPane::onTouchBegin, this)); + _owner->addEventListener(UIEventType::TouchMove, CC_CALLBACK_1(ScrollPane::onTouchMove, this)); + _owner->addEventListener(UIEventType::TouchEnd, CC_CALLBACK_1(ScrollPane::onTouchEnd, this)); +} + +ScrollPane::~ScrollPane() +{ + CALL_PER_FRAME_CANCEL(ScrollPane, tweenUpdate); + CALL_LATER_CANCEL(ScrollPane, refresh); + + if (_hzScrollBar) + _hzScrollBar->release(); + if (_vtScrollBar) + _vtScrollBar->release(); + if (_header) + _header->release(); + if (_footer) + _footer->release(); +} + +void ScrollPane::setup(ByteBuffer* buffer) +{ + _scrollType = (ScrollType)buffer->readByte(); + ScrollBarDisplayType scrollBarDisplay = (ScrollBarDisplayType)buffer->readByte(); + int flags = buffer->readInt(); + + if (buffer->readBool()) + { + _scrollBarMargin.top = buffer->readInt(); + _scrollBarMargin.bottom = buffer->readInt(); + _scrollBarMargin.left = buffer->readInt(); + _scrollBarMargin.right = buffer->readInt(); + } + + const std::string& vtScrollBarRes = buffer->readS(); + const std::string& hzScrollBarRes = buffer->readS(); + const std::string& headerRes = buffer->readS(); + const std::string& footerRes = buffer->readS(); + + _displayOnLeft = (flags & 1) != 0; + _snapToItem = (flags & 2) != 0; + _displayInDemand = (flags & 4) != 0; + _pageMode = (flags & 8) != 0; + if ((flags & 16) != 0) + _touchEffect = true; + else if ((flags & 32) != 0) + _touchEffect = false; + if ((flags & 64) != 0) + _bouncebackEffect = true; + else if ((flags & 128) != 0) + _bouncebackEffect = false; + _inertiaDisabled = (flags & 256) != 0; + _maskContainer->setClippingEnabled((flags & 512) == 0); + _floating = (flags & 1024) != 0; + _dontClipMargin = (flags & 2048) != 0; + + if (scrollBarDisplay == ScrollBarDisplayType::DEFAULT) + { +#ifdef CC_PLATFORM_PC + scrollBarDisplay = UIConfig::defaultScrollBarDisplay; +#else + scrollBarDisplay = ScrollBarDisplayType::AUTO; +#endif + } + + if (scrollBarDisplay != ScrollBarDisplayType::HIDDEN) + { + if (_scrollType == ScrollType::BOTH || _scrollType == ScrollType::VERTICAL) + { + const std::string& res = vtScrollBarRes.size() == 0 ? UIConfig::verticalScrollBar : vtScrollBarRes; + if (res.length() > 0) + { + _vtScrollBar = dynamic_cast(UIPackage::createObjectFromURL(res)); + if (_vtScrollBar == nullptr) + CCLOGWARN("FairyGUI: cannot create scrollbar from %s", res.c_str()); + else + { + _vtScrollBar->retain(); + _vtScrollBar->setScrollPane(this, true); + _vtScrollBar->_alignToBL = true; + _owner->displayObject()->addChild(_vtScrollBar->displayObject()); + } + } + } + if (_scrollType == ScrollType::BOTH || _scrollType == ScrollType::HORIZONTAL) + { + const std::string& res = hzScrollBarRes.length() == 0 ? UIConfig::horizontalScrollBar : hzScrollBarRes; + if (res.length() > 0) + { + _hzScrollBar = dynamic_cast(UIPackage::createObjectFromURL(res)); + if (_hzScrollBar == nullptr) + CCLOGWARN("FairyGUI: cannot create scrollbar from %s", res.c_str()); + else + { + _hzScrollBar->retain(); + _hzScrollBar->setScrollPane(this, false); + _hzScrollBar->_alignToBL = true; + _owner->displayObject()->addChild(_hzScrollBar->displayObject()); + } + } + } + + _scrollBarDisplayAuto = scrollBarDisplay == ScrollBarDisplayType::AUTO; + if (_scrollBarDisplayAuto) + { + if (_vtScrollBar != nullptr) + _vtScrollBar->setVisible(false); + if (_hzScrollBar != nullptr) + _hzScrollBar->setVisible(false); + + _owner->addEventListener(UIEventType::RollOver, CC_CALLBACK_1(ScrollPane::onRollOver, this)); + _owner->addEventListener(UIEventType::RollOut, CC_CALLBACK_1(ScrollPane::onRollOut, this)); + } + } + else + _mouseWheelEnabled = false; + + if (headerRes.length() > 0) + { + _header = dynamic_cast(UIPackage::createObjectFromURL(headerRes)); + if (_header == nullptr) + CCLOGWARN("FairyGUI: cannot create scrollPane header from %s", headerRes.c_str()); + else + { + _header->retain(); + _header->setVisible(false); + _header->_alignToBL = true; + _owner->displayObject()->addChild(_header->displayObject()); + } + } + + if (footerRes.length() > 0) + { + _footer = dynamic_cast(UIPackage::createObjectFromURL(footerRes)); + if (_footer == nullptr) + CCLOGWARN("FairyGUI: cannot create scrollPane footer from %s", footerRes.c_str()); + else + { + _footer->retain(); + _footer->setVisible(false); + _footer->_alignToBL = true; + _owner->displayObject()->addChild(_footer->displayObject()); + } + } + + if (_header != nullptr || _footer != nullptr) + _refreshBarAxis = (_scrollType == ScrollType::BOTH || _scrollType == ScrollType::VERTICAL) ? 1 : 0; + + setSize(_owner->getWidth(), _owner->getHeight()); +} + +void ScrollPane::setScrollStep(float value) +{ + _scrollStep = value; + if (_scrollStep == 0) + _scrollStep = UIConfig::defaultScrollStep; + _mouseWheelStep = _scrollStep * 2; +} + +void ScrollPane::setPosX(float value, bool ani) +{ + _owner->ensureBoundsCorrect(); + + if (_loop == 1) + loopCheckingNewPos(value, 0); + + value = clampf(value, 0, _overlapSize.width); + if (value != _xPos) + { + _xPos = value; + posChanged(ani); + } +} + +void ScrollPane::setPosY(float value, bool ani) +{ + _owner->ensureBoundsCorrect(); + + if (_loop == 2) + loopCheckingNewPos(value, 1); + + value = clampf(value, 0, _overlapSize.height); + if (value != _yPos) + { + _yPos = value; + posChanged(ani); + } +} + +float ScrollPane::getPercX() const +{ + return _overlapSize.width == 0 ? 0 : _xPos / _overlapSize.width; +} + +void ScrollPane::setPercX(float value, bool ani) +{ + _owner->ensureBoundsCorrect(); + setPosX(_overlapSize.width * clampf(value, 0, 1), ani); +} + +float ScrollPane::getPercY() const +{ + return _overlapSize.height == 0 ? 0 : _yPos / _overlapSize.height; +} + +void ScrollPane::setPercY(float value, bool ani) +{ + _owner->ensureBoundsCorrect(); + setPosY(_overlapSize.height * clampf(value, 0, 1), ani); +} + +bool ScrollPane::isBottomMost() const +{ + return _yPos == _overlapSize.height || _overlapSize.height == 0; +} + +bool ScrollPane::isRightMost() const +{ + return _xPos == _overlapSize.width || _overlapSize.width == 0; +} + +void ScrollPane::scrollLeft(float ratio, bool ani) +{ + if (_pageMode) + setPosX(_xPos - _pageSize.width * ratio, ani); + else + setPosX(_xPos - _scrollStep * ratio, ani); +} + +void ScrollPane::scrollRight(float ratio, bool ani) +{ + if (_pageMode) + setPosX(_xPos + _pageSize.width * ratio, ani); + else + setPosX(_xPos + _scrollStep * ratio, ani); +} + +void ScrollPane::scrollUp(float ratio, bool ani) +{ + if (_pageMode) + setPosY(_yPos - _pageSize.height * ratio, ani); + else + setPosY(_yPos - _scrollStep * ratio, ani); +} + +void ScrollPane::scrollDown(float ratio, bool ani) +{ + if (_pageMode) + setPosY(_yPos + _pageSize.height * ratio, ani); + else + setPosY(_yPos + _scrollStep * ratio, ani); +} + +void ScrollPane::scrollTop(bool ani) +{ + setPercY(0, ani); +} + +void ScrollPane::scrollBottom(bool ani) +{ + setPercY(1, ani); +} + +void ScrollPane::scrollToView(GObject* obj, bool ani, bool setFirst) +{ + _owner->ensureBoundsCorrect(); + if (_needRefresh) + refresh(); + + Rect rect = Rect(obj->getX(), obj->getY(), obj->getWidth(), obj->getHeight()); + if (obj->getParent() != _owner) + rect = obj->getParent()->transformRect(rect, _owner); + scrollToView(rect, ani, setFirst); +} + +void ScrollPane::scrollToView(const cocos2d::Rect& rect, bool ani, bool setFirst) +{ + _owner->ensureBoundsCorrect(); + if (_needRefresh) + refresh(); + + if (_overlapSize.height > 0) + { + float bottom = _yPos + _viewSize.height; + if (setFirst || rect.origin.y <= _yPos || rect.size.height >= _viewSize.height) + { + if (_pageMode) + setPosY(floor(rect.origin.y / _pageSize.height) * _pageSize.height, ani); + else + setPosY(rect.origin.y, ani); + } + else if (rect.getMaxY() > bottom) + { + if (_pageMode) + setPosY(floor(rect.origin.y / _pageSize.height) * _pageSize.height, ani); + else if (rect.size.height <= _viewSize.height / 2) + setPosY(rect.origin.y + rect.size.height * 2 - _viewSize.height, ani); + else + setPosY(rect.getMaxY() - _viewSize.height, ani); + } + } + if (_overlapSize.width > 0) + { + float right = _xPos + _viewSize.width; + if (setFirst || rect.origin.x <= _xPos || rect.size.width >= _viewSize.width) + { + if (_pageMode) + setPosX(floor(rect.origin.x / _pageSize.width) * _pageSize.width, ani); + setPosX(rect.origin.x, ani); + } + else if (rect.getMaxX() > right) + { + if (_pageMode) + setPosX(floor(rect.origin.x / _pageSize.width) * _pageSize.width, ani); + else if (rect.size.width <= _viewSize.width / 2) + setPosX(rect.origin.x + rect.size.width * 2 - _viewSize.width, ani); + else + setPosX(rect.getMaxX() - _viewSize.width, ani); + } + } + + if (!ani && _needRefresh) + refresh(); +} + +bool ScrollPane::isChildInView(GObject* obj) const +{ + if (_overlapSize.height > 0) + { + float dist = obj->getY() + _container->getPositionY2(); + if (dist <= -obj->getHeight() || dist >= _viewSize.height) + return false; + } + if (_overlapSize.width > 0) + { + float dist = obj->getX() + _container->getPositionX(); + if (dist <= -obj->getWidth() || dist >= _viewSize.width) + return false; + } + + return true; +} + +int ScrollPane::getPageX() const +{ + if (!_pageMode) + return 0; + + int page = floor(_xPos / _pageSize.width); + if (_xPos - page * _pageSize.width > _pageSize.width * 0.5f) + page++; + + return page; +} + +void ScrollPane::setPageX(int value, bool ani) +{ + if (!_pageMode) + return; + + _owner->ensureBoundsCorrect(); + + if (_overlapSize.width > 0) + setPosX(value * _pageSize.width, ani); +} + +int ScrollPane::getPageY() const +{ + if (!_pageMode) + return 0; + + int page = floor(_yPos / _pageSize.height); + if (_yPos - page * _pageSize.height > _pageSize.height * 0.5f) + page++; + + return page; +} + +void ScrollPane::setPageY(int value, bool ani) +{ + if (!_pageMode) + return; + + _owner->ensureBoundsCorrect(); + + if (_overlapSize.height > 0) + setPosY(value * _pageSize.height, ani); +} + +float ScrollPane::getScrollingPosX() const +{ + return clampf(-_container->getPositionX(), 0, _overlapSize.width); +} + +float ScrollPane::getScrollingPosY() const +{ + return clampf(-_container->getPositionY2(), 0, _overlapSize.height); +} + +void ScrollPane::setViewWidth(float value) +{ + value = value + _owner->_margin.left + _owner->_margin.right; + if (_vtScrollBar != nullptr && !_floating) + value += _vtScrollBar->getWidth(); + _owner->setWidth(value); +} + +void ScrollPane::setViewHeight(float value) +{ + value = value + _owner->_margin.top + _owner->_margin.bottom; + if (_hzScrollBar != nullptr && !_floating) + value += _hzScrollBar->getHeight(); + _owner->setHeight(value); +} + +void ScrollPane::lockHeader(int size) +{ + if (_headerLockedSize == size) + return; + + Vec2 cpos = _container->getPosition2(); + + _headerLockedSize = size; + if (!_owner->isDispatchingEvent(UIEventType::PullDownRelease) && sp_getField(cpos, _refreshBarAxis) >= 0) + { + _tweenStart = cpos; + _tweenChange.setZero(); + sp_setField(_tweenChange, _refreshBarAxis, _headerLockedSize - sp_getField(_tweenStart, _refreshBarAxis)); + _tweenDuration.set(TWEEN_TIME_DEFAULT, TWEEN_TIME_DEFAULT); + startTween(2); + + CALL_PER_FRAME(ScrollPane, tweenUpdate); + } +} + +void ScrollPane::lockFooter(int size) +{ + if (_footerLockedSize == size) + return; + + Vec2 cpos = _container->getPosition2(); + + _footerLockedSize = size; + if (!_owner->isDispatchingEvent(UIEventType::PullUpRelease) && sp_getField(cpos, _refreshBarAxis) >= 0) + { + _tweenStart = cpos; + _tweenChange.setZero(); + float max = sp_getField(_overlapSize, _refreshBarAxis); + if (max == 0) + max = MAX(sp_getField(_contentSize, _refreshBarAxis) + _footerLockedSize - sp_getField(_viewSize, _refreshBarAxis), 0); + else + max += _footerLockedSize; + sp_setField(_tweenChange, _refreshBarAxis, -max - sp_getField(_tweenStart, _refreshBarAxis)); + _tweenDuration.set(TWEEN_TIME_DEFAULT, TWEEN_TIME_DEFAULT); + startTween(2); + + CALL_PER_FRAME(ScrollPane, tweenUpdate); + } +} + +void ScrollPane::cancelDragging() +{ + if (_draggingPane == this) + _draggingPane = nullptr; + + _gestureFlag = 0; + _dragged = false; +} + +void ScrollPane::handleControllerChanged(GController* c) +{ + if (_pageController == c) + { + if (_scrollType == ScrollType::HORIZONTAL) + setPageX(c->getSelectedIndex(), true); + else + setPageY(c->getSelectedIndex(), true); + } +} + +void ScrollPane::updatePageController() +{ + if (_pageController != nullptr && !_pageController->changing) + { + int index; + if (_scrollType == ScrollType::HORIZONTAL) + index = getPageX(); + else + index = getPageY(); + if (index < _pageController->getPageCount()) + { + GController* c = _pageController; + _pageController = nullptr; //avoid calling handleControllerChanged + c->setSelectedIndex(index); + _pageController = c; + } + } +} + +void ScrollPane::adjustMaskContainer() +{ + float mx, my; + if (_displayOnLeft && _vtScrollBar != nullptr && !_floating) + mx = floor(_owner->_margin.left + _vtScrollBar->getWidth()); + else + mx = floor(_owner->_margin.left); + my = floor(_owner->_margin.top); + mx += _owner->_alignOffset.x; + my += _owner->_alignOffset.y; + + _maskContainer->setPosition(Vec2(mx, _owner->getHeight() - _viewSize.height - my)); +} + +void ScrollPane::onOwnerSizeChanged() +{ + setSize(_owner->getWidth(), _owner->getHeight()); + posChanged(false); +} + +void ScrollPane::setSize(float wv, float hv) +{ + if (_hzScrollBar != nullptr) + { + _hzScrollBar->setY(hv - _hzScrollBar->getHeight()); + if (_vtScrollBar != nullptr) + { + _hzScrollBar->setWidth(wv - _vtScrollBar->getWidth() - _scrollBarMargin.left - _scrollBarMargin.right); + if (_displayOnLeft) + _hzScrollBar->setX(_scrollBarMargin.left + _vtScrollBar->getWidth()); + else + _hzScrollBar->setX(_scrollBarMargin.left); + } + else + { + _hzScrollBar->setWidth(wv - _scrollBarMargin.left - _scrollBarMargin.right); + _hzScrollBar->setX(_scrollBarMargin.left); + } + } + if (_vtScrollBar != nullptr) + { + if (!_displayOnLeft) + _vtScrollBar->setX(wv - _vtScrollBar->getWidth()); + if (_hzScrollBar != nullptr) + _vtScrollBar->setHeight(hv - _hzScrollBar->getHeight() - _scrollBarMargin.top - _scrollBarMargin.bottom); + else + _vtScrollBar->setHeight(hv - _scrollBarMargin.top - _scrollBarMargin.bottom); + _vtScrollBar->setY(_scrollBarMargin.top); + } + + _viewSize.width = wv; + _viewSize.height = hv; + if (_hzScrollBar != nullptr && !_floating) + _viewSize.height -= _hzScrollBar->getHeight(); + if (_vtScrollBar != nullptr && !_floating) + _viewSize.width -= _vtScrollBar->getWidth(); + _viewSize.width -= (_owner->_margin.left + _owner->_margin.right); + _viewSize.height -= (_owner->_margin.top + _owner->_margin.bottom); + + _viewSize.width = MAX(1, _viewSize.width); + _viewSize.height = MAX(1, _viewSize.height); + _pageSize = _viewSize; + + adjustMaskContainer(); + handleSizeChanged(); +} + +void ScrollPane::setContentSize(float wv, float hv) +{ + if (_contentSize.width == wv && _contentSize.height == hv) + return; + + _contentSize.width = wv; + _contentSize.height = hv; + handleSizeChanged(); +} + +void ScrollPane::changeContentSizeOnScrolling(float deltaWidth, float deltaHeight, float deltaPosX, float deltaPosY) +{ + bool isRightmost = _xPos == _overlapSize.width; + bool isBottom = _yPos == _overlapSize.height; + + _contentSize.width += deltaWidth; + _contentSize.height += deltaHeight; + handleSizeChanged(); + + if (_tweening == 1) + { + if (deltaWidth != 0 && isRightmost && _tweenChange.x < 0) + { + _xPos = _overlapSize.width; + _tweenChange.x = -_xPos - _tweenStart.x; + } + + if (deltaHeight != 0 && isBottom && _tweenChange.y < 0) + { + _yPos = _overlapSize.height; + _tweenChange.y = -_yPos - _tweenStart.y; + } + } + else if (_tweening == 2) + { + if (deltaPosX != 0) + { + _container->setPositionX(_container->getPositionX() - deltaPosX); + _tweenStart.x -= deltaPosX; + _xPos = -_container->getPositionX(); + } + if (deltaPosY != 0) + { + _container->setPositionY2(_container->getPositionY2() - deltaPosY); + _tweenStart.y -= deltaPosY; + _yPos = -_container->getPositionY2(); + } + } + else if (_dragged) + { + if (deltaPosX != 0) + { + _container->setPositionX(_container->getPositionX() - deltaPosX); + _containerPos.x -= deltaPosX; + _xPos = -_container->getPositionX(); + } + if (deltaPosY != 0) + { + _container->setPositionY2(_container->getPositionY2() - deltaPosY); + _containerPos.y -= deltaPosY; + _yPos = -_container->getPositionY2(); + } + } + else + { + if (deltaWidth != 0 && isRightmost) + { + _xPos = _overlapSize.width; + _container->setPositionX(_container->getPositionX() - _xPos); + } + + if (deltaHeight != 0 && isBottom) + { + _yPos = _overlapSize.height; + _container->setPositionY2(_container->getPositionY2() - _yPos); + } + } + + if (_pageMode) + updatePageController(); +} + +void ScrollPane::handleSizeChanged() +{ + if (_displayInDemand) + { + _vScrollNone = _contentSize.height <= _viewSize.height; + _hScrollNone = _contentSize.width <= _viewSize.width; + } + + if (_vtScrollBar != nullptr) + { + if (_contentSize.height == 0) + _vtScrollBar->setDisplayPerc(0); + else + _vtScrollBar->setDisplayPerc(MIN(1, _viewSize.height / _contentSize.height)); + } + if (_hzScrollBar != nullptr) + { + if (_contentSize.width == 0) + _hzScrollBar->setDisplayPerc(0); + else + _hzScrollBar->setDisplayPerc(MIN(1, _viewSize.width / _contentSize.width)); + } + + updateScrollBarVisible(); + + _maskContainer->setContentSize(_viewSize); + Rect maskRect(Vec2(-_owner->_alignOffset.x, _owner->_alignOffset.y), _viewSize); + if (_vScrollNone && _vtScrollBar != nullptr) + maskRect.size.width += _vtScrollBar->getWidth(); + if (_hScrollNone && _hzScrollBar != nullptr) + maskRect.size.height += _hzScrollBar->getHeight(); + if (_dontClipMargin) + { + maskRect.origin.x -= _owner->_margin.left; + maskRect.size.width += _owner->_margin.left + _owner->_margin.right; + maskRect.origin.y -= _owner->_margin.top; + maskRect.size.height += _owner->_margin.top + _owner->_margin.bottom; + } + _maskContainer->setClippingRegion(maskRect); + + if (_vtScrollBar) + _vtScrollBar->handlePositionChanged(); + if (_hzScrollBar) + _hzScrollBar->handlePositionChanged(); + if (_header) + _header->handlePositionChanged(); + if (_footer) + _footer->handlePositionChanged(); + + if (_scrollType == ScrollType::HORIZONTAL || _scrollType == ScrollType::BOTH) + _overlapSize.width = ceil(MAX(0, _contentSize.width - _viewSize.width)); + else + _overlapSize.width = 0; + if (_scrollType == ScrollType::VERTICAL || _scrollType == ScrollType::BOTH) + _overlapSize.height = ceil(MAX(0, _contentSize.height - _viewSize.height)); + else + _overlapSize.height = 0; + + _xPos = clampf(_xPos, 0, _overlapSize.width); + _yPos = clampf(_yPos, 0, _overlapSize.height); + float max = sp_getField(_overlapSize, _refreshBarAxis); + if (max == 0) + max = MAX(sp_getField(_contentSize, _refreshBarAxis) + _footerLockedSize - sp_getField(_viewSize, _refreshBarAxis), 0); + else + max += _footerLockedSize; + if (_refreshBarAxis == 0) + _container->setPosition2(clampf(_container->getPositionX(), -max, _headerLockedSize), + clampf(_container->getPositionY2(), -_overlapSize.height, 0)); + else + _container->setPosition2(clampf(_container->getPositionX(), -_overlapSize.width, 0), + clampf(_container->getPositionY2(), -max, _headerLockedSize)); + + if (_header != nullptr) + { + if (_refreshBarAxis == 0) + _header->setHeight(_viewSize.height); + else + _header->setWidth(_viewSize.width); + } + + if (_footer != nullptr) + { + if (_refreshBarAxis == 0) + _footer->setHeight(_viewSize.height); + else + _footer->setWidth(_viewSize.width); + } + + updateScrollBarPos(); + if (_pageMode) + updatePageController(); +} + +GObject* ScrollPane::hitTest(const cocos2d::Vec2& pt, const cocos2d::Camera* camera) +{ + GObject* target = nullptr; + if (_vtScrollBar) + { + target = _vtScrollBar->hitTest(pt, camera); + if (target) + return target; + } + if (_hzScrollBar) + { + target = _hzScrollBar->hitTest(pt, camera); + if (target) + return target; + } + if (_header && _header->displayObject()->getParent() != nullptr) + { + target = _header->hitTest(pt, camera); + if (target) + return target; + } + if (_footer && _footer->displayObject()->getParent() != nullptr) + { + target = _footer->hitTest(pt, camera); + if (target) + return target; + } + if (_maskContainer->isClippingEnabled()) + { + Vec2 localPoint = _maskContainer->convertToNodeSpace(pt); + if (_maskContainer->getClippingRegion().containsPoint(localPoint)) + return _owner; + else + return nullptr; + } + else + return _owner; +} + +void ScrollPane::posChanged(bool ani) +{ + if (_aniFlag == 0) + _aniFlag = ani ? 1 : -1; + else if (_aniFlag == 1 && !ani) + _aniFlag = -1; + + _needRefresh = true; + CALL_LATER(ScrollPane, refresh); +} + +void ScrollPane::refresh() +{ + CALL_LATER_CANCEL(ScrollPane, refresh); + _needRefresh = false; + + if (_pageMode || _snapToItem) + { + Vec2 pos(-_xPos, -_yPos); + alignPosition(pos, false); + _xPos = -pos.x; + _yPos = -pos.y; + } + + refresh2(); + + _owner->dispatchEvent(UIEventType::Scroll); + if (_needRefresh) //pos may change in onScroll + { + _needRefresh = false; + CALL_LATER_CANCEL(ScrollPane, refresh); + + refresh2(); + } + + updateScrollBarPos(); + _aniFlag = 0; +} + +void ScrollPane::refresh2() +{ + if (_aniFlag == 1 && !_dragged) + { + Vec2 pos; + + if (_overlapSize.width > 0) + pos.x = -(int)_xPos; + else + { + if (_container->getPositionX() != 0) + _container->setPositionX(0); + pos.x = 0; + } + if (_overlapSize.height > 0) + pos.y = -(int)_yPos; + else + { + if (_container->getPositionY2() != 0) + _container->setPositionY2(0); + pos.y = 0; + } + + if (pos.x != _container->getPositionX() || pos.y != _container->getPositionY2()) + { + _tweenDuration.set(TWEEN_TIME_GO, TWEEN_TIME_GO); + _tweenStart = _container->getPosition2(); + _tweenChange = pos - _tweenStart; + startTween(1); + } + else if (_tweening != 0) + killTween(); + } + else + { + if (_tweening != 0) + killTween(); + + _container->setPosition2(Vec2((int)-_xPos, (int)-_yPos)); + + loopCheckingCurrent(); + } + + if (_pageMode) + updatePageController(); +} + +void ScrollPane::updateScrollBarPos() +{ + if (_vtScrollBar != nullptr) + _vtScrollBar->setScrollPerc(_overlapSize.height == 0 ? 0 : clampf(-_container->getPositionY2(), 0, _overlapSize.height) / _overlapSize.height); + + if (_hzScrollBar != nullptr) + _hzScrollBar->setScrollPerc(_overlapSize.width == 0 ? 0 : clampf(-_container->getPositionX(), 0, _overlapSize.width) / _overlapSize.width); + + checkRefreshBar(); +} + +void ScrollPane::updateScrollBarVisible() +{ + if (_vtScrollBar != nullptr) + { + if (_viewSize.height <= _vtScrollBar->getMinSize() || _vScrollNone) + _vtScrollBar->setVisible(false); + else + updateScrollBarVisible2(_vtScrollBar); + } + + if (_hzScrollBar != nullptr) + { + if (_viewSize.width <= _hzScrollBar->getMinSize() || _hScrollNone) + _hzScrollBar->setVisible(false); + else + updateScrollBarVisible2(_hzScrollBar); + } +} + +void ScrollPane::updateScrollBarVisible2(GScrollBar* bar) +{ + if (_scrollBarDisplayAuto) + GTween::kill(bar, TweenPropType::Alpha, false); + + if (_scrollBarDisplayAuto && !_hover && _tweening == 0 && !_dragged && !bar->_gripDragging) + { + if (bar->isVisible()) + GTween::to(1, 0, 0.5f) + ->setDelay(0.5f) + ->onComplete1(CC_CALLBACK_1(ScrollPane::onBarTweenComplete, this)) + ->setTarget(bar, TweenPropType::Alpha); + } + else + { + bar->setAlpha(1); + bar->setVisible(true); + } +} + +void ScrollPane::onBarTweenComplete(GTweener* tweener) +{ + GObject* bar = (GObject*)tweener->getTarget(); + bar->setAlpha(1); + bar->setVisible(false); +} + +float ScrollPane::getLoopPartSize(float division, int axis) +{ + return (sp_getField(_contentSize, axis) + (axis == 0 ? ((GList*)_owner)->getColumnGap() : ((GList*)_owner)->getLineGap())) / division; +} + +bool ScrollPane::loopCheckingCurrent() +{ + bool changed = false; + if (_loop == 1 && _overlapSize.width > 0) + { + if (_xPos < 0.001f) + { + _xPos += getLoopPartSize(2, 0); + changed = true; + } + else if (_xPos >= _overlapSize.width) + { + _xPos -= getLoopPartSize(2, 0); + changed = true; + } + } + else if (_loop == 2 && _overlapSize.height > 0) + { + if (_yPos < 0.001f) + { + _yPos += getLoopPartSize(2, 1); + changed = true; + } + else if (_yPos >= _overlapSize.height) + { + _yPos -= getLoopPartSize(2, 1); + changed = true; + } + } + + if (changed) + _container->setPosition2(Vec2((int)-_xPos, (int)-_yPos)); + + return changed; +} + +void ScrollPane::loopCheckingTarget(cocos2d::Vec2& endPos) +{ + if (_loop == 1) + loopCheckingTarget(endPos, 0); + + if (_loop == 2) + loopCheckingTarget(endPos, 1); +} + +void ScrollPane::loopCheckingTarget(cocos2d::Vec2& endPos, int axis) +{ + if (sp_getField(endPos, axis) > 0) + { + float halfSize = getLoopPartSize(2, axis); + float tmp = sp_getField(_tweenStart, axis) - halfSize; + if (tmp <= 0 && tmp >= -sp_getField(_overlapSize, axis)) + { + sp_incField(endPos, axis, -halfSize); + sp_setField(_tweenStart, axis, tmp); + } + } + else if (sp_getField(endPos, axis) < -sp_getField(_overlapSize, axis)) + { + float halfSize = getLoopPartSize(2, axis); + float tmp = sp_getField(_tweenStart, axis) + halfSize; + if (tmp <= 0 && tmp >= -sp_getField(_overlapSize, axis)) + { + sp_incField(endPos, axis, halfSize); + sp_setField(_tweenStart, axis, tmp); + } + } +} + +void ScrollPane::loopCheckingNewPos(float& value, int axis) +{ + float overlapSize = sp_getField(_overlapSize, axis); + if (overlapSize == 0) + return; + + float pos = axis == 0 ? _xPos : _yPos; + bool changed = false; + if (value < 0.001f) + { + value += getLoopPartSize(2, axis); + if (value > pos) + { + float v = getLoopPartSize(6, axis); + v = ceil((value - pos) / v) * v; + pos = clampf(pos + v, 0, overlapSize); + changed = true; + } + } + else if (value >= overlapSize) + { + value -= getLoopPartSize(2, axis); + if (value < pos) + { + float v = getLoopPartSize(6, axis); + v = ceil((pos - value) / v) * v; + pos = clampf(pos - v, 0, overlapSize); + changed = true; + } + } + + if (changed) + { + if (axis == 0) + _container->setPositionX(-(int)pos); + else + _container->setPositionY2(-(int)pos); + } +} + +void ScrollPane::alignPosition(Vec2& pos, bool inertialScrolling) +{ + if (_pageMode) + { + pos.x = alignByPage(pos.x, 0, inertialScrolling); + pos.y = alignByPage(pos.y, 1, inertialScrolling); + } + else if (_snapToItem) + { + Vec2 tmp = _owner->getSnappingPosition(-pos); + if (pos.x < 0 && pos.x > -_overlapSize.width) + pos.x = -tmp.x; + if (pos.y < 0 && pos.y > -_overlapSize.height) + pos.y = -tmp.y; + } +} + +float ScrollPane::alignByPage(float pos, int axis, bool inertialScrolling) +{ + int page; + float pageSize = sp_getField(_pageSize, axis); + float overlapSize = sp_getField(_overlapSize, axis); + float contentSize = sp_getField(_contentSize, axis); + + if (pos > 0) + page = 0; + else if (pos < -overlapSize) + page = ceil(contentSize / pageSize) - 1; + else + { + page = floor(-pos / pageSize); + float change = inertialScrolling ? (pos - sp_getField(_containerPos, axis)) : (pos - sp_getField(_container->getPosition2(), axis)); + float testPageSize = MIN(pageSize, contentSize - (page + 1) * pageSize); + float delta = -pos - page * pageSize; + + if (std::abs(change) > pageSize) + { + if (delta > testPageSize * 0.5f) + page++; + } + else + { + if (delta > testPageSize * (change < 0 ? 0.3f : 0.7f)) + page++; + } + + pos = -page * pageSize; + if (pos < -overlapSize) + pos = -overlapSize; + } + + if (inertialScrolling) + { + float oldPos = sp_getField(_tweenStart, axis); + int oldPage; + if (oldPos > 0) + oldPage = 0; + else if (oldPos < -overlapSize) + oldPage = ceil(contentSize / pageSize) - 1; + else + oldPage = floor(-oldPos / pageSize); + int startPage = floor(-sp_getField(_containerPos, axis) / pageSize); + if (abs(page - startPage) > 1 && abs(oldPage - startPage) <= 1) + { + if (page > startPage) + page = startPage + 1; + else + page = startPage - 1; + pos = -page * pageSize; + } + } + + return pos; +} + +cocos2d::Vec2 ScrollPane::updateTargetAndDuration(const cocos2d::Vec2& orignPos) +{ + Vec2 ret(0, 0); + ret.x = updateTargetAndDuration(orignPos.x, 0); + ret.y = updateTargetAndDuration(orignPos.y, 1); + return ret; +} + +float ScrollPane::updateTargetAndDuration(float pos, int axis) +{ + float v = sp_getField(_velocity, axis); + float duration = 0; + + if (pos > 0) + pos = 0; + else if (pos < -sp_getField(_overlapSize, axis)) + pos = -sp_getField(_overlapSize, axis); + else + { + float v2 = std::abs(v) * _velocityScale; + float ratio = 0; +#ifdef CC_PLATFORM_PC + if (v2 > 500) + ratio = pow((v2 - 500) / 500, 2); +#else + const cocos2d::Size& winSize = Director::getInstance()->getWinSizeInPixels(); + v2 *= 1136.0f / MAX(winSize.width, winSize.height); + + if (_pageMode) + { + if (v2 > 500) + ratio = pow((v2 - 500) / 500, 2); + } + else + { + if (v2 > 1000) + ratio = pow((v2 - 1000) / 1000, 2); + } +#endif + + if (ratio != 0) + { + if (ratio > 1) + ratio = 1; + + v2 *= ratio; + v *= ratio; + sp_setField(_velocity, axis, v); + + duration = log(60 / v2) / log(_decelerationRate) / 60; + float change = (int)(v * duration * 0.4f); + pos += change; + } +} + + if (duration < TWEEN_TIME_DEFAULT) + duration = TWEEN_TIME_DEFAULT; + sp_setField(_tweenDuration, axis, duration); + + return pos; +} + +void ScrollPane::fixDuration(int axis, float oldChange) +{ + float tweenChange = sp_getField(_tweenChange, axis); + if (tweenChange == 0 || std::abs(tweenChange) >= std::abs(oldChange)) + return; + + float newDuration = std::abs(tweenChange / oldChange) * sp_getField(_tweenDuration, axis); + if (newDuration < TWEEN_TIME_DEFAULT) + newDuration = TWEEN_TIME_DEFAULT; + + sp_setField(_tweenDuration, axis, newDuration); +} + +void ScrollPane::startTween(int type) +{ + _tweenTime.setZero(); + _tweening = type; + CALL_PER_FRAME(ScrollPane, tweenUpdate); + updateScrollBarVisible(); +} + +void ScrollPane::killTween() +{ + if (_tweening == 1) + { + Vec2 t = _tweenStart + _tweenChange; + _container->setPosition2(t); + _owner->dispatchEvent(UIEventType::Scroll); + } + + _tweening = 0; + CALL_PER_FRAME_CANCEL(ScrollPane, tweenUpdate); + _owner->dispatchEvent(UIEventType::ScrollEnd); +} + +void ScrollPane::checkRefreshBar() +{ + if (_header == nullptr && _footer == nullptr) + return; + + float pos = sp_getField(_container->getPosition2(), _refreshBarAxis); + if (_header != nullptr) + { + if (pos > 0) + { + _header->setVisible(true); + Vec2 vec; + + vec = _header->getSize(); + sp_setField(vec, _refreshBarAxis, pos); + _header->setSize(vec.x, vec.y); + } + else + _header->setVisible(false); + } + + if (_footer != nullptr) + { + float max = sp_getField(_overlapSize, _refreshBarAxis); + if (pos < -max || (max == 0 && _footerLockedSize > 0)) + { + _footer->setVisible(true); + + Vec2 vec; + + vec = _footer->getPosition(); + if (max > 0) + sp_setField(vec, _refreshBarAxis, pos + sp_getField(_contentSize, _refreshBarAxis)); + else + sp_setField(vec, _refreshBarAxis, MAX(MIN(pos + sp_getField(_viewSize, _refreshBarAxis), sp_getField(_viewSize, _refreshBarAxis) - _footerLockedSize), sp_getField(_viewSize, _refreshBarAxis) - sp_getField(_contentSize, _refreshBarAxis))); + _footer->setPosition(vec.x, vec.y); + + vec = _footer->getSize(); + if (max > 0) + sp_setField(vec, _refreshBarAxis, -max - pos); + else + sp_setField(vec, _refreshBarAxis, sp_getField(_viewSize, _refreshBarAxis) - sp_getField(_footer->getPosition(), _refreshBarAxis)); + _footer->setSize(vec.x, vec.y); + } + else + _footer->setVisible(false); + } +} + +void ScrollPane::tweenUpdate(float dt) +{ + float nx = runTween(0, dt); + float ny = runTween(1, dt); + + _container->setPosition2(nx, ny); + + if (_tweening == 2) + { + if (_overlapSize.width > 0) + _xPos = clampf(-nx, 0, _overlapSize.width); + if (_overlapSize.height > 0) + _yPos = clampf(-ny, 0, _overlapSize.height); + + if (_pageMode) + updatePageController(); + } + + if (_tweenChange.x == 0 && _tweenChange.y == 0) + { + _tweening = 0; + CALL_PER_FRAME_CANCEL(ScrollPane, tweenUpdate); + + loopCheckingCurrent(); + + updateScrollBarPos(); + updateScrollBarVisible(); + + _owner->dispatchEvent(UIEventType::Scroll); + _owner->dispatchEvent(UIEventType::ScrollEnd); + } + else + { + updateScrollBarPos(); + _owner->dispatchEvent(UIEventType::Scroll); + } +} + +float ScrollPane::runTween(int axis, float dt) +{ + float newValue; + if (sp_getField(_tweenChange, axis) != 0) + { + sp_incField(_tweenTime, axis, dt); + if (sp_getField(_tweenTime, axis) >= sp_getField(_tweenDuration, axis)) + { + newValue = sp_getField(_tweenStart, axis) + sp_getField(_tweenChange, axis); + sp_setField(_tweenChange, axis, 0); + } + else + { + float ratio = sp_EaseFunc(sp_getField(_tweenTime, axis), sp_getField(_tweenDuration, axis)); + newValue = sp_getField(_tweenStart, axis) + (int)(sp_getField(_tweenChange, axis) * ratio); + } + + float threshold1 = 0; + float threshold2 = -sp_getField(_overlapSize, axis); + if (_headerLockedSize > 0 && _refreshBarAxis == axis) + threshold1 = _headerLockedSize; + if (_footerLockedSize > 0 && _refreshBarAxis == axis) + { + float max = sp_getField(_overlapSize, _refreshBarAxis); + if (max == 0) + max = MAX(sp_getField(_contentSize, _refreshBarAxis) + _footerLockedSize - sp_getField(_viewSize, _refreshBarAxis), 0); + else + max += _footerLockedSize; + threshold2 = -max; + } + + if (_tweening == 2 && _bouncebackEffect) + { + if ((newValue > 20 + threshold1 && sp_getField(_tweenChange, axis) > 0) || (newValue > threshold1 && sp_getField(_tweenChange, axis) == 0)) + { + sp_setField(_tweenTime, axis, 0); + sp_setField(_tweenDuration, axis, TWEEN_TIME_DEFAULT); + sp_setField(_tweenChange, axis, -newValue + threshold1); + sp_setField(_tweenStart, axis, newValue); + } + else if ((newValue < threshold2 - 20 && sp_getField(_tweenChange, axis) < 0) || (newValue < threshold2 && sp_getField(_tweenChange, axis) == 0)) + { + sp_setField(_tweenTime, axis, 0); + sp_setField(_tweenDuration, axis, TWEEN_TIME_DEFAULT); + sp_setField(_tweenChange, axis, threshold2 - newValue); + sp_setField(_tweenStart, axis, newValue); + } + } + else + { + if (newValue > threshold1) + { + newValue = threshold1; + sp_setField(_tweenChange, axis, 0); + } + else if (newValue < threshold2) + { + newValue = threshold2; + sp_setField(_tweenChange, axis, 0); + } + } + } + else + newValue = sp_getField(_container->getPosition2(), axis); + + return newValue; +} + +void ScrollPane::onTouchBegin(EventContext* context) +{ + if (!_touchEffect) + return; + + context->captureTouch(); + InputEvent* evt = context->getInput(); + Vec2 pt = _owner->globalToLocal(evt->getPosition()); + + if (_tweening != 0) + { + killTween(); + evt->getProcessor()->cancelClick(evt->getTouchId()); + + _dragged = true; + } + else + _dragged = false; + + _containerPos = _container->getPosition2(); + _beginTouchPos = _lastTouchPos = pt; + _lastTouchGlobalPos = evt->getPosition(); + _isHoldAreaDone = false; + _velocity.setZero(); + _velocityScale = 1; + _lastMoveTime = clock(); +} + +void ScrollPane::onTouchMove(EventContext* context) +{ + if (!_touchEffect) + return; + + if ((_draggingPane != nullptr && _draggingPane != this) || GObject::getDraggingObject() != nullptr) + return; + + InputEvent* evt = context->getInput(); + Vec2 pt = _owner->globalToLocal(evt->getPosition()); + + int sensitivity; +#ifdef CC_PLATFORM_PC + sensitivity = 8; +#else + sensitivity = UIConfig::touchScrollSensitivity; +#endif + + float diff; + bool sv = false, sh = false; + + if (_scrollType == ScrollType::VERTICAL) + { + if (!_isHoldAreaDone) + { + _gestureFlag |= 1; + + diff = std::abs(_beginTouchPos.y - pt.y); + if (diff < sensitivity) + return; + + if ((_gestureFlag & 2) != 0) + { + float diff2 = std::abs(_beginTouchPos.x - pt.x); + if (diff < diff2) + return; + } + } + + sv = true; + } + else if (_scrollType == ScrollType::HORIZONTAL) + { + if (!_isHoldAreaDone) + { + _gestureFlag |= 2; + + diff = std::abs(_beginTouchPos.x - pt.x); + if (diff < sensitivity) + return; + + if ((_gestureFlag & 1) != 0) + { + float diff2 = std::abs(_beginTouchPos.y - pt.y); + if (diff < diff2) + return; + } + } + + sh = true; + } + else + { + _gestureFlag = 3; + + if (!_isHoldAreaDone) + { + diff = std::abs(_beginTouchPos.y - pt.y); + if (diff < sensitivity) + { + diff = std::abs(_beginTouchPos.x - pt.x); + if (diff < sensitivity) + return; + } + } + + sv = sh = true; + } + + Vec2 newPos = _containerPos + pt - _beginTouchPos; + newPos.x = (int)newPos.x; + newPos.y = (int)newPos.y; + + if (sv) + { + if (newPos.y > 0) + { + if (!_bouncebackEffect) + _container->setPositionY2(0); + else if (_header != nullptr && _header->maxSize.height != 0) + _container->setPositionY2(((int)MIN(newPos.y * 0.5f, _header->maxSize.height))); + else + _container->setPositionY2(((int)MIN(newPos.y * 0.5f, _viewSize.height * PULL_RATIO))); + } + else if (newPos.y < -_overlapSize.height) + { + if (!_bouncebackEffect) + _container->setPositionY2(-_overlapSize.height); + else if (_footer != nullptr && _footer->maxSize.height > 0) + _container->setPositionY2(((int)MAX((newPos.y + _overlapSize.height) * 0.5f, -_footer->maxSize.height) - _overlapSize.height)); + else + _container->setPositionY2(((int)MAX((newPos.y + _overlapSize.height) * 0.5f, -_viewSize.height * PULL_RATIO) - _overlapSize.height)); + } + else + _container->setPositionY2(newPos.y); + } + + if (sh) + { + if (newPos.x > 0) + { + if (!_bouncebackEffect) + _container->setPositionX(0); + else if (_header != nullptr && _header->maxSize.width != 0) + _container->setPositionX((int)MIN(newPos.x * 0.5f, _header->maxSize.width)); + else + _container->setPositionX((int)MIN(newPos.x * 0.5f, _viewSize.width * PULL_RATIO)); + } + else if (newPos.x < 0 - _overlapSize.width) + { + if (!_bouncebackEffect) + _container->setPositionX(-_overlapSize.width); + else if (_footer != nullptr && _footer->maxSize.width > 0) + _container->setPositionX((int)MAX((newPos.x + _overlapSize.width) * 0.5f, -_footer->maxSize.width) - _overlapSize.width); + else + _container->setPositionX((int)MAX((newPos.x + _overlapSize.width) * 0.5f, -_viewSize.width * PULL_RATIO) - _overlapSize.width); + } + else + _container->setPositionX(newPos.x); + } + + auto deltaTime = Director::getInstance()->getDeltaTime(); + float elapsed = (clock() - _lastMoveTime) / (double)CLOCKS_PER_SEC; + elapsed = elapsed * 60 - 1; + if (elapsed > 1) + _velocity = _velocity * pow(0.833f, elapsed); + Vec2 deltaPosition = pt - _lastTouchPos; + if (!sh) + deltaPosition.x = 0; + if (!sv) + deltaPosition.y = 0; + _velocity = _velocity.lerp(deltaPosition / deltaTime, deltaTime * 10); + + Vec2 deltaGlobalPosition = _lastTouchGlobalPos - evt->getPosition(); + if (deltaPosition.x != 0) + _velocityScale = std::abs(deltaGlobalPosition.x / deltaPosition.x); + else if (deltaPosition.y != 0) + _velocityScale = std::abs(deltaGlobalPosition.y / deltaPosition.y); + + _lastTouchPos = pt; + _lastTouchGlobalPos = evt->getPosition(); + _lastMoveTime = clock(); + + if (_overlapSize.width > 0) + _xPos = clampf(-_container->getPositionX(), 0, _overlapSize.width); + if (_overlapSize.height > 0) + _yPos = clampf(-_container->getPositionY2(), 0, _overlapSize.height); + + if (_loop != 0) + { + newPos = _container->getPosition2(); + if (loopCheckingCurrent()) + _containerPos += _container->getPosition2() - newPos; + } + + _draggingPane = this; + _isHoldAreaDone = true; + _dragged = true; + + updateScrollBarPos(); + updateScrollBarVisible(); + if (_pageMode) + updatePageController(); + _owner->dispatchEvent(UIEventType::Scroll); +} + +void ScrollPane::onTouchEnd(EventContext* context) +{ + if (_draggingPane == this) + _draggingPane = nullptr; + + _gestureFlag = 0; + + if (!_dragged || !_touchEffect) + { + _dragged = false; + return; + } + + _dragged = false; + _tweenStart = _container->getPosition2(); + + Vec2 endPos = _tweenStart; + bool flag = false; + if (_container->getPositionX() > 0) + { + endPos.x = 0; + flag = true; + } + else if (_container->getPositionX() < -_overlapSize.width) + { + endPos.x = -_overlapSize.width; + flag = true; + } + if (_container->getPositionY2() > 0) + { + endPos.y = 0; + flag = true; + } + else if (_container->getPositionY2() < -_overlapSize.height) + { + endPos.y = -_overlapSize.height; + flag = true; + } + + if (flag) + { + _tweenChange = endPos - _tweenStart; + if (_tweenChange.x < -UIConfig::touchDragSensitivity || _tweenChange.y < -UIConfig::touchDragSensitivity) + _owner->dispatchEvent(UIEventType::PullDownRelease); + else if (_tweenChange.x > UIConfig::touchDragSensitivity || _tweenChange.y > UIConfig::touchDragSensitivity) + _owner->dispatchEvent(UIEventType::PullUpRelease); + + if (_headerLockedSize > 0 && sp_getField(endPos, _refreshBarAxis) == 0) + { + sp_setField(endPos, _refreshBarAxis, _headerLockedSize); + _tweenChange = endPos - _tweenStart; + } + else if (_footerLockedSize > 0 && sp_getField(endPos, _refreshBarAxis) == -sp_getField(_overlapSize, _refreshBarAxis)) + { + float max = sp_getField(_overlapSize, _refreshBarAxis); + if (max == 0) + max = MAX(sp_getField(_contentSize, _refreshBarAxis) + _footerLockedSize - sp_getField(_viewSize, _refreshBarAxis), 0); + else + max += _footerLockedSize; + sp_setField(endPos, _refreshBarAxis, -max); + _tweenChange = endPos - _tweenStart; + } + + _tweenDuration.set(TWEEN_TIME_DEFAULT, TWEEN_TIME_DEFAULT); + } + else + { + if (!_inertiaDisabled) + { + float elapsed = (clock() - _lastMoveTime) / (double)CLOCKS_PER_SEC; + elapsed = elapsed * 60 - 1; + if (elapsed > 1) + _velocity = _velocity * pow(0.833f, elapsed); + + endPos = updateTargetAndDuration(_tweenStart); + } + else + _tweenDuration.set(TWEEN_TIME_DEFAULT, TWEEN_TIME_DEFAULT); + Vec2 oldChange = endPos - _tweenStart; + + loopCheckingTarget(endPos); + if (_pageMode || _snapToItem) + alignPosition(endPos, true); + + _tweenChange = endPos - _tweenStart; + if (_tweenChange.x == 0 && _tweenChange.y == 0) + { + updateScrollBarVisible(); + return; + } + + if (_pageMode || _snapToItem) + { + fixDuration(0, oldChange.x); + fixDuration(1, oldChange.y); + } + } + + startTween(2); +} + +void ScrollPane::onMouseWheel(EventContext* context) +{ + if (!_mouseWheelEnabled) + return; + + InputEvent* evt = context->getInput(); + int delta = evt->getMouseWheelDelta(); + delta = delta > 0 ? 1 : -1; + if (_overlapSize.width > 0 && _overlapSize.height == 0) + { + if (_pageMode) + setPosX(_xPos + _pageSize.width * delta, false); + else + setPosX(_xPos + _mouseWheelStep * delta, false); + } + else + { + if (_pageMode) + setPosY(_yPos + _pageSize.height * delta, false); + else + setPosY(_yPos + _mouseWheelStep * delta, false); + } +} + +void ScrollPane::onRollOver(EventContext* context) +{ + _hover = true; + updateScrollBarVisible(); +} + +void ScrollPane::onRollOut(EventContext* context) +{ + _hover = false; + updateScrollBarVisible(); +} + +NS_FGUI_END diff --git a/extensions/fairygui/ScrollPane.h b/extensions/fairygui/ScrollPane.h new file mode 100644 index 0000000000..3830962877 --- /dev/null +++ b/extensions/fairygui/ScrollPane.h @@ -0,0 +1,219 @@ +#ifndef __SCROLLPANE_H__ +#define __SCROLLPANE_H__ + +#include "FairyGUIMacros.h" +#include "Margin.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; +class GComponent; +class GScrollBar; +class FUIContainer; +class FUIInnerContainer; +class GController; +class EventContext; +class ByteBuffer; +class GTweener; + +class ScrollPane : public cocos2d::Ref +{ +public: + ScrollPane(GComponent* owner); + virtual ~ScrollPane(); + + void setup(ByteBuffer* buffer); + + GComponent* getOwner() const { return _owner; } + GComponent* getHeader() const { return _header; } + GComponent* getFooter() const { return _footer; } + GScrollBar* getVtScrollBar() const { return _vtScrollBar; } + GScrollBar* getHzScrollBar() const { return _hzScrollBar; } + + bool isBouncebackEffect() const { return _bouncebackEffect; } + void setBouncebackEffect(bool value) { _bouncebackEffect = value; } + + bool isTouchEffect() const { return _touchEffect; } + void setTouchEffect(bool value) { _touchEffect = value; } + + bool isInertiaDisabled() const { return _inertiaDisabled; } + void setInertiaDisabled(bool value) { _inertiaDisabled = value; } + + float getScrollStep() const { return _scrollStep; } + void setScrollStep(float value); + + bool isSnapToItem() const { return _snapToItem; } + void setSnapToItem(bool value) { _snapToItem = value; } + + bool isPageMode() const { return _pageMode; } + void setPageMode(bool value) { _pageMode = value; } + + GController* getPageController() const { return _pageController; } + void setPageController(GController* value) { _pageController = value; } + + bool isMouseWheelEnabled() const { return _mouseWheelEnabled; } + void setMouseWheelEnabled(bool value) { _mouseWheelEnabled = value; } + + float getDecelerationRate() const { return _decelerationRate; } + void setDecelerationRate(float value) { _decelerationRate = value; } + + float getPosX() const { return _xPos; } + void setPosX(float value, bool ani = false); + float getPosY() const { return _yPos; } + void setPosY(float value, bool ani = false); + + float getPercX() const; + void setPercX(float value, bool ani = false); + float getPercY() const; + void setPercY(float value, bool ani = false); + + bool isBottomMost() const; + bool isRightMost() const; + + void scrollLeft(float ratio = 1, bool ani = false); + void scrollRight(float ratio = 1, bool ani = false); + void scrollUp(float ratio = 1, bool ani = false); + void scrollDown(float ratio = 1, bool ani = false); + void scrollTop(bool ani = false); + void scrollBottom(bool ani = false); + void scrollToView(GObject* obj, bool ani = false, bool setFirst = false); + void scrollToView(const cocos2d::Rect& rect, bool ani = false, bool setFirst = false); + bool isChildInView(GObject* obj) const; + + int getPageX() const; + void setPageX(int value, bool ani = false); + int getPageY() const; + void setPageY(int value, bool ani = false); + + float getScrollingPosX() const; + float getScrollingPosY() const; + + const cocos2d::Size& getContentSize() const { return _contentSize; } + const cocos2d::Size& getViewSize() const { return _viewSize; } + + void lockHeader(int size); + void lockFooter(int size); + + void cancelDragging(); + static ScrollPane* getDraggingPane() { return _draggingPane; } + +private: + void onOwnerSizeChanged(); + void adjustMaskContainer(); + void setContentSize(float wv, float hv); + void changeContentSizeOnScrolling(float deltaWidth, float deltaHeight, float deltaPosX, float deltaPosY); + void setViewWidth(float value); + void setViewHeight(float value); + void setSize(float wv, float hv); + void handleSizeChanged(); + + void handleControllerChanged(GController* c); + void updatePageController(); + + GObject* hitTest(const cocos2d::Vec2& pt, const cocos2d::Camera* camera); + + void posChanged(bool ani); + CALL_LATER_FUNC(ScrollPane, refresh); + void refresh2(); + + void updateScrollBarPos(); + void updateScrollBarVisible(); + void updateScrollBarVisible2(GScrollBar* bar); + + float getLoopPartSize(float division, int axis); + bool loopCheckingCurrent(); + void loopCheckingTarget(cocos2d::Vec2& endPos); + void loopCheckingTarget(cocos2d::Vec2& endPos, int axis); + void loopCheckingNewPos(float& value, int axis); + void alignPosition(cocos2d::Vec2& pos, bool inertialScrolling); + float alignByPage(float pos, int axis, bool inertialScrolling); + cocos2d::Vec2 updateTargetAndDuration(const cocos2d::Vec2& orignPos); + float updateTargetAndDuration(float pos, int axis); + void fixDuration(int axis, float oldChange); + void startTween(int type); + void killTween(); + void tweenUpdate(float dt); + float runTween(int axis, float dt); + + void checkRefreshBar(); + + void onTouchBegin(EventContext* context); + void onTouchMove(EventContext* context); + void onTouchEnd(EventContext* context); + void onMouseWheel(EventContext* context); + void onRollOver(EventContext* context); + void onRollOut(EventContext* context); + void onBarTweenComplete(GTweener* tweener); + + ScrollType _scrollType; + float _scrollStep; + float _mouseWheelStep; + Margin _scrollBarMargin; + bool _bouncebackEffect; + bool _touchEffect; + bool _scrollBarDisplayAuto; + bool _vScrollNone; + bool _hScrollNone; + bool _needRefresh; + int _refreshBarAxis; + bool _displayOnLeft; + bool _snapToItem; + bool _displayInDemand; + bool _mouseWheelEnabled; + bool _inertiaDisabled; + float _decelerationRate; + bool _pageMode; + bool _floating; + bool _dontClipMargin; + + float _xPos; + float _yPos; + + cocos2d::Size _viewSize; + cocos2d::Size _contentSize; + cocos2d::Size _overlapSize; + cocos2d::Size _pageSize; + + cocos2d::Vec2 _containerPos; + cocos2d::Vec2 _beginTouchPos; + cocos2d::Vec2 _lastTouchPos; + cocos2d::Vec2 _lastTouchGlobalPos; + cocos2d::Vec2 _velocity; + float _velocityScale; + clock_t _lastMoveTime; + bool _dragged; + bool _isHoldAreaDone; + int _aniFlag; + int _loop; + bool _hover; + + int _headerLockedSize; + int _footerLockedSize; + + int _tweening; + cocos2d::Vec2 _tweenStart; + cocos2d::Vec2 _tweenChange; + cocos2d::Vec2 _tweenTime; + cocos2d::Vec2 _tweenDuration; + + GComponent* _owner; + FUIContainer* _maskContainer; + FUIInnerContainer* _container; + GScrollBar* _hzScrollBar; + GScrollBar* _vtScrollBar; + GComponent* _header; + GComponent* _footer; + GController* _pageController; + + static int _gestureFlag; + static ScrollPane* _draggingPane; + + friend class GComponent; + friend class GList; + friend class GScrollBar; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/Transition.cpp b/extensions/fairygui/Transition.cpp new file mode 100644 index 0000000000..f663e79a65 --- /dev/null +++ b/extensions/fairygui/Transition.cpp @@ -0,0 +1,1545 @@ +#include "Transition.h" +#include "GComponent.h" +#include "GRoot.h" +#include "tween/GPath.h" +#include "tween/GTween.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; +using namespace std; + +const int OPTION_IGNORE_DISPLAY_CONTROLLER = 1; +const int OPTION_AUTO_STOP_DISABLED = 2; +const int OPTION_AUTO_STOP_AT_END = 4; + +class TValueBase +{ +}; + +class TValue_Visible : public TValueBase +{ +public: + bool visible; +}; + +class TValue_Animation : public TValueBase +{ +public: + int frame; + bool playing; + bool flag; +}; + +class TValue_Sound : public TValueBase +{ +public: + std::string sound; + float volume; +}; + +class TValue_Transition : public TValueBase +{ +public: + std::string transName; + int playTimes; + Transition* trans; + float stopTime; +}; + +class TValue_Shake : public TValueBase +{ +public: + float amplitude; + float duration; + cocos2d::Vec2 lastOffset; + cocos2d::Vec2 offset; +}; + +class TValue_Text : public TValueBase +{ +public: + std::string text; +}; + +class TValue : public TValueBase +{ +public: + float f1; + float f2; + float f3; + float f4; + bool b1; + bool b2; + bool b3; + + TValue(); + cocos2d::Vec2 getVec2() const; + void setVec2(const cocos2d::Vec2& value); + cocos2d::Vec4 getVec4() const; + void setVec4(const cocos2d::Vec4& value); + cocos2d::Color4B getColor() const; + void setColor(const cocos2d::Color4B& value); +}; + +TValue::TValue() +{ + f1 = f2 = f3 = f4 = 0; + b1 = b2 = true; + b3 = false; +} + +cocos2d::Vec2 TValue::getVec2() const +{ + return cocos2d::Vec2(f1, f2); +} + +void TValue::setVec2(const cocos2d::Vec2& value) +{ + f1 = value.x; + f2 = value.y; +} + +cocos2d::Vec4 TValue::getVec4() const +{ + return cocos2d::Vec4(f1, f2, f3, f4); +} + +void TValue::setVec4(const cocos2d::Vec4& value) +{ + f1 = value.x; + f2 = value.y; + f3 = value.z; + f4 = value.w; +} + +cocos2d::Color4B TValue::getColor() const +{ + return cocos2d::Color4B(f1, f2, f3, f4); +} + +void TValue::setColor(const cocos2d::Color4B& value) +{ + f1 = value.r; + f2 = value.g; + f3 = value.b; + f4 = value.a; +} + +class TweenConfig +{ +public: + float duration; + EaseType easeType; + int repeat; + bool yoyo; + + TValue* startValue; + TValue* endValue; + GPath* path; + + string endLabel; + Transition::TransitionHook endHook; + + TweenConfig(); + ~TweenConfig(); +}; + +TweenConfig::TweenConfig() + : startValue(nullptr), + endValue(nullptr), + path(nullptr) +{ + easeType = EaseType::QuadOut; + startValue = new TValue(); + endValue = new TValue(); +} + +TweenConfig::~TweenConfig() +{ + CC_SAFE_DELETE(path); + CC_SAFE_DELETE(startValue); + CC_SAFE_DELETE(endValue); +} + +class TransitionItem +{ +public: + float time; + string targetId; + TransitionActionType type; + TweenConfig* tweenConfig; + string label; + TValueBase* value; + Transition::TransitionHook hook; + + //running properties + GTweener* tweener; + GObject* target; + uint32_t displayLockToken; + + TransitionItem(TransitionActionType aType); + ~TransitionItem(); +}; + +TransitionItem::TransitionItem(TransitionActionType aType) + : time(0), + type(aType), + hook(nullptr), + tweenConfig(nullptr), + tweener(nullptr), + target(nullptr), + displayLockToken(0) +{ + switch (type) + { + case TransitionActionType::XY: + case TransitionActionType::Size: + case TransitionActionType::Scale: + case TransitionActionType::Pivot: + case TransitionActionType::Skew: + case TransitionActionType::Alpha: + case TransitionActionType::Rotation: + case TransitionActionType::Color: + case TransitionActionType::ColorFilter: + value = new TValue(); + break; + + case TransitionActionType::Animation: + value = new TValue_Animation(); + break; + + case TransitionActionType::Shake: + value = new TValue_Shake(); + break; + + case TransitionActionType::Sound: + value = new TValue_Sound(); + break; + + case TransitionActionType::Transition: + value = new TValue_Transition(); + break; + + case TransitionActionType::Visible: + value = new TValue_Visible(); + break; + + case TransitionActionType::Text: + case TransitionActionType::Icon: + value = new TValue_Text(); + break; + + default: + value = nullptr; + break; + } +} + +TransitionItem::~TransitionItem() +{ + if (tweener != nullptr) + { + tweener->kill(); + tweener = nullptr; + } + + target = nullptr; + hook = nullptr; + if (tweenConfig != nullptr) + { + tweenConfig->endHook = nullptr; + delete tweenConfig; + } + + CC_SAFE_DELETE(value); +} + +Transition::Transition(GComponent* owner) + : _owner(owner), + _totalTimes(0), + _totalTasks(0), + _playing(false), + _paused(false), + _ownerBaseX(0), + _ownerBaseY(0), + _onComplete(nullptr), + _options(0), + _reversed(false), + _totalDuration(0), + _autoPlay(false), + _autoPlayDelay(0), + _timeScale(1), + _startTime(0), + _endTime(0) +{ +} + +Transition::~Transition() +{ + if (_playing) + GTween::kill(this); //delay start + + for (auto& item : _items) + delete item; + + _playing = false; + _onComplete = nullptr; +} + +void Transition::play(PlayCompleteCallback callback) +{ + play(1, 0, 0, -1, callback, false); +} + +void Transition::play(int times, float delay, PlayCompleteCallback callback) +{ + play(times, delay, 0, -1, callback, false); +} + +void Transition::play(int times, float delay, float startTime, float endTime, PlayCompleteCallback callback) +{ + play(times, delay, startTime, endTime, callback, false); +} + +void Transition::playReverse(PlayCompleteCallback callback) +{ + play(1, 0, 0, -1, callback, true); +} + +void Transition::playReverse(int times, float delay, PlayCompleteCallback callback) +{ + play(times, delay, 0, -1, callback, true); +} + +void Transition::play(int times, float delay, float startTime, float endTime, PlayCompleteCallback onComplete, bool reverse) +{ + stop(true, true); + + _totalTimes = times; + _reversed = reverse; + _startTime = startTime; + _endTime = endTime; + _playing = true; + _paused = false; + _onComplete = onComplete; + + int cnt = (int)_items.size(); + for (int i = 0; i < cnt; i++) + { + TransitionItem* item = _items[i]; + if (item->target == nullptr) + { + if (!item->targetId.empty()) + item->target = _owner->getChildById(item->targetId); + else + item->target = _owner; + } + else if (item->target != _owner && item->target->getParent() != _owner) //maybe removed + item->target = nullptr; + + if (item->target != nullptr && item->type == TransitionActionType::Transition) + { + TValue_Transition* value = (TValue_Transition*)item->value; + Transition* trans = dynamic_cast(item->target)->getTransition(value->transName); + if (trans == this) + trans = nullptr; + if (trans != nullptr) + { + if (value->playTimes == 0) //stop + { + int j; + for (j = i - 1; j >= 0; j--) + { + TransitionItem* item2 = _items[j]; + if (item2->type == TransitionActionType::Transition) + { + TValue_Transition* value2 = (TValue_Transition*)item2->value; + if (value2->trans == trans) + { + value2->stopTime = item->time - item2->time; + break; + } + } + } + if (j < 0) + value->stopTime = 0; + else + trans = nullptr; //no need to handle stop anymore + } + else + value->stopTime = -1; + } + value->trans = trans; + } + } + + if (delay == 0) + onDelayedPlay(); + else + GTween::delayedCall(delay)->setTarget(this)->onComplete(CC_CALLBACK_0(Transition::onDelayedPlay, this)); +} + +void Transition::changePlayTimes(int value) +{ + _totalTimes = value; +} + +void Transition::setAutoPlay(bool autoPlay, int times, float delay) +{ + if (_autoPlay != autoPlay) + { + _autoPlay = autoPlay; + _autoPlayTimes = times; + _autoPlayDelay = delay; + if (_autoPlay) + { + if (_owner->onStage()) + play(_autoPlayTimes, _autoPlayDelay, nullptr); + } + else + { + if (!_owner->onStage()) + stop(false, true); + } + } +} + +void Transition::stop() +{ + stop(true, false); +} + +void Transition::stop(bool setToComplete, bool processCallback) +{ + if (!_playing) + return; + + _playing = false; + _totalTasks = 0; + _totalTimes = 0; + PlayCompleteCallback func = _onComplete; + _onComplete = nullptr; + + int cnt = (int)_items.size(); + if (_reversed) + { + for (int i = cnt - 1; i >= 0; i--) + { + TransitionItem* item = _items[i]; + if (item->target == nullptr) + continue; + + stopItem(item, setToComplete); + } + } + else + { + for (int i = 0; i < cnt; i++) + { + TransitionItem* item = _items[i]; + if (item->target == nullptr) + continue; + + stopItem(item, setToComplete); + } + } + if (processCallback && func != nullptr) + func(); +} + +void Transition::stopItem(TransitionItem* item, bool setToComplete) +{ + if (item->displayLockToken != 0) + { + item->target->releaseDisplayLock(item->displayLockToken); + item->displayLockToken = 0; + } + + if (item->tweener != nullptr) + { + item->tweener->kill(setToComplete); + item->tweener = nullptr; + + if (item->type == TransitionActionType::Shake && !setToComplete) + { + item->target->_gearLocked = true; + item->target->setPosition(item->target->getX() - ((TValue_Shake*)item->value)->lastOffset.x, item->target->getY() - ((TValue_Shake*)item->value)->lastOffset.y); + item->target->_gearLocked = false; + } + } +} + +void Transition::setPaused(bool paused) +{ + if (!_playing || _paused == paused) + return; + + _paused = paused; + GTweener* tweener = GTween::getTween(this); + if (tweener != nullptr) + tweener->setPaused(paused); + + for (auto& item : _items) + { + if (item->target == nullptr) + continue; + + if (item->type == TransitionActionType::Transition) + { + if (((TValue_Transition*)item->value)->trans != nullptr) + ((TValue_Transition*)item->value)->trans->setPaused(paused); + } + else if (item->type == TransitionActionType::Animation) + { + if (paused) + { + ((TValue_Animation*)item->value)->flag = item->target->getProp(ObjectPropID::Playing).asBool(); + item->target->setProp(ObjectPropID::Playing, Value(false)); + } + else + item->target->setProp(ObjectPropID::Playing, Value(((TValue_Animation*)item->value)->flag)); + } + + if (item->tweener != nullptr) + item->tweener->setPaused(paused); + } +} + +void Transition::setValue(const std::string& label, const ValueVector& values) +{ + for (auto& item : _items) + { + void* value; + if (item->label == label) + { + if (item->tweenConfig != nullptr) + value = item->tweenConfig->startValue; + else + value = item->value; + } + else if (item->tweenConfig != nullptr && item->tweenConfig->endLabel == label) + { + value = item->tweenConfig->endValue; + } + else + continue; + + switch (item->type) + { + case TransitionActionType::XY: + case TransitionActionType::Size: + case TransitionActionType::Pivot: + case TransitionActionType::Scale: + case TransitionActionType::Skew: + { + TValue* tvalue = (TValue*)value; + tvalue->b1 = true; + tvalue->b2 = true; + tvalue->f1 = values[0].asFloat(); + tvalue->f2 = values[1].asFloat(); + break; + } + + case TransitionActionType::Alpha: + case TransitionActionType::Rotation: + ((TValue*)value)->f1 = values[0].asFloat(); + ; + break; + + case TransitionActionType::Color: + { + uint32_t v = values[0].asUnsignedInt(); + ((TValue*)value)->setColor(Color4B((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF, (v >> 24) & 0xFF)); + break; + } + + case TransitionActionType::Animation: + { + TValue_Animation* tvalue = (TValue_Animation*)value; + tvalue->frame = values[0].asInt(); + if (values.size() > 1) + tvalue->playing = values[1].asBool(); + break; + } + + case TransitionActionType::Visible: + ((TValue_Visible*)value)->visible = values[0].asBool(); + break; + + case TransitionActionType::Sound: + { + TValue_Sound* tvalue = (TValue_Sound*)value; + tvalue->sound = values[0].asString(); + if (values.size() > 1) + tvalue->volume = values[1].asFloat(); + break; + } + + case TransitionActionType::Transition: + { + TValue_Transition* tvalue = (TValue_Transition*)value; + tvalue->transName = values[0].asString(); + if (values.size() > 1) + tvalue->playTimes = values[1].asInt(); + break; + } + + case TransitionActionType::Shake: + { + ((TValue_Shake*)value)->amplitude = values[0].asFloat(); + if (values.size() > 1) + ((TValue_Shake*)value)->duration = values[1].asFloat(); + break; + } + + case TransitionActionType::ColorFilter: + { + TValue* tvalue = (TValue*)value; + tvalue->f1 = values[0].asFloat(); + tvalue->f2 = values[1].asFloat(); + tvalue->f3 = values[2].asFloat(); + tvalue->f4 = values[3].asFloat(); + break; + } + + case TransitionActionType::Text: + case TransitionActionType::Icon: + ((TValue_Text*)value)->text = values[0].asString(); + break; + default: + break; + } + } +} + +void Transition::setHook(const std::string& label, TransitionHook callback) +{ + for (auto& item : _items) + { + if (item->label == label) + { + item->hook = callback; + break; + } + else if (item->tweenConfig != nullptr && item->tweenConfig->endLabel == label) + { + item->tweenConfig->endHook = callback; + break; + } + } +} + +void Transition::clearHooks() +{ + for (auto& item : _items) + { + item->hook = nullptr; + if (item->tweenConfig != nullptr) + item->tweenConfig->endHook = nullptr; + } +} + +void Transition::setTarget(const std::string& label, GObject* newTarget) +{ + for (auto& item : _items) + { + if (item->label == label) + { + + item->targetId = newTarget->id; + item->target = nullptr; + } + } +} + +void Transition::setDuration(const std::string& label, float value) +{ + for (auto& item : _items) + { + if (item->tweenConfig != nullptr && item->label == label) + item->tweenConfig->duration = value; + } +} + +float Transition::getLabelTime(const string& label) const +{ + for (auto& item : _items) + { + if (item->label == label) + return item->time; + else if (item->tweenConfig != nullptr && item->label == label) + return item->time + item->tweenConfig->duration; + } + + return NAN; +} + +void Transition::setTimeScale(float value) +{ + if (_timeScale != value) + { + _timeScale = value; + + for (auto& item : _items) + { + if (item->tweener != nullptr) + item->tweener->setTimeScale(value); + else if (item->type == TransitionActionType::Transition) + { + if (((TValue_Transition*)item->value)->trans != nullptr) + ((TValue_Transition*)item->value)->trans->setTimeScale(value); + } + else if (item->type == TransitionActionType::Animation) + { + if (item->target != nullptr) + item->target->setProp(ObjectPropID::TimeScale, Value(value)); + } + } + } +} + +void Transition::updateFromRelations(const std::string& targetId, float dx, float dy) +{ + int cnt = (int)_items.size(); + if (cnt == 0) + return; + + for (auto& item : _items) + { + if (item->type == TransitionActionType::XY && item->targetId == targetId) + { + if (item->tweenConfig != nullptr) + { + if(!item->tweenConfig->startValue->b3){ + item->tweenConfig->startValue->f1 += dx; + item->tweenConfig->startValue->f2 += dy; + item->tweenConfig->endValue->f1 += dx; + item->tweenConfig->endValue->f2 += dy; + } + } + else + { + if(!((TValue*)item->value)->b3){ + ((TValue*)item->value)->f1 += dx; + ((TValue*)item->value)->f2 += dy; + } + } + } + } +} + +void Transition::onOwnerAddedToStage() +{ + if (_autoPlay && !_playing) + play(_autoPlayTimes, _autoPlayDelay, nullptr); +} + +void Transition::onOwnerRemovedFromStage() +{ + if ((_options & OPTION_AUTO_STOP_DISABLED) == 0) + stop((_options & OPTION_AUTO_STOP_AT_END) != 0 ? true : false, false); +} + +void Transition::onDelayedPlay() +{ + internalPlay(); + + _playing = _totalTasks > 0; + if (_playing) + { + if ((_options & OPTION_IGNORE_DISPLAY_CONTROLLER) != 0) + { + for (auto& item : _items) + { + if (item->target != nullptr && item->target != _owner) + item->displayLockToken = item->target->addDisplayLock(); + } + } + } + else if (_onComplete != nullptr) + { + PlayCompleteCallback func = _onComplete; + _onComplete = nullptr; + func(); + } +} + +void Transition::internalPlay() +{ + _ownerBaseX = _owner->getX(); + _ownerBaseY = _owner->getY(); + + _totalTasks = 0; + + bool needSkipAnimations = false; + int cnt = (int)_items.size(); + if (!_reversed) + { + for (int i = 0; i < cnt; i++) + { + TransitionItem* item = _items[i]; + if (item->target == nullptr) + continue; + + if (item->type == TransitionActionType::Animation && _startTime != 0 && item->time <= _startTime) + { + needSkipAnimations = true; + ((TValue_Animation*)item->value)->flag = false; + } + else + playItem(item); + } + } + else + { + for (int i = cnt - 1; i >= 0; i--) + { + TransitionItem* item = _items[i]; + if (item->target == nullptr) + continue; + + playItem(item); + } + } + + if (needSkipAnimations) + skipAnimations(); +} + +void Transition::playItem(TransitionItem* item) +{ + float time; + if (item->tweenConfig != nullptr) + { + if (_reversed) + time = (_totalDuration - item->time - item->tweenConfig->duration); + else + time = item->time; + + if (_endTime == -1 || time <= _endTime) + { + TValue* startValue; + TValue* endValue; + + if (_reversed) + { + startValue = item->tweenConfig->endValue; + endValue = item->tweenConfig->startValue; + } + else + { + startValue = item->tweenConfig->startValue; + endValue = item->tweenConfig->endValue; + } + + ((TValue*)item->value)->b1 = startValue->b1 || endValue->b1; + ((TValue*)item->value)->b2 = startValue->b2 || endValue->b2; + + switch (item->type) + { + case TransitionActionType::XY: + case TransitionActionType::Size: + case TransitionActionType::Scale: + case TransitionActionType::Skew: + item->tweener = GTween::to(startValue->getVec2(), endValue->getVec2(), item->tweenConfig->duration); + break; + + case TransitionActionType::Alpha: + case TransitionActionType::Rotation: + item->tweener = GTween::to(startValue->f1, endValue->f1, item->tweenConfig->duration); + break; + + case TransitionActionType::Color: + item->tweener = GTween::to(startValue->getColor(), endValue->getColor(), item->tweenConfig->duration); + break; + + case TransitionActionType::ColorFilter: + item->tweener = GTween::to(startValue->getVec4(), endValue->getVec4(), item->tweenConfig->duration); + break; + default: + break; + } + + item->tweener->setDelay(time) + ->setEase(item->tweenConfig->easeType) + ->setRepeat(item->tweenConfig->repeat, item->tweenConfig->yoyo) + ->setTimeScale(_timeScale) + ->setTargetAny(item) + ->onStart(CC_CALLBACK_1(Transition::onTweenStart, this)) + ->onUpdate(CC_CALLBACK_1(Transition::onTweenUpdate, this)) + ->onComplete1(CC_CALLBACK_1(Transition::onTweenComplete, this)); + + if (_endTime >= 0) + item->tweener->setBreakpoint(_endTime - time); + + _totalTasks++; + } + } + else if (item->type == TransitionActionType::Shake) + { + TValue_Shake* value = (TValue_Shake*)item->value; + + if (_reversed) + time = (_totalDuration - item->time - value->duration); + else + time = item->time; + + if (_endTime == -1 || time <= _endTime) + { + value->lastOffset.setZero(); + value->offset.setZero(); + item->tweener = GTween::shake(Vec2::ZERO, value->amplitude, value->duration) + ->setDelay(time) + ->setTimeScale(_timeScale) + ->setTargetAny(item) + ->onStart(CC_CALLBACK_1(Transition::onTweenStart, this)) + ->onUpdate(CC_CALLBACK_1(Transition::onTweenUpdate, this)) + ->onComplete1(CC_CALLBACK_1(Transition::onTweenComplete, this)); + + if (_endTime >= 0) + item->tweener->setBreakpoint(_endTime - item->time); + + _totalTasks++; + } + } + else + { + if (_reversed) + time = (_totalDuration - item->time); + else + time = item->time; + + if (time <= _startTime) + { + applyValue(item); + callHook(item, false); + } + else if (_endTime == -1 || time <= _endTime) + { + _totalTasks++; + item->tweener = GTween::delayedCall(time) + ->setTimeScale(_timeScale) + ->setTargetAny(item) + ->onComplete1(CC_CALLBACK_1(Transition::onDelayedPlayItem, this)); + } + } + + if (item->tweener != nullptr) + item->tweener->seek(_startTime); +} + +void Transition::skipAnimations() +{ + int frame; + float playStartTime; + float playTotalTime; + GObject* target; + TValue_Animation* value; + TransitionItem* item; + + int cnt = (int)_items.size(); + for (int i = 0; i < cnt; i++) + { + item = _items[i]; + if (item->type != TransitionActionType::Animation || item->time > _startTime) + continue; + + value = (TValue_Animation*)item->value; + if (value->flag) + continue; + + target = item->target; + frame = target->getProp(ObjectPropID::Frame).asInt(); + playStartTime = target->getProp(ObjectPropID::Playing).asBool() ? 0 : -1; + playTotalTime = 0; + + for (int j = i; j < cnt; j++) + { + item = _items[j]; + if (item->type != TransitionActionType::Animation || item->target != target || item->time > _startTime) + continue; + + value = (TValue_Animation*)item->value; + value->flag = true; + + if (value->frame != -1) + { + frame = value->frame; + if (value->playing) + playStartTime = item->time; + else + playStartTime = -1; + playTotalTime = 0; + } + else + { + if (value->playing) + { + if (playStartTime < 0) + playStartTime = item->time; + } + else + { + if (playStartTime >= 0) + playTotalTime += (item->time - playStartTime); + playStartTime = -1; + } + } + + callHook(item, false); + } + + if (playStartTime >= 0) + playTotalTime += (_startTime - playStartTime); + + target->setProp(ObjectPropID::Playing, Value(playStartTime >= 0)); + target->setProp(ObjectPropID::Frame, Value(frame)); + if (playTotalTime > 0) + target->setProp(ObjectPropID::DeltaTime, Value(playTotalTime)); + } +} + +void Transition::onDelayedPlayItem(GTweener* tweener) +{ + TransitionItem* item = (TransitionItem*)tweener->getTarget(); + item->tweener = nullptr; + _totalTasks--; + + applyValue(item); + callHook(item, false); + + checkAllComplete(); +} + +void Transition::onTweenStart(GTweener* tweener) +{ + TransitionItem* item = (TransitionItem*)tweener->getTarget(); + + if (item->type == TransitionActionType::XY || item->type == TransitionActionType::Size) + { + TValue* startValue; + TValue* endValue; + + if (_reversed) + { + startValue = item->tweenConfig->endValue; + endValue = item->tweenConfig->startValue; + } + else + { + startValue = item->tweenConfig->startValue; + endValue = item->tweenConfig->endValue; + } + + if (item->type == TransitionActionType::XY) + { + if (item->target != _owner) + { + if (!startValue->b1) + tweener->startValue.x = item->target->getX(); + else if (startValue->b3) //percent + tweener->startValue.x = startValue->f1 * _owner->getWidth(); + + if (!startValue->b2) + tweener->startValue.y = item->target->getY(); + else if (startValue->b3) //percent + tweener->startValue.y = startValue->f2 * _owner->getHeight(); + + if (!endValue->b1) + tweener->endValue.x = tweener->startValue.x; + else if (endValue->b3) + tweener->endValue.x = endValue->f1 * _owner->getWidth(); + + if (!endValue->b2) + tweener->endValue.y = tweener->startValue.y; + else if (endValue->b3) + tweener->endValue.y = endValue->f2 * _owner->getHeight(); + } + else + { + if (!startValue->b1) + tweener->startValue.x = item->target->getX() - _ownerBaseX; + if (!startValue->b2) + tweener->startValue.y = item->target->getY() - _ownerBaseY; + + if (!endValue->b1) + tweener->endValue.x = tweener->startValue.x; + if (!endValue->b2) + tweener->endValue.y = tweener->startValue.y; + } + } + else + { + if (!startValue->b1) + tweener->startValue.x = item->target->getWidth(); + if (!startValue->b2) + tweener->startValue.y = item->target->getHeight(); + + if (!endValue->b1) + tweener->endValue.x = tweener->startValue.x; + if (!endValue->b2) + tweener->endValue.y = tweener->startValue.y; + } + + if (item->tweenConfig->path != nullptr) + { + ((TValue*)item->value)->b1 = ((TValue*)item->value)->b2 = true; + tweener->setPath(item->tweenConfig->path); + } + } + + callHook(item, false); +} + +void Transition::onTweenUpdate(GTweener* tweener) +{ + TransitionItem* item = (TransitionItem*)tweener->getTarget(); + switch (item->type) + { + case TransitionActionType::XY: + case TransitionActionType::Size: + case TransitionActionType::Scale: + case TransitionActionType::Skew: + if (item->tweenConfig->path != nullptr) + ((TValue*)item->value)->setVec2(tweener->value.getVec2() + tweener->startValue.getVec2()); + else + ((TValue*)item->value)->setVec2(tweener->value.getVec2()); + break; + + case TransitionActionType::Alpha: + case TransitionActionType::Rotation: + ((TValue*)item->value)->f1 = tweener->value.x; + break; + + case TransitionActionType::Color: + ((TValue*)item->value)->setColor(tweener->value.getColor()); + break; + + case TransitionActionType::ColorFilter: + ((TValue*)item->value)->setVec4(tweener->value.getVec4()); + break; + + case TransitionActionType::Shake: + ((TValue_Shake*)item->value)->offset = tweener->deltaValue.getVec2(); + break; + default: + break; + } + applyValue(item); +} + +void Transition::onTweenComplete(GTweener* tweener) +{ + TransitionItem* item = (TransitionItem*)tweener->getTarget(); + item->tweener = nullptr; + _totalTasks--; + + if (tweener->allCompleted()) + callHook(item, true); + + checkAllComplete(); +} + +void Transition::onPlayTransCompleted(TransitionItem* item) +{ + _totalTasks--; + + checkAllComplete(); +} + +void Transition::callHook(TransitionItem* item, bool tweenEnd) +{ + if (tweenEnd) + { + if (item->tweenConfig != nullptr && item->tweenConfig->endHook != nullptr) + item->tweenConfig->endHook(); + } + else + { + if (item->time >= _startTime && item->hook != nullptr) + item->hook(); + } +} + +void Transition::checkAllComplete() +{ + if (_playing && _totalTasks == 0) + { + if (_totalTimes < 0) + { + internalPlay(); + } + else + { + _totalTimes--; + if (_totalTimes > 0) + internalPlay(); + else + { + _playing = false; + + for (auto& item : _items) + { + if (item->target != nullptr && item->displayLockToken != 0) + { + item->target->releaseDisplayLock(item->displayLockToken); + item->displayLockToken = 0; + } + } + + if (_onComplete != nullptr) + { + PlayCompleteCallback func = _onComplete; + _onComplete = nullptr; + func(); + } + } + } + } +} + +void Transition::applyValue(TransitionItem* item) +{ + item->target->_gearLocked = true; + + switch (item->type) + { + case TransitionActionType::XY: + { + TValue* value = (TValue*)item->value; + if (item->target == _owner) + { + if (value->b1 && value->b2) + item->target->setPosition(value->f1 + _ownerBaseX, value->f2 + _ownerBaseY); + else if (value->b1) + item->target->setX(value->f1 + _ownerBaseX); + else + item->target->setY(value->f2 + _ownerBaseY); + } + else + { + if (value->b3) //position in percent + { + if (value->b1 && value->b2) + item->target->setPosition(value->f1 * _owner->getWidth(), value->f2 * _owner->getHeight()); + else if (value->b1) + item->target->setX(value->f1 * _owner->getWidth()); + else if (value->b2) + item->target->setY(value->f2 * _owner->getHeight()); + } + else + { + if (value->b1 && value->b2) + item->target->setPosition(value->f1, value->f2); + else if (value->b1) + item->target->setX(value->f1); + else if (value->b2) + item->target->setY(value->f2); + } + } + } + break; + + case TransitionActionType::Size: + { + TValue* value = (TValue*)item->value; + if (!value->b1) + value->f1 = item->target->getWidth(); + if (!value->b2) + value->f2 = item->target->getHeight(); + item->target->setSize(value->f1, value->f2); + } + break; + + case TransitionActionType::Pivot: + item->target->setPivot(((TValue*)item->value)->f1, ((TValue*)item->value)->f2, item->target->isPivotAsAnchor()); + break; + + case TransitionActionType::Alpha: + item->target->setAlpha(((TValue*)item->value)->f1); + break; + + case TransitionActionType::Rotation: + item->target->setRotation(((TValue*)item->value)->f1); + break; + + case TransitionActionType::Scale: + item->target->setScale(((TValue*)item->value)->f1, ((TValue*)item->value)->f2); + break; + + case TransitionActionType::Skew: + item->target->setSkewX(((TValue*)item->value)->f1); + item->target->setSkewY(((TValue*)item->value)->f2); + break; + + case TransitionActionType::Color: + item->target->setProp(ObjectPropID::Color, Value(ToolSet::colorToInt((Color3B)((TValue*)item->value)->getColor()))); + break; + + case TransitionActionType::Animation: + { + TValue_Animation* value = (TValue_Animation*)item->value; + if (value->frame >= 0) + item->target->setProp(ObjectPropID::Frame, Value(value->frame)); + item->target->setProp(ObjectPropID::Playing, Value(value->playing)); + item->target->setProp(ObjectPropID::TimeScale, Value(_timeScale)); + break; + } + + case TransitionActionType::Visible: + item->target->setVisible(((TValue_Visible*)item->value)->visible); + break; + + case TransitionActionType::Shake: + { + TValue_Shake* value = (TValue_Shake*)item->value; + item->target->setPosition(item->target->getX() - value->lastOffset.x + value->offset.x, item->target->getY() - value->lastOffset.y + value->offset.y); + value->lastOffset = value->offset; + break; + } + + case TransitionActionType::Transition: + if (_playing) + { + TValue_Transition* value = (TValue_Transition*)item->value; + if (value->trans != nullptr) + { + _totalTasks++; + + float startTime = _startTime > item->time ? (_startTime - item->time) : 0; + float endTime = _endTime >= 0 ? (_endTime - item->time) : -1; + if (value->stopTime >= 0 && (endTime < 0 || endTime > value->stopTime)) + endTime = value->stopTime; + value->trans->setTimeScale(_timeScale); + value->trans->play(value->playTimes, 0, startTime, endTime, [this, item]() { onPlayTransCompleted(item); }, _reversed); + } + } + break; + + case TransitionActionType::Sound: + if (_playing && item->time >= _startTime) + { + TValue_Sound* value = (TValue_Sound*)item->value; + if (!value->sound.empty()) + UIRoot->playSound(value->sound, value->volume); + break; + } + + case TransitionActionType::ColorFilter: + break; + + case TransitionActionType::Text: + item->target->setText(((TValue_Text*)item->value)->text); + break; + + case TransitionActionType::Icon: + item->target->setIcon(((TValue_Text*)item->value)->text); + break; + default: + break; + } + + item->target->_gearLocked = false; +} + +static std::vector helperPoints; + +void Transition::setup(ByteBuffer* buffer) +{ + name = buffer->readS(); + _options = buffer->readInt(); + _autoPlay = buffer->readBool(); + _autoPlayTimes = buffer->readInt(); + _autoPlayDelay = buffer->readFloat(); + + int cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + int dataLen = buffer->readShort(); + int curPos = buffer->getPos(); + + buffer->seek(curPos, 0); + + TransitionItem* item = new TransitionItem((TransitionActionType)buffer->readByte()); + _items.push_back(item); + + item->time = buffer->readFloat(); + int targetId = buffer->readShort(); + if (targetId < 0) + item->targetId = STD_STRING_EMPTY; + else + item->targetId = _owner->getChildAt(targetId)->id; + item->label = buffer->readS(); + + if (buffer->readBool()) + { + buffer->seek(curPos, 1); + + item->tweenConfig = new TweenConfig(); + item->tweenConfig->duration = buffer->readFloat(); + if (item->time + item->tweenConfig->duration > _totalDuration) + _totalDuration = item->time + item->tweenConfig->duration; + item->tweenConfig->easeType = (EaseType)buffer->readByte(); + item->tweenConfig->repeat = buffer->readInt(); + item->tweenConfig->yoyo = buffer->readBool(); + item->tweenConfig->endLabel = buffer->readS(); + + buffer->seek(curPos, 2); + + decodeValue(item, buffer, item->tweenConfig->startValue); + + buffer->seek(curPos, 3); + + decodeValue(item, buffer, item->tweenConfig->endValue); + + if (buffer->version >= 2) + { + int pathLen = buffer->readInt(); + if (pathLen > 0) + { + item->tweenConfig->path = new GPath(); + std::vector& pts = helperPoints; + pts.clear(); + + Vec3 v0, v1, v2; + + for (int j = 0; j < pathLen; j++) + { + GPathPoint::CurveType curveType = (GPathPoint::CurveType)buffer->readByte(); + switch (curveType) + { + case GPathPoint::CurveType::Bezier: + v0.x = buffer->readFloat(); + v0.y = buffer->readFloat(); + v1.x = buffer->readFloat(); + v1.y = buffer->readFloat(); + pts.push_back(GPathPoint(v0, v1)); + break; + + case GPathPoint::CurveType::CubicBezier: + v0.x = buffer->readFloat(); + v0.y = buffer->readFloat(); + v1.x = buffer->readFloat(); + v1.y = buffer->readFloat(); + v2.x = buffer->readFloat(); + v2.y = buffer->readFloat(); + pts.push_back(GPathPoint(v0, v1, v2)); + break; + + default: + v0.x = buffer->readFloat(); + v0.y = buffer->readFloat(); + pts.push_back(GPathPoint(v0, curveType)); + break; + } + } + + item->tweenConfig->path->create(pts.data(), (int)pts.size()); + } + } + } + else + { + if (item->time > _totalDuration) + _totalDuration = item->time; + + buffer->seek(curPos, 2); + + decodeValue(item, buffer, item->value); + } + + buffer->setPos(curPos + dataLen); + } +} + +void Transition::decodeValue(TransitionItem* item, ByteBuffer* buffer, void* value) +{ + switch (item->type) + { + case TransitionActionType::XY: + case TransitionActionType::Size: + case TransitionActionType::Pivot: + case TransitionActionType::Skew: + { + TValue* tvalue = (TValue*)value; + tvalue->b1 = buffer->readBool(); + tvalue->b2 = buffer->readBool(); + tvalue->f1 = buffer->readFloat(); + tvalue->f2 = buffer->readFloat(); + + if (buffer->version >= 2 && item->type == TransitionActionType::XY) + tvalue->b3 = buffer->readBool(); //percent + break; + } + + case TransitionActionType::Alpha: + case TransitionActionType::Rotation: + ((TValue*)value)->f1 = buffer->readFloat(); + break; + + case TransitionActionType::Scale: + { + ((TValue*)value)->f1 = buffer->readFloat(); + ((TValue*)value)->f2 = buffer->readFloat(); + break; + } + + case TransitionActionType::Color: + ((TValue*)value)->setColor(buffer->readColor()); + break; + + case TransitionActionType::Animation: + { + ((TValue_Animation*)value)->playing = buffer->readBool(); + ((TValue_Animation*)value)->frame = buffer->readInt(); + break; + } + + case TransitionActionType::Visible: + ((TValue_Visible*)value)->visible = buffer->readBool(); + break; + + case TransitionActionType::Sound: + { + ((TValue_Sound*)value)->sound = buffer->readS(); + ((TValue_Sound*)value)->volume = buffer->readFloat(); + break; + } + + case TransitionActionType::Transition: + { + ((TValue_Transition*)value)->transName = buffer->readS(); + ((TValue_Transition*)value)->playTimes = buffer->readInt(); + break; + } + + case TransitionActionType::Shake: + { + ((TValue_Shake*)value)->amplitude = buffer->readFloat(); + ((TValue_Shake*)value)->duration = buffer->readFloat(); + break; + } + + case TransitionActionType::ColorFilter: + { + TValue* tvalue = (TValue*)value; + tvalue->f1 = buffer->readFloat(); + tvalue->f2 = buffer->readFloat(); + tvalue->f3 = buffer->readFloat(); + tvalue->f4 = buffer->readFloat(); + break; + } + + case TransitionActionType::Text: + case TransitionActionType::Icon: + ((TValue_Text*)value)->text = buffer->readS(); + break; + + default: + break; + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/Transition.h b/extensions/fairygui/Transition.h new file mode 100644 index 0000000000..17071ff78e --- /dev/null +++ b/extensions/fairygui/Transition.h @@ -0,0 +1,94 @@ +#ifndef __TRANSITION_H__ +#define __TRANSITION_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; +class GComponent; +class TransitionItem; +class GTweener; +class ByteBuffer; + +class Transition : public cocos2d::Ref +{ +public: + typedef std::function PlayCompleteCallback; + typedef std::function TransitionHook; + + Transition(GComponent* owner); + virtual ~Transition(); + + GComponent* getOwner() const { return _owner; } + bool isPlaying() const { return _playing; } + + void play(PlayCompleteCallback callback = nullptr); + void play(int times, float delay, PlayCompleteCallback callback = nullptr); + void play(int times, float delay, float startTime, float endTime, PlayCompleteCallback callback = nullptr); + void playReverse(PlayCompleteCallback callback = nullptr); + void playReverse(int times, float delay, PlayCompleteCallback callback = nullptr); + void changePlayTimes(int value); + void stop(); + void stop(bool setToComplete, bool processCallback); + void setAutoPlay(bool autoPlay, int times, float delay); + void setPaused(bool paused); + + void setValue(const std::string& label, const cocos2d::ValueVector& values); + void setHook(const std::string& label, TransitionHook callback); + void clearHooks(); + void setTarget(const std::string& label, GObject* newTarget); + void setDuration(const std::string& label, float value); + float getLabelTime(const std::string& label) const; + float getTimeScale() const { return _timeScale; } + void setTimeScale(float value); + + void updateFromRelations(const std::string& targetId, float dx, float dy); + void onOwnerAddedToStage(); + void onOwnerRemovedFromStage(); + + void setup(ByteBuffer* buffer); + + std::string name; + +private: + void play(int times, float delay, float startTime, float endTime, PlayCompleteCallback onComplete, bool reverse); + void stopItem(TransitionItem* item, bool setToComplete); + void onDelayedPlay(); + void internalPlay(); + void playItem(TransitionItem* item); + void skipAnimations(); + void onDelayedPlayItem(GTweener* tweener); + void onTweenStart(GTweener* tweener); + void onTweenUpdate(GTweener* tweener); + void onTweenComplete(GTweener* tweener); + void onPlayTransCompleted(TransitionItem* item); + void callHook(TransitionItem* item, bool tweenEnd); + void checkAllComplete(); + void applyValue(TransitionItem* item); + void decodeValue(TransitionItem* item, ByteBuffer* buffer, void* value); + + GComponent* _owner; + std::vector _items; + int _totalTimes; + int _totalTasks; + bool _playing; + bool _paused; + float _ownerBaseX; + float _ownerBaseY; + PlayCompleteCallback _onComplete; + int _options; + bool _reversed; + float _totalDuration; + bool _autoPlay; + int _autoPlayTimes; + float _autoPlayDelay; + float _timeScale; + float _startTime; + float _endTime; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/TranslationHelper.cpp b/extensions/fairygui/TranslationHelper.cpp new file mode 100644 index 0000000000..fe0c1560f2 --- /dev/null +++ b/extensions/fairygui/TranslationHelper.cpp @@ -0,0 +1,291 @@ +#include "TranslationHelper.h" +#include "PackageItem.h" +#include "UIPackage.h" +#if defined(ENGINEX_VERSION) +#include "pugixml/pugixml_imp.hpp" +#else +#include "tinyxml2/tinyxml2.h" +#endif +#include "utils/ByteBuffer.h" + +USING_NS_CC; +NS_FGUI_BEGIN + +using namespace std; + +std::unordered_map> TranslationHelper::strings; + +void TranslationHelper::loadFromXML(const char* xmlString, size_t nBytes) +{ + strings.clear(); + +#if defined(ENGINEX_VERSION) + pugi::xml_document doc; + if (doc.load_buffer(xmlString, nBytes)) { + auto root = doc.document_element(); + auto ele = doc.child("string"); + while (ele) + { + std::string key = ele.attribute("name").value(); + std::string text = ele.text().as_string(); + size_t i = key.find("-"); + if (i == std::string::npos) + continue; + + std::string key2 = key.substr(0, i); + std::string key3 = key.substr(i + 1); + std::unordered_map& col = strings[key2]; + col[key3] = text; + + ele = ele.next_sibling("string"); + } + } +#else + tinyxml2::XMLDocument* xml = new tinyxml2::XMLDocument(); + xml->Parse(xmlString, nBytes); + + tinyxml2::XMLElement* root = xml->RootElement(); + tinyxml2::XMLElement* ele = root->FirstChildElement("string"); + while (ele) + { + std::string key = ele->Attribute("name"); + std::string text = ele->GetText(); + size_t i = key.find("-"); + if (i == std::string::npos) + continue; + + std::string key2 = key.substr(0, i); + std::string key3 = key.substr(i + 1); + std::unordered_map& col = strings[key2]; + col[key3] = text; + + ele = ele->NextSiblingElement("string"); + } + + delete xml; +#endif +} + +void TranslationHelper::translateComponent(PackageItem* item) +{ + if (strings.empty()) + return; + + auto col = strings.find(item->owner->getId() + item->id); + if (col == strings.end()) + return; + + std::unordered_map& strings = col->second; + + ByteBuffer* buffer = item->rawData; + + buffer->seek(0, 2); + + int childCount = buffer->readShort(); + for (int i = 0; i < childCount; i++) + { + int dataLen = buffer->readShort(); + int curPos = buffer->getPos(); + + buffer->seek(curPos, 0); + + ObjectType baseType = (ObjectType)buffer->readByte(); + ObjectType type = baseType; + + buffer->skip(4); + const string& elementId = buffer->readS(); + + if (type == ObjectType::COMPONENT) + { + if (buffer->seek(curPos, 6)) + type = (ObjectType)buffer->readByte(); + } + + buffer->seek(curPos, 1); + + auto it = strings.find(elementId + "-tips"); + if (it != strings.end()) + buffer->writeS(it->second); + + buffer->seek(curPos, 2); + + int gearCnt = buffer->readShort(); + for (int j = 0; j < gearCnt; j++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + if (buffer->readByte() == 6) //gearText + { + buffer->skip(2); //controller + int valueCnt = buffer->readShort(); + for (int k = 0; k < valueCnt; k++) + { + const string& page = buffer->readS(); + if (!page.empty()) + { + if ((it = strings.find(elementId + "-texts_" + Value(k).asString())) != strings.end()) + buffer->writeS(it->second); + else + buffer->skip(2); + } + } + + if (buffer->readBool() && (it = strings.find(elementId + "-texts_def")) != strings.end()) + buffer->writeS(it->second); + } + + buffer->setPos(nextPos); + } + + if (baseType == ObjectType::COMPONENT && buffer->version >= 2) + { + buffer->seek(curPos, 4); + + buffer->skip(2); //pageController + + buffer->skip(4 * buffer->readShort()); + + int cpCount = buffer->readShort(); + for (int k = 0; k < cpCount; k++) + { + std::string target = buffer->readS(); + int propertyId = buffer->readShort(); + if (propertyId == 0 && (it = strings.find(elementId + "-cp-" + target)) != strings.end()) + buffer->writeS(it->second); + else + buffer->skip(2); + } + } + + switch (type) + { + case ObjectType::TEXT: + case ObjectType::RICHTEXT: + case ObjectType::INPUTTEXT: + { + if ((it = strings.find(elementId)) != strings.end()) + { + buffer->seek(curPos, 6); + buffer->writeS(it->second); + } + if ((it = strings.find(elementId + "-prompt")) != strings.end()) + { + buffer->seek(curPos, 4); + buffer->writeS(it->second); + } + break; + } + + case ObjectType::LIST: + { + buffer->seek(curPos, 8); + buffer->skip(2); + int itemCount = buffer->readShort(); + for (int j = 0; j < itemCount; j++) + { + int nextPos = buffer->readUshort(); + nextPos += buffer->getPos(); + + buffer->skip(2); //url + if (type == ObjectType::TREE) + buffer->skip(2); + + //title + if ((it = strings.find(elementId + "-" + Value(j).asString())) != strings.end()) + buffer->writeS(it->second); + else + buffer->skip(2); + + //selected title + if ((it = strings.find(elementId + "-" + Value(j).asString() + "-0")) != strings.end()) + buffer->writeS(it->second); + else + buffer->skip(2); + + if (buffer->version >= 2) + { + buffer->skip(6); + buffer->skip(buffer->readShort() * 4); //controllers + + int cpCount = buffer->readShort(); + for (int k = 0; k < cpCount; k++) + { + std::string target = buffer->readS(); + int propertyId = buffer->readShort(); + if (propertyId == 0 && (it = strings.find(elementId + "-" + Value(j).asString() + "-" + target)) != strings.end()) + buffer->writeS(it->second); + else + buffer->skip(2); + } + } + + buffer->setPos(nextPos); + } + break; + } + + case ObjectType::LABEL: + { + if (buffer->seek(curPos, 6) && (ObjectType)buffer->readByte() == type) + { + if ((it = strings.find(elementId)) != strings.end()) + buffer->writeS(it->second); + else + buffer->skip(2); + + buffer->skip(2); + if (buffer->readBool()) + buffer->skip(4); + buffer->skip(4); + if (buffer->readBool() && (it = strings.find(elementId + "-prompt")) != strings.end()) + buffer->writeS(it->second); + } + break; + } + + case ObjectType::BUTTON: + { + if (buffer->seek(curPos, 6) && (ObjectType)buffer->readByte() == type) + { + if ((it = strings.find(elementId)) != strings.end()) + buffer->writeS(it->second); + else + buffer->skip(2); + if ((it = strings.find(elementId + "-0")) != strings.end()) + buffer->writeS(it->second); + } + break; + } + + case ObjectType::COMBOBOX: + { + if (buffer->seek(curPos, 6) && (ObjectType)buffer->readByte() == type) + { + int itemCount = buffer->readShort(); + for (int j = 0; j < itemCount; j++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + if ((it = strings.find(elementId + "-" + Value(j).asString())) != strings.end()) + buffer->writeS(it->second); + + buffer->setPos(nextPos); + } + + if ((it = strings.find(elementId)) != strings.end()) + buffer->writeS(it->second); + } + + break; + } + default: + break; + } + + buffer->setPos(curPos + dataLen); + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/TranslationHelper.h b/extensions/fairygui/TranslationHelper.h new file mode 100644 index 0000000000..60cfa56833 --- /dev/null +++ b/extensions/fairygui/TranslationHelper.h @@ -0,0 +1,22 @@ +#ifndef __TRANSLATIONHELPER_H_ +#define __TRANSLATIONHELPER_H_ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class PackageItem; + +class TranslationHelper +{ +public: + static std::unordered_map> strings; + + static void loadFromXML(const char *xmlString, size_t nBytes); + static void translateComponent(PackageItem* item); +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/UIConfig.cpp b/extensions/fairygui/UIConfig.cpp new file mode 100644 index 0000000000..64f3079736 --- /dev/null +++ b/extensions/fairygui/UIConfig.cpp @@ -0,0 +1,63 @@ +#include "UIConfig.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +std::string UIConfig::defaultFont = ""; +std::string UIConfig::buttonSound = ""; +float UIConfig::buttonSoundVolumeScale = 1; +int UIConfig::defaultScrollStep = 25; +float UIConfig::defaultScrollDecelerationRate = 0.967f; +bool UIConfig::defaultScrollTouchEffect = true; +bool UIConfig::defaultScrollBounceEffect = true; +ScrollBarDisplayType UIConfig::defaultScrollBarDisplay = ScrollBarDisplayType::DEFAULT; +std::string UIConfig::verticalScrollBar = ""; +std::string UIConfig::horizontalScrollBar = ""; +int UIConfig::touchDragSensitivity = 10; +int UIConfig::clickDragSensitivity = 2; +int UIConfig::touchScrollSensitivity = 20; +int UIConfig::defaultComboBoxVisibleItemCount = 10; +std::string UIConfig::globalModalWaiting = ""; +std::string UIConfig::tooltipsWin = ""; +Color4F UIConfig::modalLayerColor = Color4F(0, 0, 0, 0.4f); +bool UIConfig::bringWindowToFrontOnClick = true; +std::string UIConfig::windowModalWaiting = ""; +std::string UIConfig::popupMenu = ""; +std::string UIConfig::popupMenu_seperator = ""; + +std::unordered_map UIConfig::_fontNames; + +void UIConfig::registerFont(const std::string& aliasName, const std::string& realName) +{ + FontNameItem fi; + fi.name = realName; + bool tmp = FileUtils::getInstance()->isPopupNotify(); + FileUtils::getInstance()->setPopupNotify(false); + fi.ttf = FileUtils::getInstance()->isFileExist(realName); + FileUtils::getInstance()->setPopupNotify(tmp); + _fontNames[aliasName] = fi; +} + +const std::string& UIConfig::getRealFontName(const std::string& aliasName, bool* isTTF) +{ + std::unordered_map::const_iterator it; + if (aliasName.empty()) + it = _fontNames.find(UIConfig::defaultFont); + else + it = _fontNames.find(aliasName); + if (it != _fontNames.end()) + { + if(isTTF) + *isTTF = it->second.ttf; + return it->second.name; + } + else + { + if (isTTF) + *isTTF = false; + return aliasName; + } +} + +NS_FGUI_END + diff --git a/extensions/fairygui/UIConfig.h b/extensions/fairygui/UIConfig.h new file mode 100644 index 0000000000..c706b9ae79 --- /dev/null +++ b/extensions/fairygui/UIConfig.h @@ -0,0 +1,48 @@ +#ifndef __UICONFIG_H__ +#define __UICONFIG_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +class UIConfig +{ +public: + static std::string defaultFont; + static std::string buttonSound; + static float buttonSoundVolumeScale; + static int defaultScrollStep; + static float defaultScrollDecelerationRate; + static bool defaultScrollTouchEffect; + static bool defaultScrollBounceEffect; + static ScrollBarDisplayType defaultScrollBarDisplay; + static std::string verticalScrollBar; + static std::string horizontalScrollBar; + static int touchDragSensitivity; + static int clickDragSensitivity; + static int touchScrollSensitivity; + static int defaultComboBoxVisibleItemCount; + static std::string globalModalWaiting; + static cocos2d::Color4F modalLayerColor; + static std::string tooltipsWin; + static bool bringWindowToFrontOnClick; + static std::string windowModalWaiting; + static std::string popupMenu; + static std::string popupMenu_seperator; + + static void registerFont(const std::string& aliasName, const std::string& realName); + static const std::string& getRealFontName(const std::string& aliasName, bool* isTTF = nullptr); + +private: + struct FontNameItem + { + std::string name; + bool ttf; + }; + static std::unordered_map _fontNames; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/UIObjectFactory.cpp b/extensions/fairygui/UIObjectFactory.cpp new file mode 100644 index 0000000000..d80178c32b --- /dev/null +++ b/extensions/fairygui/UIObjectFactory.cpp @@ -0,0 +1,145 @@ +#include "UIObjectFactory.h" +#include "GButton.h" +#include "GComboBox.h" +#include "GComponent.h" +#include "GGraph.h" +#include "GGroup.h" +#include "GImage.h" +#include "GLabel.h" +#include "GList.h" +#include "GLoader.h" +#include "GMovieClip.h" +#include "GProgressBar.h" +#include "GRichTextField.h" +#include "GScrollBar.h" +#include "GSlider.h" +#include "GTextField.h" +#include "GTextInput.h" +#include "GTree.h" +#include "GLoader3D.h" +#include "UIPackage.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN + +using namespace std; + +unordered_map UIObjectFactory::_packageItemExtensions; +UIObjectFactory::GLoaderCreator UIObjectFactory::_loaderCreator; + +void UIObjectFactory::setPackageItemExtension(const string& url, GComponentCreator creator) +{ + if (url.size() == 0) + { + CCLOG("Invaild url: %s", url.c_str()); + return; + } + PackageItem* pi = UIPackage::getItemByURL(url); + if (pi) + pi->extensionCreator = creator; + + _packageItemExtensions[url] = creator; +} + +GObject* UIObjectFactory::newObject(PackageItem* pi) +{ + GObject* obj; + if (pi->extensionCreator != nullptr) + obj = pi->extensionCreator(); + else + obj = newObject(pi->objectType); + if (obj != nullptr) + obj->_packageItem = pi; + + return obj; +} + +GObject* UIObjectFactory::newObject(ObjectType type) +{ + switch (type) + { + case ObjectType::IMAGE: + return GImage::create(); + + case ObjectType::MOVIECLIP: + return GMovieClip::create(); + + case ObjectType::COMPONENT: + return GComponent::create(); + + case ObjectType::TEXT: + return GBasicTextField::create(); + + case ObjectType::RICHTEXT: + return GRichTextField::create(); + + case ObjectType::INPUTTEXT: + return GTextInput::create(); + + case ObjectType::GROUP: + return GGroup::create(); + + case ObjectType::LIST: + return GList::create(); + + case ObjectType::GRAPH: + return GGraph::create(); + + case ObjectType::LOADER: + if (_loaderCreator != nullptr) + return _loaderCreator(); + else + return GLoader::create(); + + case ObjectType::BUTTON: + return GButton::create(); + + case ObjectType::LABEL: + return GLabel::create(); + + case ObjectType::PROGRESSBAR: + return GProgressBar::create(); + + case ObjectType::SLIDER: + return GSlider::create(); + + case ObjectType::SCROLLBAR: + return GScrollBar::create(); + + case ObjectType::COMBOBOX: + return GComboBox::create(); + + case ObjectType::TREE: + return GTree::create(); + + case ObjectType::LOADER3D: + return GLoader3D::create(); + + default: + return nullptr; + } +} + +void UIObjectFactory::setLoaderExtension(GLoaderCreator creator) +{ + _loaderCreator = creator; +} + +void UIObjectFactory::resolvePackageItemExtension(PackageItem* pi) +{ + auto it = _packageItemExtensions.find(UIPackage::URL_PREFIX + pi->owner->getId() + pi->id); + if (it != _packageItemExtensions.end()) + { + pi->extensionCreator = it->second; + return; + } + it = _packageItemExtensions.find(UIPackage::URL_PREFIX + pi->owner->getName() + "/" + pi->name); + if (it != _packageItemExtensions.end()) + { + pi->extensionCreator = it->second; + return; + } + pi->extensionCreator = nullptr; +} + +NS_FGUI_END diff --git a/extensions/fairygui/UIObjectFactory.h b/extensions/fairygui/UIObjectFactory.h new file mode 100644 index 0000000000..f8a71d3abd --- /dev/null +++ b/extensions/fairygui/UIObjectFactory.h @@ -0,0 +1,35 @@ +#ifndef __UIOBJECTFACTORY_H__ +#define __UIOBJECTFACTORY_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "GComponent.h" +#include "PackageItem.h" +#include "GLoader.h" + +NS_FGUI_BEGIN + +class UIObjectFactory +{ +public: + typedef std::function GComponentCreator; + typedef std::function GLoaderCreator; + + static void setPackageItemExtension(const std::string& url, GComponentCreator creator); + static GObject* newObject(PackageItem* pi); + static GObject* newObject(ObjectType type); + + static void setLoaderExtension(GLoaderCreator creator); + +private: + static void resolvePackageItemExtension(PackageItem* pi); + + static std::unordered_map _packageItemExtensions; + static GLoaderCreator _loaderCreator; + + friend class UIPackage; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/UIPackage.cpp b/extensions/fairygui/UIPackage.cpp new file mode 100644 index 0000000000..2a07942002 --- /dev/null +++ b/extensions/fairygui/UIPackage.cpp @@ -0,0 +1,884 @@ +#include "UIPackage.h" +#include "UIObjectFactory.h" +#include "display/BitmapFont.h" +#include "event/HitTest.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +using namespace std; + +const string UIPackage::URL_PREFIX = "ui://"; +int UIPackage::_constructing = 0; +std::string UIPackage::_branch; +std::unordered_map UIPackage::_vars; + +const unsigned char* emptyTextureData = new unsigned char[16]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +std::unordered_map UIPackage::_packageInstById; +std::unordered_map UIPackage::_packageInstByName; +std::vector UIPackage::_packageList; + +Texture2D* UIPackage::_emptyTexture; + +struct AtlasSprite +{ + PackageItem* atlas; + Rect rect; + Size originalSize; + Vec2 offset; + bool rotated; +}; + +UIPackage::UIPackage() + : _branchIndex(-1) +{ +} + +UIPackage::~UIPackage() +{ + for (auto& it : _items) + it->release(); + for (auto& it : _sprites) + delete it.second; +} + +void UIPackage::setBranch(const std::string& value) +{ + _branch = value; + for (auto& it : _packageList) + { + if (it->_branches.size() > 0) + { + it->_branchIndex = ToolSet::findInStringArray(it->_branches, value); + } + } +} + +const std::string& UIPackage::getVar(const std::string& key) +{ + auto it = _vars.find(key); + if (it != _vars.end()) + return it->second; + else + return STD_STRING_EMPTY; +} + +void UIPackage::setVar(const std::string& key, const std::string& value) +{ + _vars[key] = value; +} + +UIPackage* UIPackage::getById(const string& id) +{ + auto it = _packageInstById.find(id); + if (it != _packageInstById.end()) + return it->second; + else + return nullptr; +} + +UIPackage* UIPackage::getByName(const string& name) +{ + auto it = _packageInstByName.find(name); + if (it != _packageInstByName.end()) + return it->second; + else + return nullptr; +} + +UIPackage* UIPackage::addPackage(const string& assetPath) +{ + auto it = _packageInstById.find(assetPath); + if (it != _packageInstById.end()) + return it->second; + + if (_emptyTexture == nullptr) + { + Image* emptyImage = new Image(); + emptyImage->initWithRawData(emptyTextureData, 16, 2, 2, 4, false); + _emptyTexture = new Texture2D(); + _emptyTexture->initWithImage(emptyImage); + delete emptyImage; + } + + Data data; + + if (FileUtils::getInstance()->getContents(assetPath + ".fui", &data) != FileUtils::Status::OK) + { + CCLOGERROR("FairyGUI: cannot load package from '%s'", assetPath.c_str()); + return nullptr; + } + + ssize_t size; + char* p = (char*)data.takeBuffer(&size); + ByteBuffer buffer(p, 0, (int)size, true); + + UIPackage* pkg = new UIPackage(); + pkg->_assetPath = assetPath; + if (!pkg->loadPackage(&buffer)) + { + delete pkg; + return nullptr; + } + + _packageInstById[pkg->getId()] = pkg; + _packageInstByName[pkg->getName()] = pkg; + _packageInstById[assetPath] = pkg; + _packageList.push_back(pkg); + + return pkg; +} + +void UIPackage::removePackage(const string& packageIdOrName) +{ + UIPackage* pkg = UIPackage::getByName(packageIdOrName); + if (!pkg) + pkg = getById(packageIdOrName); + + if (pkg) + { + auto it = std::find(_packageList.cbegin(), _packageList.cend(), pkg); + if (it != _packageList.cend()) + _packageList.erase(it); + + _packageInstById.erase(pkg->getId()); + _packageInstById.erase(pkg->_assetPath); + _packageInstByName.erase(pkg->getName()); + + pkg->release(); + } + else + CCLOGERROR("FairyGUI: invalid package name or id: %s", packageIdOrName.c_str()); +} + +void UIPackage::removeAllPackages() +{ + for (auto& it : _packageList) + it->release(); + + _packageInstById.clear(); + _packageInstByName.clear(); + _packageList.clear(); +} + +GObject* UIPackage::createObject(const string& pkgName, const string& resName) +{ + UIPackage* pkg = UIPackage::getByName(pkgName); + if (pkg) + return pkg->createObject(resName); + else + { + CCLOGERROR("FairyGUI: package not found - %s", pkgName.c_str()); + return nullptr; + } +} + +GObject* UIPackage::createObjectFromURL(const string& url) +{ + PackageItem* pi = UIPackage::getItemByURL(url); + if (pi) + return pi->owner->createObject(pi); + else + { + CCLOGERROR("FairyGUI: resource not found - %s", url.c_str()); + return nullptr; + } +} + +string UIPackage::getItemURL(const string& pkgName, const string& resName) +{ + UIPackage* pkg = UIPackage::getByName(pkgName); + if (pkg) + { + PackageItem* pi = pkg->getItemByName(resName); + if (pi) + return URL_PREFIX + pkg->getId() + pi->id; + } + return STD_STRING_EMPTY; +} + +PackageItem* UIPackage::getItemByURL(const string& url) +{ + if (url.size() == 0) + return nullptr; + + ssize_t pos1 = url.find('/'); + if (pos1 == -1) + return nullptr; + + ssize_t pos2 = url.find('/', pos1 + 2); + if (pos2 == -1) + { + if (url.size() > 13) + { + string pkgId = url.substr(5, 8); + UIPackage* pkg = getById(pkgId); + if (pkg != nullptr) + { + string srcId = url.substr(13); + return pkg->getItem(srcId); + } + } + } + else + { + string pkgName = url.substr(pos1 + 2, pos2 - pos1 - 2); + UIPackage* pkg = getByName(pkgName); + if (pkg != nullptr) + { + string srcName = url.substr(pos2 + 1); + return pkg->getItemByName(srcName); + } + } + + return nullptr; +} + +string UIPackage::normalizeURL(const string& url) +{ + if (url.size() == 0) + return url; + + ssize_t pos1 = url.find('/'); + if (pos1 == -1) + return url; + + ssize_t pos2 = url.find('/', pos1 + 2); + if (pos2 == -1) + return url; + else + { + string pkgName = url.substr(pos1 + 2, pos2 - pos1 - 2); + string srcName = url.substr(pos2 + 1); + return getItemURL(pkgName, srcName); + } +} + +void* UIPackage::getItemAsset(const std::string& pkgName, const std::string& resName, PackageItemType type) +{ + UIPackage* pkg = UIPackage::getByName(pkgName); + if (pkg) + { + PackageItem* pi = pkg->getItemByName(resName); + if (pi) + { + if (type != PackageItemType::UNKNOWN && pi->type != type) + return nullptr; + else + return pkg->getItemAsset(pi); + } + } + return nullptr; +} + +void* UIPackage::getItemAssetByURL(const std::string& url, PackageItemType type) +{ + PackageItem* pi = UIPackage::getItemByURL(url); + if (pi) + { + if (type != PackageItemType::UNKNOWN && pi->type != type) + return nullptr; + else + return pi->owner->getItemAsset(pi); + } + else + return nullptr; +} + +PackageItem* UIPackage::getItem(const string& itemId) +{ + auto it = _itemsById.find(itemId); + if (it != _itemsById.end()) + return it->second; + else + return nullptr; +} + +PackageItem* UIPackage::getItemByName(const string& itemName) +{ + auto it = _itemsByName.find(itemName); + if (it != _itemsByName.end()) + return it->second; + else + return nullptr; +} + +GObject* UIPackage::createObject(const string& resName) +{ + PackageItem* pi = getItemByName(resName); + CCASSERT(pi, StringUtils::format("FairyGUI: resource not found - %s in %s", + resName.c_str(), _name.c_str()) + .c_str()); + + return createObject(pi); +} + +GObject* UIPackage::createObject(PackageItem* item) +{ + GObject* g = UIObjectFactory::newObject(item); + if (g == nullptr) + return nullptr; + + _constructing++; + g->constructFromResource(); + _constructing--; + return g; +} + +bool UIPackage::loadPackage(ByteBuffer* buffer) +{ + if (buffer->readUint() != 0x46475549) + { + CCLOGERROR("FairyGUI: old package format found in '%s'", _assetPath.c_str()); + return false; + } + + buffer->version = buffer->readInt(); + bool ver2 = buffer->version >= 2; + buffer->readBool(); //compressed + _id = buffer->readString(); + _name = buffer->readString(); + buffer->skip(20); + int indexTablePos = buffer->getPos(); + int cnt; + + buffer->seek(indexTablePos, 4); + + cnt = buffer->readInt(); + _stringTable.resize(cnt); + for (int i = 0; i < cnt; i++) + _stringTable[i] = buffer->readString(); + buffer->setStringTable(&_stringTable); + + buffer->seek(indexTablePos, 0); + cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + std::unordered_map info; + info["id"] = buffer->readS(); + info["name"] = buffer->readS(); + + _dependencies.push_back(info); + } + + bool branchIncluded = false; + if (ver2) + { + cnt = buffer->readShort(); + if (cnt > 0) + { + buffer->readSArray(_branches, cnt); + if (_branch.size() > 0) + _branchIndex = ToolSet::findInStringArray(_branches, _branch); + } + + branchIncluded = cnt > 0; + } + + buffer->seek(indexTablePos, 1); + + PackageItem* pi; + string path = _assetPath; + size_t pos = path.find('/'); + string shortPath = pos == -1 ? STD_STRING_EMPTY : path.substr(0, pos + 1); + path += "_"; + + cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + int nextPos = buffer->readInt(); + nextPos += buffer->getPos(); + + pi = new PackageItem(); + pi->owner = this; + pi->type = (PackageItemType)buffer->readByte(); + pi->id = buffer->readS(); + pi->name = buffer->readS(); + buffer->skip(2); //path + pi->file = buffer->readS(); + buffer->readBool(); //exported + pi->width = buffer->readInt(); + pi->height = buffer->readInt(); + + switch (pi->type) + { + case PackageItemType::IMAGE: + { + pi->objectType = ObjectType::IMAGE; + int scaleOption = buffer->readByte(); + if (scaleOption == 1) + { + pi->scale9Grid = new Rect(); + pi->scale9Grid->origin.x = buffer->readInt(); + pi->scale9Grid->origin.y = buffer->readInt(); + pi->scale9Grid->size.width = buffer->readInt(); + pi->scale9Grid->size.height = buffer->readInt(); + pi->tileGridIndice = buffer->readInt(); + } + else if (scaleOption == 2) + pi->scaleByTile = true; + + buffer->readBool(); //smoothing + break; + } + + case PackageItemType::MOVIECLIP: + { + buffer->readBool(); //smoothing + pi->objectType = ObjectType::MOVIECLIP; + pi->rawData = buffer->readBuffer(); + break; + } + + case PackageItemType::FONT: + { + pi->rawData = buffer->readBuffer(); + break; + } + + case PackageItemType::COMPONENT: + { + int extension = buffer->readByte(); + if (extension > 0) + pi->objectType = (ObjectType)extension; + else + pi->objectType = ObjectType::COMPONENT; + pi->rawData = buffer->readBuffer(); + + UIObjectFactory::resolvePackageItemExtension(pi); + break; + } + + case PackageItemType::ATLAS: + case PackageItemType::SOUND: + case PackageItemType::MISC: + { + pi->file = path + pi->file; + break; + } + + case PackageItemType::SPINE: + case PackageItemType::DRAGONBONES: + { + pi->file = shortPath + pi->file; + pi->skeletonAnchor = new Vec2(); + pi->skeletonAnchor->x = buffer->readFloat(); + pi->skeletonAnchor->y = buffer->readFloat(); + break; + } + + default: + break; + } + + if (ver2) + { + std::string str = buffer->readS(); //branch + if (!str.empty()) + pi->name = str + "/" + pi->name; + + int branchCnt = buffer->readUbyte(); + if (branchCnt > 0) + { + if (branchIncluded) + { + pi->branches = new std::vector(); + buffer->readSArray(*pi->branches, branchCnt); + } + else + _itemsById[buffer->readS()] = pi; + } + + int highResCnt = buffer->readUbyte(); + if (highResCnt > 0) + { + pi->highResolution = new std::vector(); + buffer->readSArray(*pi->highResolution, highResCnt); + } + } + + _items.push_back(pi); + _itemsById[pi->id] = pi; + if (!pi->name.empty()) + _itemsByName[pi->name] = pi; + + buffer->setPos(nextPos); + } + + buffer->seek(indexTablePos, 2); + + cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + const string& itemId = buffer->readS(); + pi = _itemsById[buffer->readS()]; + + AtlasSprite* sprite = new AtlasSprite(); + sprite->atlas = pi; + sprite->rect.origin.x = buffer->readInt(); + sprite->rect.origin.y = buffer->readInt(); + sprite->rect.size.width = buffer->readInt(); + sprite->rect.size.height = buffer->readInt(); + sprite->rotated = buffer->readBool(); + if (ver2 && buffer->readBool()) + { + sprite->offset.x = buffer->readInt(); + sprite->offset.y = buffer->readInt(); + sprite->originalSize.width = buffer->readInt(); + sprite->originalSize.height = buffer->readInt(); + } + else + { + sprite->offset.setZero(); + sprite->originalSize.width = sprite->rect.size.width; + sprite->originalSize.height = sprite->rect.size.height; + } + _sprites[itemId] = sprite; + + buffer->setPos(nextPos); + } + + if (buffer->seek(indexTablePos, 3)) + { + cnt = buffer->readShort(); + for (int i = 0; i < cnt; i++) + { + int nextPos = buffer->readInt(); + nextPos += buffer->getPos(); + + auto it = _itemsById.find(buffer->readS()); + if (it != _itemsById.end()) + { + pi = it->second; + if (pi->type == PackageItemType::IMAGE) + { + pi->pixelHitTestData = new PixelHitTestData(); + pi->pixelHitTestData->load(buffer); + } + } + + buffer->setPos(nextPos); + } + } + + return true; +} + +void* UIPackage::getItemAsset(PackageItem* item) +{ + switch (item->type) + { + case PackageItemType::IMAGE: + if (item->spriteFrame == nullptr) + loadImage(item); + return item->spriteFrame; + + case PackageItemType::ATLAS: + if (item->texture == nullptr) + loadAtlas(item); + return item->texture; + + case PackageItemType::FONT: + if (item->bitmapFont == nullptr) + loadFont(item); + return item->bitmapFont; + + case PackageItemType::MOVIECLIP: + if (item->animation == nullptr) + loadMovieClip(item); + return item->animation; + + default: + return nullptr; + } +} + +void UIPackage::loadAtlas(PackageItem* item) +{ + Image* image = new Image(); +#if COCOS2D_VERSION < 0x00031702 + Image::setPNGPremultipliedAlphaEnabled(false); +#endif + if (!image->initWithImageFile(item->file)) + { + item->texture = _emptyTexture; + _emptyTexture->retain(); + delete image; +#if COCOS2D_VERSION < 0x00031702 + Image::setPNGPremultipliedAlphaEnabled(true); +#endif + CCLOGWARN("FairyGUI: texture '%s' not found in %s", item->file.c_str(), _name.c_str()); + return; + } +#if COCOS2D_VERSION < 0x00031702 + Image::setPNGPremultipliedAlphaEnabled(true); +#endif + + Texture2D* tex = new Texture2D(); + tex->initWithImage(image); + item->texture = tex; + delete image; + + string alphaFilePath; + string ext = FileUtils::getInstance()->getFileExtension(item->file); + size_t pos = item->file.find_last_of('.'); + if (pos != -1) + alphaFilePath = item->file.substr(0, pos) + "!a" + ext; + else + alphaFilePath = item->file + "!a" + ext; + + bool hasAlphaTexture = ToolSet::isFileExist(alphaFilePath); + if (hasAlphaTexture) + { + image = new Image(); + if (!image->initWithImageFile(alphaFilePath)) + { + delete image; + return; + } + +#if defined(ENGINEX_VERSION) + if(image->getFileType() == Image::Format::ETC) + tex->updateWithImage(image, Texture2D::getDefaultAlphaPixelFormat(), 1, TextureFormatEXT::ETC1_ALPHA); +#else + tex = new Texture2D(); + tex->initWithImage(image); + item->texture->setAlphaTexture(tex); + tex->release(); +#endif + delete image; + } +} + +AtlasSprite* UIPackage::getSprite(const std::string& spriteId) +{ + auto it = _sprites.find(spriteId); + if (it != _sprites.end()) + return it->second; + else + return nullptr; +} + +//note: SpriteFrame.ref=1 not autorelease. +SpriteFrame* UIPackage::createSpriteTexture(AtlasSprite* sprite) +{ + getItemAsset(sprite->atlas); + + //not using createWithTexture for saving a autorelease call. + SpriteFrame* spriteFrame = new SpriteFrame(); + spriteFrame->initWithTexture(sprite->atlas->texture, sprite->rect, sprite->rotated, + Vec2(sprite->offset.x - (sprite->originalSize.width - sprite->rect.size.width) / 2, -(sprite->offset.y - (sprite->originalSize.height - sprite->rect.size.height) / 2)), + sprite->originalSize); + + return spriteFrame; +} + +void UIPackage::loadImage(PackageItem* item) +{ + AtlasSprite* sprite = getSprite(item->id); + if (sprite != nullptr) + item->spriteFrame = createSpriteTexture(sprite); + else + { + item->spriteFrame = new (std::nothrow) SpriteFrame(); + item->spriteFrame->initWithTexture(_emptyTexture, Rect()); + } + if (item->scaleByTile) + { +#if COCOS2D_VERSION >= 0x00040000 + Texture2D::TexParams tp(backend::SamplerFilter::LINEAR, backend::SamplerFilter::LINEAR, + backend::SamplerAddressMode::REPEAT, backend::SamplerAddressMode::REPEAT); +#else + Texture2D::TexParams tp = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT }; +#endif + item->spriteFrame->getTexture()->setTexParameters(tp); +} +} + +void UIPackage::loadMovieClip(PackageItem* item) +{ + item->animation = Animation::create(); + item->animation->retain(); + + ByteBuffer* buffer = item->rawData; + + buffer->seek(0, 0); + + float interval = buffer->readInt() / 1000.0f; + item->swing = buffer->readBool(); + item->repeatDelay = buffer->readInt() / 1000.0f; + + buffer->seek(0, 1); + + int frameCount = buffer->readShort(); + Vector frames(frameCount); + + Size mcSizeInPixels = Size(item->width, item->height); + Size mcSize = CC_SIZE_PIXELS_TO_POINTS(mcSizeInPixels); + + AtlasSprite* sprite; + SpriteFrame* spriteFrame; + + for (int i = 0; i < frameCount; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + Rect rect; + rect.origin.x = buffer->readInt(); + rect.origin.y = buffer->readInt(); + rect.size.width = buffer->readInt(); + rect.size.height = buffer->readInt(); + float addDelay = buffer->readInt() / 1000.0f; + const string& spriteId = buffer->readS(); + + if (!spriteId.empty() && (sprite = getSprite(spriteId)) != nullptr) + { + spriteFrame = createSpriteTexture(sprite); + spriteFrame->setOriginalSizeInPixels(mcSizeInPixels); + spriteFrame->setOriginalSize(mcSize); + } + else + { + //dont use createWithTexture + spriteFrame = new (std::nothrow) SpriteFrame(); + spriteFrame->initWithTexture(_emptyTexture, Rect()); + } + + spriteFrame->setOffset(Vec2(rect.origin.x - (mcSize.width - rect.size.width) / 2, -(rect.origin.y - (mcSize.height - rect.size.height) / 2))); + AnimationFrame* frame = AnimationFrame::create(spriteFrame, addDelay / interval + 1, ValueMapNull); + frames.pushBack(frame); + //tranfer to AnimationFrame + spriteFrame->release(); + + buffer->setPos(nextPos); + } + + item->animation->initWithAnimationFrames(frames, interval, 1); + delete buffer; + item->rawData = nullptr; +} + +void UIPackage::loadFont(PackageItem* item) +{ + item->bitmapFont = BitmapFont::create(); + FontAtlas* fontAtlas = new FontAtlas(*item->bitmapFont); + item->bitmapFont->_fontAtlas = fontAtlas; + + ByteBuffer* buffer = item->rawData; + + buffer->seek(0, 0); + + bool ttf = buffer->readBool(); + item->bitmapFont->_canTint = buffer->readBool(); + item->bitmapFont->_resizable = buffer->readBool(); + buffer->readBool(); //hasChannel + int fontSize = buffer->readInt(); + int xadvance = buffer->readInt(); + int lineHeight = buffer->readInt(); + + Texture2D* mainTexture = nullptr; + AtlasSprite* mainSprite = nullptr; + + if (ttf && (mainSprite = getSprite(item->id)) != nullptr) + mainTexture = (Texture2D*)getItemAsset(mainSprite->atlas); + + buffer->seek(0, 1); + + FontLetterDefinition def; + int bx = 0, by = 0; + int bw = 0, bh = 0; + PackageItem* charImg = nullptr; + + int cnt = buffer->readInt(); + for (int i = 0; i < cnt; i++) + { + int nextPos = buffer->readShort(); + nextPos += buffer->getPos(); + + memset(&def, 0, sizeof(def)); + + unsigned short ch = buffer->readUshort(); + const string& img = buffer->readS(); + bx = buffer->readInt(); + by = buffer->readInt(); + def.offsetX = buffer->readInt(); + def.offsetY = buffer->readInt(); + bw = buffer->readInt(); + bh = buffer->readInt(); + def.xAdvance = buffer->readInt(); + buffer->readByte(); //channel + + if (ttf) + { + Rect tempRect = Rect(bx + mainSprite->rect.origin.x, by + mainSprite->rect.origin.y, bw, bh); + tempRect = CC_RECT_PIXELS_TO_POINTS(tempRect); + def.U = tempRect.origin.x; + def.V = tempRect.origin.y; + def.width = tempRect.size.width; + def.height = tempRect.size.height; + def.validDefinition = true; + } + else + { + charImg = getItem(img); + if (charImg) + { + charImg = charImg->getBranch(); + bw = charImg->width; + bh = charImg->height; + + AtlasSprite* sprite = getSprite(img); + if (sprite != nullptr) + { + def.offsetX += sprite->offset.x; + def.offsetY += sprite->offset.y; + } + + charImg = charImg->getHighResolution(); + getItemAsset(charImg); + + Rect tempRect = charImg->spriteFrame->getRectInPixels(); + tempRect = CC_RECT_PIXELS_TO_POINTS(tempRect); + def.U = tempRect.origin.x; + def.V = tempRect.origin.y; + def.width = tempRect.size.width; + def.height = tempRect.size.height; + if (mainTexture == nullptr) + mainTexture = charImg->spriteFrame->getTexture(); + def.validDefinition = true; + + if (def.xAdvance == 0) + { + if (xadvance == 0) + def.xAdvance = def.offsetX + bw; + else + def.xAdvance = xadvance; + } + + if (fontSize == 0) + fontSize = bh; + lineHeight = MAX(fontSize, lineHeight); + } + } + + fontAtlas->addLetterDefinition(ch, def); + buffer->setPos(nextPos); + } + + if (mainTexture != nullptr) + fontAtlas->addTexture(mainTexture, 0); + fontAtlas->setLineHeight(lineHeight); + item->bitmapFont->_originalFontSize = fontSize; + + delete buffer; + item->rawData = nullptr; +} + +NS_FGUI_END diff --git a/extensions/fairygui/UIPackage.h b/extensions/fairygui/UIPackage.h new file mode 100644 index 0000000000..041a86822a --- /dev/null +++ b/extensions/fairygui/UIPackage.h @@ -0,0 +1,89 @@ +#ifndef __UIPACKAGE_H__ +#define __UIPACKAGE_H__ + +#include "FairyGUIMacros.h" +#include "GObject.h" +#include "PackageItem.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +struct AtlasSprite; +class ByteBuffer; + +class UIPackage : public cocos2d::Ref +{ +public: + UIPackage(); + ~UIPackage(); + + static UIPackage* getById(const std::string& id); + static UIPackage* getByName(const std::string& name); + static UIPackage* addPackage(const std::string& descFilePath); + static void removePackage(const std::string& packageIdOrName); + static void removeAllPackages(); + static GObject* createObject(const std::string& pkgName, const std::string& resName); + static GObject* createObjectFromURL(const std::string& url); + static std::string getItemURL(const std::string& pkgName, const std::string& resName); + static PackageItem* getItemByURL(const std::string& url); + static std::string normalizeURL(const std::string& url); + static void* getItemAsset(const std::string& pkgName, const std::string& resName, PackageItemType type = PackageItemType::UNKNOWN); + static void* getItemAssetByURL(const std::string& url, PackageItemType type = PackageItemType::UNKNOWN); + static cocos2d::Texture2D* getEmptyTexture() { return _emptyTexture; } + + const std::string& getId() const { return _id; } + const std::string& getName() const { return _name; } + + PackageItem* getItem(const std::string& itemId); + PackageItem* getItemByName(const std::string& itemName); + void* getItemAsset(PackageItem* item); + + static const std::string& getBranch() { return _branch; } + static void setBranch(const std::string& value); + static const std::string& getVar(const std::string& key); + static void setVar(const std::string& key, const std::string& value); + + static int _constructing; + static const std::string URL_PREFIX; + +private: + bool loadPackage(ByteBuffer* buffer); + void loadAtlas(PackageItem* item); + AtlasSprite* getSprite(const std::string& spriteId); + cocos2d::SpriteFrame* createSpriteTexture(AtlasSprite* sprite); + void loadImage(PackageItem* item); + void loadMovieClip(PackageItem* item); + void loadFont(PackageItem* item); + + GObject* createObject(const std::string& resName); + GObject* createObject(PackageItem* item); + +private: + std::string _id; + std::string _name; + std::string _assetPath; + + std::vector _items; + std::unordered_map _itemsById; + std::unordered_map _itemsByName; + std::unordered_map _sprites; + std::string _customId; + std::vector _stringTable; + std::vector> _dependencies; + std::vector _branches; + int _branchIndex; + + static std::unordered_map _packageInstById; + static std::unordered_map _packageInstByName; + static std::vector _packageList; + static std::unordered_map _vars; + static std::string _branch; + + static cocos2d::Texture2D* _emptyTexture; + + friend class PackageItem; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/Window.cpp b/extensions/fairygui/Window.cpp new file mode 100644 index 0000000000..c00f7239ee --- /dev/null +++ b/extensions/fairygui/Window.cpp @@ -0,0 +1,296 @@ +#include "Window.h" +#include "GRoot.h" +#include "UIPackage.h" +#include "UIConfig.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +Window::Window() : + _requestingCmd(0), + _frame(nullptr), + _contentPane(nullptr), + _modalWaitPane(nullptr), + _closeButton(nullptr), + _dragArea(nullptr), + _contentArea(nullptr), + _modal(false), + _inited(false), + _loading(false) +{ + _bringToFontOnClick = UIConfig::bringWindowToFrontOnClick; +} + +Window::~Window() +{ + CC_SAFE_RELEASE(_contentPane); + CC_SAFE_RELEASE(_frame); + CC_SAFE_RELEASE(_closeButton); + CC_SAFE_RELEASE(_dragArea); + CC_SAFE_RELEASE(_modalWaitPane); +} + +void Window::handleInit() +{ + GComponent::handleInit(); + + addEventListener(UIEventType::TouchBegin, CC_CALLBACK_1(Window::onTouchBegin, this)); +} + +void Window::setContentPane(GComponent* value) +{ + if (_contentPane != value) + { + if (_contentPane != nullptr) + { + removeChild(_contentPane); + + CC_SAFE_RELEASE(_frame); + _contentPane->release(); + } + _contentPane = value; + if (_contentPane != nullptr) + { + _contentPane->retain(); + + addChild(_contentPane); + setSize(_contentPane->getWidth(), _contentPane->getHeight()); + _contentPane->addRelation(this, RelationType::Size); + _frame = dynamic_cast(_contentPane->getChild("frame")); + if (_frame != nullptr) + { + _frame->retain(); + + setCloseButton(_frame->getChild("closeButton")); + setDragArea(_frame->getChild("dragArea")); + setContentArea(_frame->getChild("contentArea")); + } + } + else + _frame = nullptr; + } +} + +void Window::setCloseButton(GObject * value) +{ + if (_closeButton != nullptr) + { + _closeButton->removeClickListener(EventTag(this)); + _closeButton->release(); + } + _closeButton = value; + if (_closeButton != nullptr) + { + _closeButton->retain(); + _closeButton->addClickListener(CC_CALLBACK_1(Window::closeEventHandler, this), EventTag(this)); + } +} + +void Window::setDragArea(GObject * value) +{ + if (_dragArea != value) + { + if (_dragArea != nullptr) + { + _dragArea->setDraggable(false); + _dragArea->removeEventListener(UIEventType::DragStart, EventTag(this)); + _dragArea->release(); + } + + _dragArea = value; + if (_dragArea != nullptr) + { + _dragArea->retain(); + if (dynamic_cast(_dragArea) && ((GGraph*)_dragArea)->isEmpty()) + ((GGraph*)_dragArea)->drawRect(_dragArea->getWidth(), _dragArea->getHeight(), 0, Color4F(0, 0, 0, 0), Color4F(0, 0, 0, 0)); + _dragArea->setDraggable(true); + _dragArea->addEventListener(UIEventType::DragStart, CC_CALLBACK_1(Window::onDragStart, this), EventTag(this)); + } + } +} + +void Window::show() +{ + UIRoot->showWindow(this); +} + +void Window::hide() +{ + if (isShowing()) + doHideAnimation(); +} + +void Window::hideImmediately() +{ + UIRoot->hideWindowImmediately(this); +} + +void Window::toggleStatus() +{ + if (isTop()) + hide(); + else + show(); +} + +void Window::bringToFront() +{ + UIRoot->bringToFront(this); +} + +bool Window::isTop() const +{ + return _parent != nullptr && _parent->getChildIndex(this) == _parent->numChildren() - 1; +} + +void Window::showModalWait(int requestingCmd) +{ + if (requestingCmd != 0) + _requestingCmd = requestingCmd; + + if (!UIConfig::windowModalWaiting.empty()) + { + if (_modalWaitPane == nullptr) + { + _modalWaitPane = UIPackage::createObjectFromURL(UIConfig::windowModalWaiting); + _modalWaitPane->retain(); + } + + layoutModalWaitPane(); + + addChild(_modalWaitPane); + } +} + +void Window::layoutModalWaitPane() +{ + if (_contentArea != nullptr) + { + Vec2 pt = _frame->localToGlobal(Vec2::ZERO); + pt = globalToLocal(pt); + _modalWaitPane->setPosition((int)pt.x + _contentArea->getX(), (int)pt.y + _contentArea->getY()); + _modalWaitPane->setSize(_contentArea->getWidth(), _contentArea->getHeight()); + } + else + _modalWaitPane->setSize(_size.width, _size.height); +} + +bool Window::closeModalWait(int requestingCmd) +{ + if (requestingCmd != 0) + { + if (_requestingCmd != requestingCmd) + return false; + } + _requestingCmd = 0; + + if (_modalWaitPane != nullptr && _modalWaitPane->getParent() != nullptr) + removeChild(_modalWaitPane); + + return true; +} + +void Window::initWindow() +{ + if (_inited || _loading) + return; + + if (!_uiSources.empty()) + { + _loading = false; + int cnt = (int)_uiSources.size(); + for (int i = 0; i < cnt; i++) + { + IUISource* lib = _uiSources.at(i); + if (!lib->isLoaded()) + { + lib->load(CC_CALLBACK_0(Window::onUILoadComplete, this)); + _loading = true; + } + } + + if (!_loading) + _initWindow(); + } + else + _initWindow(); +} + +void Window::_initWindow() +{ + _inited = true; + onInit(); + + if (isShowing()) + doShowAnimation(); +} + +void Window::addUISource(IUISource * uiSource) +{ + _uiSources.pushBack(uiSource); +} + +void Window::doShowAnimation() +{ + onShown(); +} + +void Window::doHideAnimation() +{ + hideImmediately(); +} + +void Window::closeEventHandler(EventContext * context) +{ + hide(); +} + +void Window::onUILoadComplete() +{ + int cnt = (int)_uiSources.size(); + for (int i = 0; i < cnt; i++) + { + IUISource* lib = _uiSources.at(i); + if (!lib->isLoaded()) + return; + } + + _loading = false; + _initWindow(); +} + +void Window::onEnter() +{ + GComponent::onEnter(); + + if (!_inited) + initWindow(); + else + doShowAnimation(); +} + +void Window::onExit() +{ + GComponent::onExit(); + + closeModalWait(); + onHide(); +} + +void Window::onTouchBegin(EventContext * context) +{ + if (isShowing() && _bringToFontOnClick) + { + bringToFront(); + } +} + +void Window::onDragStart(EventContext * context) +{ + context->preventDefault(); + + startDrag(context->getInput()->getTouchId()); +} + + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/Window.h b/extensions/fairygui/Window.h new file mode 100644 index 0000000000..60b88815d5 --- /dev/null +++ b/extensions/fairygui/Window.h @@ -0,0 +1,99 @@ +#ifndef __WINDOW_H__ +#define __WINDOW_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "GComponent.h" + +NS_FGUI_BEGIN + +class IUISource : public cocos2d::Ref +{ +public: + virtual const std::string& getFileName() = 0; + virtual void setFileName(const std::string& value) = 0; + virtual bool isLoaded() = 0; + virtual void load(std::function callback) = 0; +}; + +class Window : public GComponent +{ +public: + Window(); + virtual ~Window(); + + CREATE_FUNC(Window); + + void show(); + void hide(); + void hideImmediately(); + void toggleStatus(); + void bringToFront(); + bool isShowing() const { return _parent != nullptr; } + bool isTop() const; + bool isModal() const { return _modal; } + void setModal(bool value) { _modal = value; } + + void showModalWait() { showModalWait(0); } + void showModalWait(int requestingCmd); + bool closeModalWait() { return closeModalWait(0); } + bool closeModalWait(int requestingCmd); + + void initWindow(); + void addUISource(IUISource* uiSource); + + bool isBringToFrontOnClick() { return _bringToFontOnClick; } + void setBringToFrontOnClick(bool value) { _bringToFontOnClick = value; } + + GComponent* getContentPane() const { return _contentPane; } + void setContentPane(GComponent* value); + GComponent* getFrame() const { return _frame; } + + GObject* getCloseButton() const { return _closeButton; } + void setCloseButton(GObject* value); + GObject* getDragArea() const { return _dragArea; } + void setDragArea(GObject* value); + GObject* getContentArea() const { return _contentArea; } + void setContentArea(GObject* value) { _contentArea = value; } + + GObject* getModalWaitingPane() const { return _modalWaitPane; } + +protected: + virtual void handleInit() override; + virtual void onInit() {}; + virtual void onShown() {}; + virtual void onHide() {}; + virtual void doShowAnimation(); + virtual void doHideAnimation(); + + virtual void onEnter() override; + virtual void onExit() override; + + void closeEventHandler(EventContext*context); + + GComponent* _contentPane; + +private: + void layoutModalWaitPane(); + void onUILoadComplete(); + void _initWindow(); + + void onTouchBegin(EventContext* context); + void onDragStart(EventContext* context); + + int _requestingCmd; + GComponent* _frame; + GObject* _modalWaitPane; + GObject* _closeButton; + GObject* _dragArea; + GObject* _contentArea; + bool _modal; + bool _bringToFontOnClick; + cocos2d::Vector _uiSources; + bool _inited; + bool _loading; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/controller_action/ChangePageAction.cpp b/extensions/fairygui/controller_action/ChangePageAction.cpp new file mode 100644 index 0000000000..5c83b5c330 --- /dev/null +++ b/extensions/fairygui/controller_action/ChangePageAction.cpp @@ -0,0 +1,50 @@ +#include "ChangePageAction.h" +#include "Controller.h" +#include "GComponent.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +void ChangePageAction::setup(ByteBuffer* buffer) +{ + ControllerAction::setup(buffer); + + objectId = buffer->readS(); + controllerName = buffer->readS(); + targetPage = buffer->readS(); +} + +void ChangePageAction::enter(GController* controller) +{ + if (controllerName.empty()) + return; + + GComponent* gcom; + if (!objectId.empty()) + gcom = controller->getParent()->getChildById(objectId)->as(); + else + gcom = controller->getParent(); + if (gcom != nullptr) + { + GController* cc = gcom->getController(controllerName); + if (cc != nullptr && cc != controller && !cc->changing) + { + if (targetPage.compare("~1") == 0) + { + if (controller->getSelectedIndex() < cc->getPageCount()) + cc->setSelectedIndex(controller->getSelectedIndex()); + } + else if (targetPage.compare("~2") == 0) + cc->setSelectedPage(controller->getSelectedPage()); + else + cc->setSelectedPageId(targetPage); + } + } +} + +void ChangePageAction::leave(GController* controller) +{ +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/controller_action/ChangePageAction.h b/extensions/fairygui/controller_action/ChangePageAction.h new file mode 100644 index 0000000000..d2d988993b --- /dev/null +++ b/extensions/fairygui/controller_action/ChangePageAction.h @@ -0,0 +1,24 @@ +#ifndef __CHANGEPAGEACTION_H__ +#define __CHANGEPAGEACTION_H__ + +#include "ControllerAction.h" + +NS_FGUI_BEGIN + +class ChangePageAction : public ControllerAction +{ +public: + virtual void setup(ByteBuffer * buffer) override; + + std::string objectId; + std::string controllerName; + std::string targetPage; + +protected: + virtual void enter(GController* controller) override; + virtual void leave(GController* controller) override; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/controller_action/ControllerAction.cpp b/extensions/fairygui/controller_action/ControllerAction.cpp new file mode 100644 index 0000000000..48dcfc26dc --- /dev/null +++ b/extensions/fairygui/controller_action/ControllerAction.cpp @@ -0,0 +1,56 @@ +#include "ControllerAction.h" +#include "Controller.h" +#include "ChangePageAction.h" +#include "PlayTransitionAction.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +ControllerAction * ControllerAction::createAction(int type) +{ + switch (type) + { + case 0: + return new PlayTransitionAction(); + + case 1: + return new ChangePageAction(); + } + return nullptr; +} + +ControllerAction::ControllerAction() +{ +} + +ControllerAction::~ControllerAction() +{ +} + +void ControllerAction::run(GController * controller, const std::string & prevPage, const std::string & curPage) +{ + if ((fromPage.empty() || std::find(fromPage.cbegin(), fromPage.cend(), prevPage) != fromPage.cend()) + && (toPage.empty() || std::find(toPage.cbegin(), toPage.cend(), curPage) != toPage.cend())) + enter(controller); + else + leave(controller); +} + +void ControllerAction::setup(ByteBuffer * buffer) +{ + int cnt; + + cnt = buffer->readShort(); + fromPage.resize(cnt); + for (int i = 0; i < cnt; i++) + fromPage[i].assign(buffer->readS()); + + cnt = buffer->readShort(); + toPage.resize(cnt); + for (int i = 0; i < cnt; i++) + toPage[i].assign(buffer->readS()); +} + +NS_FGUI_END diff --git a/extensions/fairygui/controller_action/ControllerAction.h b/extensions/fairygui/controller_action/ControllerAction.h new file mode 100644 index 0000000000..ef44cc92f9 --- /dev/null +++ b/extensions/fairygui/controller_action/ControllerAction.h @@ -0,0 +1,33 @@ +#ifndef __CONTROLLERACTION_H__ +#define __CONTROLLERACTION_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GController; +class ByteBuffer; + +class ControllerAction +{ +public: + static ControllerAction* createAction(int types); + + ControllerAction(); + virtual ~ControllerAction(); + + void run(GController* controller, const std::string& prevPage, const std::string& curPage); + virtual void setup(ByteBuffer * buffer); + + std::vector fromPage; + std::vector toPage; + +protected: + virtual void enter(GController* controller) = 0; + virtual void leave(GController* controller) = 0; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/controller_action/PlayTransitionAction.cpp b/extensions/fairygui/controller_action/PlayTransitionAction.cpp new file mode 100644 index 0000000000..8eeae84a29 --- /dev/null +++ b/extensions/fairygui/controller_action/PlayTransitionAction.cpp @@ -0,0 +1,46 @@ +#include "PlayTransitionAction.h" +#include "Controller.h" +#include "GComponent.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +PlayTransitionAction::PlayTransitionAction() : + playTimes(1), delay(0), stopOnExit(false), _currentTransition(nullptr) +{ +} + +void PlayTransitionAction::setup(ByteBuffer * buffer) +{ + ControllerAction::setup(buffer); + + transitionName = buffer->readS(); + playTimes = buffer->readInt(); + delay = buffer->readFloat(); + stopOnExit = buffer->readBool(); +} + +void PlayTransitionAction::enter(GController * controller) +{ + Transition* trans = controller->getParent()->getTransition(transitionName); + if (trans != nullptr) + { + if (_currentTransition != nullptr && _currentTransition->isPlaying()) + trans->changePlayTimes(playTimes); + else + trans->play(playTimes, delay, nullptr); + _currentTransition = trans; + } +} + +void PlayTransitionAction::leave(GController * controller) +{ + if (stopOnExit && _currentTransition != nullptr) + { + _currentTransition->stop(); + _currentTransition = nullptr; + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/controller_action/PlayTransitionAction.h b/extensions/fairygui/controller_action/PlayTransitionAction.h new file mode 100644 index 0000000000..5530c60f12 --- /dev/null +++ b/extensions/fairygui/controller_action/PlayTransitionAction.h @@ -0,0 +1,30 @@ +#ifndef __PLAYTRNASITIONACTION_H__ +#define __PLAYTRNASITIONACTION_H__ + +#include "ControllerAction.h" + +NS_FGUI_BEGIN + +class Transition; + +class PlayTransitionAction : public ControllerAction +{ +public: + PlayTransitionAction(); + virtual void setup(ByteBuffer * buffer) override; + + std::string transitionName; + int playTimes; + float delay; + bool stopOnExit; + +protected: + virtual void enter(GController* controller) override; + virtual void leave(GController* controller) override; + + Transition* _currentTransition; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/display/BitmapFont.cpp b/extensions/fairygui/display/BitmapFont.cpp new file mode 100644 index 0000000000..0f02d8672d --- /dev/null +++ b/extensions/fairygui/display/BitmapFont.cpp @@ -0,0 +1,24 @@ +#include "BitmapFont.h" + +USING_NS_FGUI; + +BitmapFont::BitmapFont():_fontAtlas(nullptr),_originalFontSize(0),_resizable(false) +{ +} + +BitmapFont::~BitmapFont() +{ +} + +BitmapFont * BitmapFont::create() +{ + BitmapFont *font = new BitmapFont(); + font->autorelease(); + + return font; +} + +int * BitmapFont::getHorizontalKerningForTextUTF32(const std::u32string & text, int & outNumLetters) const +{ + return nullptr; +} diff --git a/extensions/fairygui/display/BitmapFont.h b/extensions/fairygui/display/BitmapFont.h new file mode 100644 index 0000000000..d86892cc87 --- /dev/null +++ b/extensions/fairygui/display/BitmapFont.h @@ -0,0 +1,37 @@ +#ifndef __BITMAPFONT_H__ +#define __BITMAPFONT_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "PackageItem.h" + +NS_FGUI_BEGIN + +class BitmapFont : public cocos2d::Font +{ +public: + BitmapFont(); + virtual ~BitmapFont(); + + static BitmapFont* create(); + + virtual int* getHorizontalKerningForTextUTF32(const std::u32string& text, int &outNumLetters) const override; + virtual cocos2d::FontAtlas *createFontAtlas() override { return _fontAtlas; } + void releaseAtlas() { _fontAtlas->release(); } + void setFontSize(float fontSize) {} + int getOriginalFontSize()const { return _originalFontSize; } + bool isResizable() { return _resizable; } + bool canTint() { return _canTint; } + +private: + float _originalFontSize; + bool _resizable; + bool _canTint; + cocos2d::FontAtlas* _fontAtlas; + + friend class UIPackage; +}; + +NS_FGUI_END + +#endif \ No newline at end of file diff --git a/extensions/fairygui/display/FUIContainer.cpp b/extensions/fairygui/display/FUIContainer.cpp new file mode 100644 index 0000000000..d88ed01b5e --- /dev/null +++ b/extensions/fairygui/display/FUIContainer.cpp @@ -0,0 +1,507 @@ +#include "FUIContainer.h" +#include "base/CCStencilStateManager.h" +#include "utils/ToolSet.h" +#include "GComponent.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +#if COCOS2D_VERSION < 0x00040000 +#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) +#define CC_CLIPPING_NODE_OPENGLES 0 +#else +#define CC_CLIPPING_NODE_OPENGLES 1 +#endif + +#if CC_CLIPPING_NODE_OPENGLES +static void setProgram(Node *n, GLProgram *p) +{ + n->setGLProgram(p); + + auto& children = n->getChildren(); + for (const auto &child : children) { + setProgram(child, p); + } +} +#endif +#endif // COCOS2D_VERSION < 0x00040000 + +RectClippingSupport::RectClippingSupport() : + _clippingEnabled(false), + _scissorOldState(false), + _clippingRectDirty(true) +{ +} + +StencilClippingSupport::StencilClippingSupport() : + _stencil(nullptr), + _originStencilProgram(nullptr), + _stencilStateManager(new StencilStateManager()) +{ +} + +FUIContainer::FUIContainer() : + _rectClippingSupport(nullptr), + _stencilClippingSupport(nullptr), + gOwner(nullptr) +{ +} + +FUIContainer::~FUIContainer() +{ + CC_SAFE_DELETE(_rectClippingSupport); + if (_stencilClippingSupport) + { + if (_stencilClippingSupport->_stencil) + { + _stencilClippingSupport->_stencil->stopAllActions(); + _stencilClippingSupport->_stencil->release(); + } + CC_SAFE_DELETE(_stencilClippingSupport->_stencilStateManager); + delete _stencilClippingSupport; + } +} + +bool FUIContainer::isClippingEnabled() const +{ + if (_rectClippingSupport != nullptr) + return _rectClippingSupport->_clippingEnabled; + else + return false; +} + +void FUIContainer::setClippingEnabled(bool value) +{ + if (_rectClippingSupport == nullptr) + { + if (!value) + return; + + _rectClippingSupport = new RectClippingSupport(); + } + + _rectClippingSupport->_clippingEnabled = value; +} + +const Rect & FUIContainer::getClippingRegion() const +{ + if (_rectClippingSupport != nullptr) + return _rectClippingSupport->_clippingRegion; + else + return Rect::ZERO; +} + +void FUIContainer::setClippingRegion(const Rect & clippingRegion) +{ + if (_rectClippingSupport == nullptr) + _rectClippingSupport = new RectClippingSupport(); + + _rectClippingSupport->_clippingRegion = clippingRegion; +} + +cocos2d::Node * FUIContainer::getStencil() const +{ + if (_stencilClippingSupport != nullptr) + return _stencilClippingSupport->_stencil; + else + return nullptr; +} + +void FUIContainer::setStencil(cocos2d::Node * stencil) +{ + if (_stencilClippingSupport == nullptr) + { + if (stencil == nullptr) + return; + + _stencilClippingSupport = new StencilClippingSupport(); + } + + //early out if the stencil is already set + if (_stencilClippingSupport->_stencil == stencil) + return; + +#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS + auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine(); + if (sEngine) + { + if (_stencilClippingSupport->_stencil) + sEngine->releaseScriptObject(this, _stencilClippingSupport->_stencil); + if (stencil) + sEngine->retainScriptObject(this, stencil); + } +#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS + + //cleanup current stencil + if (_stencilClippingSupport->_stencil != nullptr && _stencilClippingSupport->_stencil->isRunning()) + { + _stencilClippingSupport->_stencil->onExitTransitionDidStart(); + _stencilClippingSupport->_stencil->onExit(); + } + CC_SAFE_RELEASE_NULL(_stencilClippingSupport->_stencil); + + //initialise new stencil + _stencilClippingSupport->_stencil = stencil; + CC_SAFE_RETAIN(_stencilClippingSupport->_stencil); + if (_stencilClippingSupport->_stencil != nullptr && this->isRunning()) + { + _stencilClippingSupport->_stencil->onEnter(); + if (this->_isTransitionFinished) + { + _stencilClippingSupport->_stencil->onEnterTransitionDidFinish(); + } + } + + if (_stencilClippingSupport->_stencil != nullptr) +#if COCOS2D_VERSION >= 0x00040000 + _stencilClippingSupport->_originStencilProgram = _stencilClippingSupport->_stencil->getProgramState(); +#else + _stencilClippingSupport->_originStencilProgram = _stencilClippingSupport->_stencil->getGLProgram(); +#endif +} + +float FUIContainer::getAlphaThreshold() const +{ + if (_stencilClippingSupport != nullptr) + return _stencilClippingSupport->_stencilStateManager->getAlphaThreshold(); + else + return 1; +} + +void FUIContainer::setAlphaThreshold(float alphaThreshold) +{ + if (_stencilClippingSupport == nullptr) + _stencilClippingSupport = new StencilClippingSupport(); + +#if COCOS2D_VERSION >= 0x00040000 + if (alphaThreshold == 1 && alphaThreshold != _stencilClippingSupport->_stencilStateManager->getAlphaThreshold()) { + if (_stencilClippingSupport->_stencil) { + restoreAllProgramStates(); + } + } +#else +#if CC_CLIPPING_NODE_OPENGLES + if (alphaThreshold == 1 && alphaThreshold != _stencilClippingSupport->_stencilStateManager->getAlphaThreshold()) + { + // should reset program used by _stencil + if (_stencilClippingSupport->_stencil) + setProgram(_stencilClippingSupport->_stencil, _stencilClippingSupport->_originStencilProgram); + } +#endif +#endif + + _stencilClippingSupport->_stencilStateManager->setAlphaThreshold(alphaThreshold); +} + +bool FUIContainer::isInverted() const +{ + if (_stencilClippingSupport != nullptr) + return _stencilClippingSupport->_stencilStateManager->isInverted(); + else + return false; +} + +void FUIContainer::setInverted(bool inverted) +{ + if (_stencilClippingSupport == nullptr) + _stencilClippingSupport = new StencilClippingSupport(); + + _stencilClippingSupport->_stencilStateManager->setInverted(inverted); +} + +void FUIContainer::onEnter() +{ +#if CC_ENABLE_SCRIPT_BINDING + if (_scriptType == kScriptTypeJavascript) + { + if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnter)) + return; + } +#endif + + Node::onEnter(); + + if (_stencilClippingSupport != nullptr && _stencilClippingSupport->_stencil != nullptr) + _stencilClippingSupport->_stencil->onEnter(); + + if (_rectClippingSupport != nullptr) + _rectClippingSupport->_clippingRectDirty = true; +} + +void FUIContainer::onEnterTransitionDidFinish() +{ +#if CC_ENABLE_SCRIPT_BINDING + if (_scriptType == kScriptTypeJavascript) + { + if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnEnterTransitionDidFinish)) + return; + } +#endif + + Node::onEnterTransitionDidFinish(); + + if (_stencilClippingSupport != nullptr && _stencilClippingSupport->_stencil != nullptr) + { + _stencilClippingSupport->_stencil->onEnterTransitionDidFinish(); + } +} + +void FUIContainer::onExitTransitionDidStart() +{ +#if CC_ENABLE_SCRIPT_BINDING + if (_scriptType == kScriptTypeJavascript) + { + if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExitTransitionDidStart)) + return; + } +#endif + + if (_stencilClippingSupport != nullptr && _stencilClippingSupport->_stencil != nullptr) + _stencilClippingSupport->_stencil->onExitTransitionDidStart(); + Node::onExitTransitionDidStart(); +} + +void FUIContainer::onExit() +{ +#if CC_ENABLE_SCRIPT_BINDING + if (_scriptType == kScriptTypeJavascript) + { + if (ScriptEngineManager::sendNodeEventToJSExtended(this, kNodeOnExit)) + return; + } +#endif + + if (_stencilClippingSupport != nullptr && _stencilClippingSupport->_stencil != nullptr) + _stencilClippingSupport->_stencil->onExit(); + + Node::onExit(); +} + +void FUIContainer::setCameraMask(unsigned short mask, bool applyChildren) +{ + Node::setCameraMask(mask, applyChildren); + + if (_stencilClippingSupport != nullptr && _stencilClippingSupport->_stencil != nullptr) + _stencilClippingSupport->_stencil->setCameraMask(mask, applyChildren); +} + +void FUIContainer::setContentSize(const Size & contentSize) +{ + Node::setContentSize(contentSize); + + if (_rectClippingSupport) + _rectClippingSupport->_clippingRectDirty = true; +} + +#if COCOS2D_VERSION >= 0x00040000 +void FUIContainer::setProgramStateRecursively(Node* node, backend::ProgramState* programState) +{ + _originalStencilProgramState[node] = node->getProgramState(); + node->setProgramState(programState); + + auto& children = node->getChildren(); + for (const auto &child : children) { + setProgramStateRecursively(child, programState); + } +} + +void FUIContainer::restoreAllProgramStates() +{ + for (auto item : _originalStencilProgramState) + { + auto node = item.first; + auto programState = item.second; + node->setProgramState(programState); + } +} +#endif + +void FUIContainer::onBeforeVisitScissor() +{ + auto glview = Director::getInstance()->getOpenGLView(); + _rectClippingSupport->_scissorOldState = glview->isScissorEnabled(); + Rect clippingRect = getClippingRect(); + if (false == _rectClippingSupport->_scissorOldState) + { +#if COCOS2D_VERSION >= 0x00040000 + Director::getInstance()->getRenderer()->setScissorTest(true); +#else + glEnable(GL_SCISSOR_TEST); +#endif + } + else + { + _rectClippingSupport->_clippingOldRect = glview->getScissorRect(); + clippingRect = ToolSet::intersection(clippingRect, _rectClippingSupport->_clippingOldRect); + } + + glview->setScissorInPoints(clippingRect.origin.x, + clippingRect.origin.y, + clippingRect.size.width, + clippingRect.size.height); +} + +void FUIContainer::onAfterVisitScissor() +{ + if (_rectClippingSupport->_scissorOldState) + { + auto glview = Director::getInstance()->getOpenGLView(); + glview->setScissorInPoints(_rectClippingSupport->_clippingOldRect.origin.x, + _rectClippingSupport->_clippingOldRect.origin.y, + _rectClippingSupport->_clippingOldRect.size.width, + _rectClippingSupport->_clippingOldRect.size.height); + } + else + { + // revert scissor test +#if COCOS2D_VERSION >= 0x00040000 + Director::getInstance()->getRenderer()->setScissorTest(false); +#else + glDisable(GL_SCISSOR_TEST); +#endif + } +} + +const Rect& FUIContainer::getClippingRect() +{ + if (_rectClippingSupport->_clippingRectDirty) + { + Vec2 worldPos = convertToWorldSpaceAR(_rectClippingSupport->_clippingRegion.origin); + AffineTransform t = getNodeToWorldAffineTransform(); + float scissorWidth = _rectClippingSupport->_clippingRegion.size.width*t.a; + float scissorHeight = _rectClippingSupport->_clippingRegion.size.height*t.d; + _rectClippingSupport->_clippingRect.setRect(worldPos.x - (scissorWidth * _anchorPoint.x), worldPos.y - (scissorHeight * _anchorPoint.y), scissorWidth, scissorHeight); + _rectClippingSupport->_clippingRectDirty = false; + } + return _rectClippingSupport->_clippingRect; +} + +void FUIContainer::visit(cocos2d::Renderer * renderer, const cocos2d::Mat4 & parentTransform, uint32_t parentFlags) +{ + if (_stencilClippingSupport != nullptr) + { + if (!_visible || _children.empty()) + return; + + uint32_t flags = processParentFlags(parentTransform, parentFlags); + + // IMPORTANT: + // To ease the migration to v3.0, we still support the Mat4 stack, + // but it is deprecated and your code should not rely on it + Director* director = Director::getInstance(); + CCASSERT(nullptr != director, "Director is null when setting matrix stack"); + director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); + director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform); + + //Add group command + + _stencilClippingSupport->_groupCommand.init(_globalZOrder); + renderer->addCommand(&_stencilClippingSupport->_groupCommand); + + renderer->pushGroup(_stencilClippingSupport->_groupCommand.getRenderQueueID()); + +#if COCOS2D_VERSION >= 0x00040000 + _stencilClippingSupport->_stencilStateManager->onBeforeVisit(_globalZOrder); +#else + _stencilClippingSupport->_beforeVisitCmd.init(_globalZOrder); + _stencilClippingSupport->_beforeVisitCmd.func = CC_CALLBACK_0(StencilStateManager::onBeforeVisit, _stencilClippingSupport->_stencilStateManager); + renderer->addCommand(&_stencilClippingSupport->_beforeVisitCmd); +#endif + + auto alphaThreshold = this->getAlphaThreshold(); + if (alphaThreshold < 1) + { +#if COCOS2D_VERSION >= 0x00040000 + auto* program = backend::Program::getBuiltinProgram(backend::ProgramType::POSITION_TEXTURE_COLOR_ALPHA_TEST); + auto programState = new (std::nothrow) backend::ProgramState(program); + auto alphaLocation = programState->getUniformLocation("u_alpha_value"); + programState->setUniform(alphaLocation, &alphaThreshold, sizeof(alphaThreshold)); + setProgramStateRecursively(_stencilClippingSupport->_stencil, programState); + CC_SAFE_RELEASE_NULL(programState); +#else +#if CC_CLIPPING_NODE_OPENGLES + // since glAlphaTest do not exists in OES, use a shader that writes + // pixel only if greater than an alpha threshold + GLProgram *program = GLProgramCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_ALPHA_TEST_NO_MV); + GLint alphaValueLocation = glGetUniformLocation(program->getProgram(), GLProgram::UNIFORM_NAME_ALPHA_TEST_VALUE); + // set our alphaThreshold + program->use(); + program->setUniformLocationWith1f(alphaValueLocation, alphaThreshold); + // we need to recursively apply this shader to all the nodes in the stencil node + // FIXME: we should have a way to apply shader to all nodes without having to do this + setProgram(_stencilClippingSupport->_stencil, program); +#endif +#endif + + } + _stencilClippingSupport->_stencil->visit(renderer, _modelViewTransform, flags); + + _stencilClippingSupport->_afterDrawStencilCmd.init(_globalZOrder); + _stencilClippingSupport->_afterDrawStencilCmd.func = CC_CALLBACK_0(StencilStateManager::onAfterDrawStencil, _stencilClippingSupport->_stencilStateManager); + renderer->addCommand(&_stencilClippingSupport->_afterDrawStencilCmd); + + int i = 0; + bool visibleByCamera = isVisitableByVisitingCamera(); + + if (!_children.empty()) + { + sortAllChildren(); + // draw children zOrder < 0 + for (auto size = _children.size(); i < size; ++i) + { + auto node = _children.at(i); + + if (node && node->getLocalZOrder() < 0) + node->visit(renderer, _modelViewTransform, flags); + else + break; + } + // self draw + if (visibleByCamera) + this->draw(renderer, _modelViewTransform, flags); + + for (auto it = _children.cbegin() + i, itCend = _children.cend(); it != itCend; ++it) + (*it)->visit(renderer, _modelViewTransform, flags); + } + else if (visibleByCamera) + { + this->draw(renderer, _modelViewTransform, flags); + } + + _stencilClippingSupport->_afterVisitCmd.init(_globalZOrder); + _stencilClippingSupport->_afterVisitCmd.func = CC_CALLBACK_0(StencilStateManager::onAfterVisit, _stencilClippingSupport->_stencilStateManager); + renderer->addCommand(&_stencilClippingSupport->_afterVisitCmd); + + renderer->popGroup(); + + director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); + } + else if (_rectClippingSupport != nullptr && _rectClippingSupport->_clippingEnabled) + { + if (parentFlags & FLAGS_DIRTY_MASK) + { + _rectClippingSupport->_clippingRectDirty = true; + } +#if COCOS2D_VERSION >= 0x00040000 + _rectClippingSupport->_groupCommand.init(_globalZOrder); + renderer->addCommand(&_rectClippingSupport->_groupCommand); + renderer->pushGroup(_rectClippingSupport->_groupCommand.getRenderQueueID()); +#endif + _rectClippingSupport->_beforeVisitCmdScissor.init(_globalZOrder); + _rectClippingSupport->_beforeVisitCmdScissor.func = CC_CALLBACK_0(FUIContainer::onBeforeVisitScissor, this); + renderer->addCommand(&_rectClippingSupport->_beforeVisitCmdScissor); + + Node::visit(renderer, parentTransform, parentFlags); + + _rectClippingSupport->_afterVisitCmdScissor.init(_globalZOrder); + _rectClippingSupport->_afterVisitCmdScissor.func = CC_CALLBACK_0(FUIContainer::onAfterVisitScissor, this); + renderer->addCommand(&_rectClippingSupport->_afterVisitCmdScissor); +#if COCOS2D_VERSION >= 0x00040000 + renderer->popGroup(); +#endif + } + else + Node::visit(renderer, parentTransform, parentFlags); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/display/FUIContainer.h b/extensions/fairygui/display/FUIContainer.h new file mode 100644 index 0000000000..af4cc287e7 --- /dev/null +++ b/extensions/fairygui/display/FUIContainer.h @@ -0,0 +1,115 @@ +#ifndef __FUICONTAINER_H__ +#define __FUICONTAINER_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +class GObject; + +class RectClippingSupport +{ +public: + RectClippingSupport(); + + cocos2d::Rect _clippingRegion; + bool _clippingEnabled; + bool _scissorOldState; + cocos2d::Rect _clippingOldRect; + cocos2d::Rect _clippingRect; + bool _clippingRectDirty; + +#if COCOS2D_VERSION >= 0x00040000 + cocos2d::GroupCommand _groupCommand; + cocos2d::CallbackCommand _beforeVisitCmdScissor; + cocos2d::CallbackCommand _afterVisitCmdScissor; +#else + cocos2d::CustomCommand _beforeVisitCmdScissor; + cocos2d::CustomCommand _afterVisitCmdScissor; +#endif +}; + +class StencilClippingSupport +{ +public: + StencilClippingSupport(); + + cocos2d::Node* _stencil; + cocos2d::StencilStateManager* _stencilStateManager; + cocos2d::GroupCommand _groupCommand; +#if COCOS2D_VERSION >= 0x00040000 + cocos2d::backend::ProgramState* _originStencilProgram; + cocos2d::CallbackCommand _beforeVisitCmd; + cocos2d::CallbackCommand _afterDrawStencilCmd; + cocos2d::CallbackCommand _afterVisitCmd; +#else + cocos2d::GLProgram* _originStencilProgram; + cocos2d::CustomCommand _beforeVisitCmd; + cocos2d::CustomCommand _afterDrawStencilCmd; + cocos2d::CustomCommand _afterVisitCmd; +#endif +}; + +class FUIContainer : public cocos2d::Node +{ +public: + FUIContainer(); + virtual ~FUIContainer(); + + CREATE_FUNC(FUIContainer); + + bool isClippingEnabled() const; + void setClippingEnabled(bool value); + const cocos2d::Rect& getClippingRegion() const; + void setClippingRegion(const cocos2d::Rect& clippingRegion); + + cocos2d::Node* getStencil() const; + void setStencil(cocos2d::Node* stencil); + float getAlphaThreshold() const; + void setAlphaThreshold(float alphaThreshold); + bool isInverted() const; + void setInverted(bool inverted); + + void onEnter() override; + void onEnterTransitionDidFinish() override; + void onExitTransitionDidStart() override; + void onExit() override; + void visit(cocos2d::Renderer *renderer, const cocos2d::Mat4 &parentTransform, uint32_t parentFlags) override; + void setCameraMask(unsigned short mask, bool applyChildren = true) override; + + virtual void setContentSize(const cocos2d::Size& contentSize) override; + + GObject* gOwner; +private: + void onBeforeVisitScissor(); + void onAfterVisitScissor(); + const cocos2d::Rect& getClippingRect(); + + RectClippingSupport* _rectClippingSupport; + StencilClippingSupport* _stencilClippingSupport; + +#if COCOS2D_VERSION >= 0x00040000 + void setProgramStateRecursively(Node* node, cocos2d::backend::ProgramState* programState); + void restoreAllProgramStates(); + + std::unordered_map _originalStencilProgramState; +#endif +}; + +//internal use +class FUIInnerContainer : public cocos2d::Node +{ +public: + CREATE_FUNC(FUIInnerContainer); + + void setPosition2(const cocos2d::Vec2 &position) { setPosition(position.x, _parent->getContentSize().height - position.y); } + cocos2d::Vec2 getPosition2() { return cocos2d::Vec2(_position.x, _parent->getContentSize().height - _position.y); } + void setPosition2(float x, float y) { setPosition(x, _parent->getContentSize().height - y); } + void setPositionY2(float y) { setPositionY(_parent->getContentSize().height - y); } + float getPositionY2(void) const { return _parent->getContentSize().height - _position.y; } +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/display/FUIInput.cpp b/extensions/fairygui/display/FUIInput.cpp new file mode 100644 index 0000000000..fccb6797fb --- /dev/null +++ b/extensions/fairygui/display/FUIInput.cpp @@ -0,0 +1,102 @@ +#include "FUIInput.h" +#include "UIPackage.h" +#include "GTextInput.h" +#include "UIConfig.h" +NS_FGUI_BEGIN +USING_NS_CC; + +FUIInput * FUIInput::create() +{ + FUIInput* pRet = new (std::nothrow) FUIInput(); + + if (pRet != nullptr && pRet->initWithSizeAndBackgroundSprite(Size(100, 100), + (ui::Scale9Sprite*)ui::Scale9Sprite::createWithTexture(UIPackage::getEmptyTexture()))) + { + pRet->autorelease(); + pRet->continueInit(); + } + else + { + CC_SAFE_DELETE(pRet); + } + + return pRet; +} + +FUIInput::FUIInput() : + _textFormat(new TextFormat()), + _password(false), + _keyboardType(0) +{ +} + +FUIInput::~FUIInput() +{ + delete _textFormat; +} + +std::string FUIInput::getText() const +{ + return ui::EditBox::getText(); +} + +void FUIInput::setText(const std::string & value) +{ + ui::EditBox::setText(value.c_str()); +} + +void FUIInput::applyTextFormat() +{ + setFontName(UIConfig::getRealFontName(_textFormat->face).c_str()); + setFontSize(_textFormat->fontSize); + setPlaceholderFontSize(_textFormat->fontSize); + setFontColor(_textFormat->color); + //setPlaceholderFontColor(_textFormat->color); +} + +bool FUIInput::isSingleLine() const +{ + return getInputMode() == ui::EditBox::InputMode::SINGLE_LINE; +} + +void FUIInput::setSingleLine(bool value) +{ + setInputMode(ui::EditBox::InputMode::SINGLE_LINE); +} + +void FUIInput::setPassword(bool value) +{ + _password = value; + setInputFlag(ui::EditBox::InputFlag::PASSWORD); +} + +void FUIInput::setKeyboardType(int value) +{ + //if (!_password) + //setInputMode((ui::EditBox::InputMode)value); +} + +void FUIInput::openKeyboard() +{ +#if COCOS2D_VERSION >= 0x00031700 + EditBox::openKeyboard(); +#else + touchDownAction(this, cocos2d::ui::Widget::TouchEventType::ENDED); +#endif +} + +void FUIInput::continueInit() +{ + applyTextFormat(); + + //disable default behavior + this->setTouchEnabled(false); + this->addTouchEventListener(CC_CALLBACK_2(FUIInput::_touchDownAction, this)); +} + +void FUIInput::_touchDownAction(cocos2d::Ref *sender, cocos2d::ui::Widget::TouchEventType controlEvent) +{ + //do nothing +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/display/FUIInput.h b/extensions/fairygui/display/FUIInput.h new file mode 100644 index 0000000000..e81f42f0c8 --- /dev/null +++ b/extensions/fairygui/display/FUIInput.h @@ -0,0 +1,48 @@ +#ifndef __FUIINPUT_H__ +#define __FUIINPUT_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "ui/UIEditBox/UIEditBox.h" +#include "TextFormat.h" + +NS_FGUI_BEGIN + +class FUIInput : public cocos2d::ui::EditBox +{ +public: + static FUIInput* create(); + + FUIInput(); + virtual ~FUIInput(); + + std::string getText() const; + void setText(const std::string& value); + + TextFormat* getTextFormat() const { return _textFormat; } + void applyTextFormat(); + + bool isSingleLine() const; + void setSingleLine(bool value); + + bool isPassword() const { return _password; } + void setPassword(bool value); + + int keyboardType() const { return _keyboardType; } + void setKeyboardType(int value); + + void openKeyboard(); + +private: + void continueInit(); + + void _touchDownAction(cocos2d::Ref *sender, cocos2d::ui::Widget::TouchEventType controlEvent); + + TextFormat* _textFormat; + bool _password; + int _keyboardType; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/display/FUILabel.cpp b/extensions/fairygui/display/FUILabel.cpp new file mode 100644 index 0000000000..b24a3aeb35 --- /dev/null +++ b/extensions/fairygui/display/FUILabel.cpp @@ -0,0 +1,184 @@ +#include "FUILabel.h" +#include "BitmapFont.h" +#include "UIConfig.h" +#include "UIPackage.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +static Color3B toGrayed(const Color3B& source) +{ + Color3B c = source; + c.r = c.g = c.b = c.r * 0.299f + c.g * 0.587f + c.b * 0.114f; + return c; +} + +FUILabel::FUILabel() : _fontSize(-1), + _bmFontCanTint(false), + _textFormat(new TextFormat()), + _grayed(false) +{ +} + +FUILabel::~FUILabel() +{ + delete _textFormat; +} + +void FUILabel::setText(const std::string& value) +{ + if (_fontSize < 0) + applyTextFormat(); + + setString(value); +} + +void FUILabel::applyTextFormat() +{ + if (_fontSize < 0 /**first time**/ || _fontName != _textFormat->face) + { + _fontName = _textFormat->face; + Label::LabelType oldType = _currentLabelType; + + if (_fontName.find("ui://") != -1) + { + setBMFontFilePath(_fontName); + } + else + { + bool ttf = false; + const std::string& fontName = UIConfig::getRealFontName(_fontName, &ttf); + if (ttf) + { + _fontConfig.fontFilePath = fontName; + _fontConfig.fontSize = _textFormat->fontSize; + setTTFConfig(_fontConfig); + } + else + { + setSystemFontName(fontName); + } + + if (oldType == LabelType::BMFONT) + setTextColor((Color4B)_textFormat->color); + } + } + + if (_fontSize != _textFormat->fontSize) + { + _fontSize = _textFormat->fontSize; + if (_currentLabelType == LabelType::STRING_TEXTURE) + { + setSystemFontSize(_fontSize); + } + else if (_currentLabelType == LabelType::BMFONT) + { + setBMFontSize(_fontSize); + } + else + { + _fontConfig.fontSize = _fontSize; + setTTFConfig(_fontConfig); + } + } + + if (_currentLabelType != LabelType::BMFONT) + setTextColor((Color4B)(_grayed ? toGrayed(_textFormat->color) : _textFormat->color)); + else if (_bmFontCanTint) + setColor(_grayed ? toGrayed(_textFormat->color) : _textFormat->color); + + if (_textFormat->underline) + enableUnderline(); + else + disableEffect(LabelEffect::UNDERLINE); + + if (_textFormat->italics) + enableItalics(); + else + disableEffect(LabelEffect::ITALICS); + + if (_textFormat->bold && _currentLabelType != LabelType::STRING_TEXTURE) + enableBold(); + else + disableEffect(LabelEffect::BOLD); + + setLineSpacing(_textFormat->lineSpacing); + setHorizontalAlignment(_textFormat->align); + setVerticalAlignment(_textFormat->verticalAlign); + + if (_textFormat->hasEffect(TextFormat::OUTLINE)) + enableOutline((Color4B)(_grayed ? toGrayed(_textFormat->outlineColor) : _textFormat->outlineColor), _textFormat->outlineSize); + else + disableEffect(LabelEffect::OUTLINE); + + if (_textFormat->hasEffect(TextFormat::SHADOW)) + enableShadow((Color4B)(_grayed ? toGrayed(_textFormat->shadowColor) : _textFormat->shadowColor), _textFormat->shadowOffset); + else if (!_textFormat->bold) + disableEffect(LabelEffect::SHADOW); +} + +bool FUILabel::setBMFontFilePath(const std::string& bmfontFilePath, const Vec2& imageOffset, float fontSize) +{ + BitmapFont* bmFont = (BitmapFont*)UIPackage::getItemAssetByURL(bmfontFilePath, PackageItemType::FONT); + if (bmFont == nullptr) + { + reset(); + return false; + } + + //assign the default fontSize + if (std::abs(fontSize) < FLT_EPSILON) + { + float originalFontSize = bmFont->getOriginalFontSize(); + _bmFontSize = originalFontSize / CC_CONTENT_SCALE_FACTOR(); + } + + if (fontSize > 0.0f && bmFont->isResizable()) + { + _bmFontSize = fontSize; + } + + _bmFontPath = bmfontFilePath; + _bmFontCanTint = bmFont->canTint(); + + _currentLabelType = LabelType::BMFONT; + setFontAtlas(bmFont->createFontAtlas()); + + return true; +} + +void FUILabel::setGrayed(bool value) +{ + if (_grayed != value) + { + _grayed = value; + + if (_currentLabelType != LabelType::BMFONT) + setTextColor((Color4B)(_grayed ? toGrayed(_textFormat->color) : _textFormat->color)); + else if (_bmFontCanTint) + setColor(_grayed ? toGrayed(_textFormat->color) : _textFormat->color); + + if (_textFormat->hasEffect(TextFormat::OUTLINE)) + enableOutline((Color4B)(_grayed ? toGrayed(_textFormat->outlineColor) : _textFormat->outlineColor), _textFormat->outlineSize); + + if (_textFormat->hasEffect(TextFormat::SHADOW)) + enableShadow((Color4B)(_grayed ? toGrayed(_textFormat->shadowColor) : _textFormat->shadowColor), _textFormat->shadowOffset); + } +} + +void FUILabel::updateBMFontScale() +{ + auto font = _fontAtlas->getFont(); + if (_currentLabelType == LabelType::BMFONT) + { + BitmapFont* bmFont = (BitmapFont*)font; + float originalFontSize = bmFont->getOriginalFontSize(); + _bmfontScale = _bmFontSize * CC_CONTENT_SCALE_FACTOR() / originalFontSize; + } + else + { + _bmfontScale = 1.0f; + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/display/FUILabel.h b/extensions/fairygui/display/FUILabel.h new file mode 100644 index 0000000000..bf1fe9523a --- /dev/null +++ b/extensions/fairygui/display/FUILabel.h @@ -0,0 +1,45 @@ +#ifndef __FUILABEL_H__ +#define __FUILABEL_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "TextFormat.h" + +NS_FGUI_BEGIN + +class FUILabel : public cocos2d::Label +{ +public: + FUILabel(); + virtual ~FUILabel(); + + CREATE_FUNC(FUILabel); + + const std::string& getText() const { return getString(); } + void setText(const std::string& value); + + TextFormat* getTextFormat() const { return _textFormat; } + void applyTextFormat(); + + virtual bool setBMFontFilePath(const std::string& bmfontFilePath, const cocos2d::Vec2& imageOffset = cocos2d::Vec2::ZERO, float fontSize = 0) override; + + void setGrayed(bool value); +protected: + /* + ×¢Ò⣡£¡£¡Èç¹ûÕâÀï³öÏÖÁ˱àÒë´íÎó£¬ÐèÒªÐÞ¸Äcocos2dµÄÔ´Â룬Îļþ2d/CCLabel.h£¬´óÔ¼ÔÚ672ÐУ¬ÎªupdateBMFontScaleº¯Êý´òÉÏvirtualÐÞÊηû¡£ + ÒòΪÕâ¸ö·½·¨ÀïÓÐÇ¿ÖÆ×ÖÌå¶ÔÏóÖ¸ÕëΪFontFntÀàÐ͵ĴúÂ룬µ«ÎÒÃDz»Ê¹ÓÃFontFnt£¨FontFntÖ»Ö§³Ö´ÓÍⲿÎļþÖÐÔØÈëÅäÖ㬸üÔã¸âµÄÊÇBMFontConfigurationÊǶ¨ÒåÔÚcppÀïµÄ¡££© + ËùÒÔÐèÒªÖØдÕâ¸ö·½·¨¡£ + */ + virtual void updateBMFontScale() override; + +private: + TextFormat* _textFormat; + std::string _fontName; + int _fontSize; + bool _bmFontCanTint; + bool _grayed; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/display/FUIRichText.cpp b/extensions/fairygui/display/FUIRichText.cpp new file mode 100644 index 0000000000..205b051404 --- /dev/null +++ b/extensions/fairygui/display/FUIRichText.cpp @@ -0,0 +1,780 @@ +#include "FUIRichText.h" + +#include +#include +#include +#include + +#include "utils/ToolSet.h" +#include "UIPackage.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +using namespace std; + +#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) +#define strcasecmp _stricmp +#endif + +static const int GUTTER_X = 2; +static const int GUTTER_Y = 2; + +static int getPrevWord(const std::string& text, int idx) +{ + // start from idx-1 + for (int i = idx - 1; i >= 0; --i) + { + if (!std::isalnum(text[i], std::locale())) + return i; + } + return -1; +} + +static bool isWrappable(const std::string& text) +{ + for (size_t i = 0, size = text.length(); i < size; ++i) + { + if (!std::isalnum(text[i], std::locale())) + return true; + } + return false; +} + +static float getPaddingAmount(TextHAlignment alignment, const float leftOver) { + switch (alignment) { + case TextHAlignment::CENTER: + return leftOver / 2.f; + case TextHAlignment::RIGHT: + return leftOver; + default: + CCASSERT(false, "invalid horizontal alignment!"); + return 0.f; + } +} + +static bool isWhitespace(char c) { + return std::isspace(c, std::locale()); +} + +static void ltrim(std::string& s) { + s.erase(s.begin(), std::find_if_not(s.begin(), + s.end(), + isWhitespace)); +} + +static void rtrim(std::string& s) { + s.erase(std::find_if_not(s.rbegin(), + s.rend(), + isWhitespace).base(), + s.end()); +} + +static float stripTrailingWhitespace(const std::vector& row) { + if (!row.empty()) { + if (auto label = dynamic_cast(row.back())) { + const auto width = label->getContentSize().width; + auto str = label->getString(); + rtrim(str); + if (label->getString() != str) { + label->setString(str); + return label->getContentSize().width - width; + } + } + } + return 0.0f; +} + +static std::string getSubStringOfUTF8String(const std::string& str, std::string::size_type start, std::string::size_type length) +{ + std::u32string utf32; + if (!StringUtils::UTF8ToUTF32(str, utf32)) { + CCLOGERROR("Can't convert string to UTF-32: %s", str.c_str()); + return ""; + } + if (utf32.size() < start) { + CCLOGERROR("'start' is out of range: %ld, %s", static_cast(start), str.c_str()); + return ""; + } + std::string result; + if (!StringUtils::UTF32ToUTF8(utf32.substr(start, length), result)) { + CCLOGERROR("Can't convert internal UTF-32 string to UTF-8: %s", str.c_str()); + return ""; + } + return result; +} + +class FUIRichElement +{ +public: + enum class Type + { + TEXT, + IMAGE, + LINK + }; + + FUIRichElement(Type type); + virtual ~FUIRichElement() {}; + + Type _type; + std::string text; + TextFormat textFormat; + int width; + int height; + FUIRichElement* link; +}; + +FUIRichElement::FUIRichElement(Type type) : + _type(type), + width(0), + height(0), + link(nullptr) +{ +}; + +class FUIXMLVisitor : public SAXDelegator +{ +public: + explicit FUIXMLVisitor(FUIRichText* richText); + virtual ~FUIXMLVisitor(); + + void startElement(void *ctx, const char *name, const char **atts) override; + void endElement(void *ctx, const char *name) override; + void textHandler(void *ctx, const char *s, size_t len) override; + +private: + ValueMap tagAttrMapWithXMLElement(const char ** attrs); + int attributeInt(const ValueMap& vm, const std::string& key, int defaultValue); + + void pushTextFormat(); + void popTextFormat(); + void addNewLine(bool check); + void finishTextBlock(); + + FUIRichText* _richText; + std::vector _textFormatStack; + std::vector _linkStack; + TextFormat _format; + size_t _textFormatStackTop; + int _skipText; + bool _ignoreWhiteSpace; + std::string _textBlock; +}; + +FUIXMLVisitor::FUIXMLVisitor(FUIRichText* richText) + : _richText(richText), + _textFormatStackTop(0), + _skipText(0), + _ignoreWhiteSpace(false) +{ + _format = *_richText->_defaultTextFormat; +} + +FUIXMLVisitor::~FUIXMLVisitor() +{ +} + +void FUIXMLVisitor::pushTextFormat() +{ + if (_textFormatStack.size() <= _textFormatStackTop) + _textFormatStack.push_back(_format); + else + _textFormatStack[_textFormatStackTop] = _format; + _textFormatStackTop++; +} + +void FUIXMLVisitor::popTextFormat() +{ + if (_textFormatStackTop > 0) + { + _format = _textFormatStack[_textFormatStackTop - 1]; + _textFormatStackTop--; + } +} + +void FUIXMLVisitor::addNewLine(bool check) +{ + FUIRichElement* lastElement = _richText->_richElements.empty() ? nullptr : _richText->_richElements.back(); + if (lastElement && lastElement->_type == FUIRichElement::Type::TEXT) + { + if (!check || lastElement->text.back() != '\n') + lastElement->text += "\n"; + return; + } + + FUIRichElement* element = new FUIRichElement(FUIRichElement::Type::TEXT); + element->textFormat = _format; + element->text = "\n"; + _richText->_richElements.push_back(element); + if (!_linkStack.empty()) + element->link = _linkStack.back(); +} + +void FUIXMLVisitor::finishTextBlock() +{ + if (!_textBlock.empty()) + { + FUIRichElement* element = new FUIRichElement(FUIRichElement::Type::TEXT); + element->textFormat = _format; + element->text = _textBlock; + _textBlock.clear(); + _richText->_richElements.push_back(element); + if (!_linkStack.empty()) + element->link = _linkStack.back(); + } +} + +#pragma warning(once:4307) +void FUIXMLVisitor::startElement(void* /*ctx*/, const char *elementName, const char **atts) +{ + finishTextBlock(); + + if (strcasecmp(elementName, "b") == 0) + { + pushTextFormat(); + _format.bold = true; + } + else if (strcasecmp(elementName, "i") == 0) + { + pushTextFormat(); + _format.italics = true; + } + else if (strcasecmp(elementName, "u") == 0) + { + pushTextFormat(); + _format.underline = true; + } + else if (strcasecmp(elementName, "font") == 0) + { + pushTextFormat(); + ValueMap&& tagAttrValueMap = tagAttrMapWithXMLElement(atts); + _format.fontSize = attributeInt(tagAttrValueMap, "size", _format.fontSize); + + auto it = tagAttrValueMap.find("color"); + if (it != tagAttrValueMap.end()) + { + _format.color = (Color3B)ToolSet::hexToColor(it->second.asString().c_str()); + _format._hasColor = true; + } + } + else if (strcasecmp(elementName, "br") == 0) + { + addNewLine(false); + } + else if (strcasecmp(elementName, "img") == 0) + { + std::string src; + ValueMap&& tagAttrValueMap = tagAttrMapWithXMLElement(atts); + + int width = 0; + int height = 0; + + auto it = tagAttrValueMap.find("src"); + if (it != tagAttrValueMap.end()) { + src = it->second.asString(); + } + + if (!src.empty()) { + PackageItem* pi = UIPackage::getItemByURL(src); + if (pi) + { + width = pi->width; + height = pi->height; + } + } + + width = attributeInt(tagAttrValueMap, "width", width); + height = attributeInt(tagAttrValueMap, "height", height); + if (width == 0) + width = 5; + if (height == 0) + height = 10; + + FUIRichElement* element = new FUIRichElement(FUIRichElement::Type::IMAGE); + element->width = width; + element->height = height; + element->text = src; + _richText->_richElements.push_back(element); + if (!_linkStack.empty()) + element->link = _linkStack.back(); + } + else if (strcasecmp(elementName, "a") == 0) + { + pushTextFormat(); + + std::string href; + ValueMap&& tagAttrValueMap = tagAttrMapWithXMLElement(atts); + auto it = tagAttrValueMap.find("href"); + if (it != tagAttrValueMap.end()) + href = it->second.asString(); + + FUIRichElement* element = new FUIRichElement(FUIRichElement::Type::LINK); + element->text = href; + _richText->_richElements.push_back(element); + _linkStack.push_back(element); + + if (_richText->_anchorTextUnderline) + _format.underline = true; + if (!_format._hasColor) + _format.color = _richText->_anchorFontColor; + } + else if (strcasecmp(elementName, "p") == 0 || strcasecmp(elementName, "ui") == 0 || strcasecmp(elementName, "div") == 0 + || strcasecmp(elementName, "li") == 0) + { + addNewLine(true); + } + else if (strcasecmp(elementName, "html") == 0 || strcasecmp(elementName, "body") == 0) + { + //full html + _ignoreWhiteSpace = true; + } + else if (strcasecmp(elementName, "head") == 0 || strcasecmp(elementName, "style") == 0 || strcasecmp(elementName, "script") == 0 + || strcasecmp(elementName, "form") == 0) + { + _skipText++; + } +} + +void FUIXMLVisitor::endElement(void* /*ctx*/, const char *elementName) +{ + finishTextBlock(); + + if (strcasecmp(elementName, "b") == 0 || strcasecmp(elementName, "i") == 0 || strcasecmp(elementName, "u") == 0 + || strcasecmp(elementName, "font") == 0) + { + popTextFormat(); + } + else if (strcasecmp(elementName, "a") == 0) + { + popTextFormat(); + + if (!_linkStack.empty()) + _linkStack.pop_back(); + } + else if (strcasecmp(elementName, "head") == 0 || strcasecmp(elementName, "style") == 0 || strcasecmp(elementName, "script") == 0 + || strcasecmp(elementName, "form") == 0) + { + _skipText--; + } +} +#pragma warning(default:4307) + +void FUIXMLVisitor::textHandler(void* /*ctx*/, const char *str, size_t len) +{ + if (_skipText != 0) + return; + + if (_ignoreWhiteSpace) + { + string s(str, len); + ltrim(s); + rtrim(s); + _textBlock += s; + } + else + _textBlock += string(str, len); +} + +ValueMap FUIXMLVisitor::tagAttrMapWithXMLElement(const char ** attrs) +{ + ValueMap tagAttrValueMap; + for (const char** attr = attrs; *attr != nullptr; attr = (attrs += 2)) { + if (attr[0] && attr[1]) { + tagAttrValueMap[attr[0]] = attr[1]; + } + } + return tagAttrValueMap; +} + +int FUIXMLVisitor::attributeInt(const ValueMap& valueMap, const std::string& key, int defaultValue) +{ + auto it = valueMap.find(key); + if (it != valueMap.end()) { + string str = it->second.asString(); + if (!str.empty() && str.back() == '%') + return ceil(atoi(str.substr(0, str.size() - 1).c_str()) / 100.0f*defaultValue); + else + return atoi(str.c_str()); + } + else + return defaultValue; +} + +FUIRichText::FUIRichText() : + _formatTextDirty(true), + _textChanged(false), + _leftSpaceWidth(0.0f), + _textRectWidth(0.0f), + _numLines(0), + _overflow(Label::Overflow::NONE), + _anchorTextUnderline(true), + _anchorFontColor(Color3B::BLUE), + _defaultTextFormat(new TextFormat()) +{ +} + +FUIRichText::~FUIRichText() +{ + for (auto &it : _richElements) + delete it; +} + +bool FUIRichText::init() +{ + if (!Node::init()) + return false; + + return true; +} + +void FUIRichText::setDimensions(float width, float height) +{ + if ((_numLines > 1 && width != _dimensions.width) || width < _contentSize.width) + _formatTextDirty = true; + _dimensions.setSize(width, height); +} + +void FUIRichText::setText(const std::string & value) +{ + _formatTextDirty = true; + _textChanged = true; + _text = value; +} + +void FUIRichText::applyTextFormat() +{ + _textChanged = true; + _formatTextDirty = true; +} + +void FUIRichText::setOverflow(cocos2d::Label::Overflow overflow) +{ + if (_overflow != overflow) + { + _overflow = overflow; + _formatTextDirty = true; + } +} + +const Size & FUIRichText::getContentSize() const +{ + if (_formatTextDirty) + const_cast(this)->formatText(); + + return Node::getContentSize(); +} + +void FUIRichText::setAnchorTextUnderline(bool enable) +{ + if (_anchorTextUnderline != enable) + { + _anchorTextUnderline = enable; + _formatTextDirty = true; + } +} + +void FUIRichText::setAnchorFontColor(const cocos2d::Color3B & color) +{ + _anchorFontColor = color; + _formatTextDirty = true; +} + +const char* FUIRichText::hitTestLink(const cocos2d::Vec2 & worldPoint) +{ + Rect rect; + for (auto &child : _children) + { + FUIRichElement* element = (FUIRichElement*)child->getUserData(); + if (!element || !element->link) + continue; + + rect.size = child->getContentSize(); + if (rect.containsPoint(child->convertToNodeSpace(worldPoint))) + return element->link->text.c_str(); + } + return nullptr; +} + +void FUIRichText::visit(cocos2d::Renderer * renderer, const cocos2d::Mat4 & parentTransform, uint32_t parentFlags) +{ + if (_visible) + formatText(); + + Node::visit(renderer, parentTransform, parentFlags); +} + +void FUIRichText::formatText() +{ + if (!_formatTextDirty) + return; + + if (_textChanged) + { + _textChanged = false; + _richElements.clear(); + _numLines = 0; + + if (!_text.empty()) + { + string xmlText = "" + _text + ""; + FUIXMLVisitor visitor(this); + SAXParser parser; + parser.setDelegator(&visitor); + parser.parseIntrusive(&xmlText.front(), xmlText.length()); + } + } + + removeAllChildrenWithCleanup(true); + _elementRenders.clear(); + _imageLoaders.clear(); + if (_overflow == Label::Overflow::NONE) + _textRectWidth = FLT_MAX; + else + _textRectWidth = _dimensions.width - GUTTER_X * 2; + + int size = (int)_richElements.size(); + if (size == 0) + { + formarRenderers(); + _formatTextDirty = false; + return; + } + + addNewLine(); + for (int i = 0; i < size; ++i) + { + FUIRichElement* element = static_cast(_richElements.at(i)); + switch (element->_type) + { + case FUIRichElement::Type::TEXT: + { + FastSplitter fs; + fs.start(element->text.c_str(), (int)element->text.size(), '\n'); + bool first = true; + while (fs.next()) + { + if (!first) + addNewLine(); + if (fs.getTextLength() > 0) + handleTextRenderer(element, element->textFormat, string(fs.getText(), fs.getTextLength())); + first = false; + } + break; + } + case FUIRichElement::Type::IMAGE: + handleImageRenderer(element); + break; + default: + break; + } + } + formarRenderers(); + _formatTextDirty = false; +} + +void FUIRichText::addNewLine() +{ + _leftSpaceWidth = _textRectWidth; + _elementRenders.emplace_back(); + _numLines++; +} + +void FUIRichText::handleTextRenderer(FUIRichElement* element, const TextFormat& format, const std::string& text) +{ + FUILabel* textRenderer = FUILabel::create(); + textRenderer->setCascadeOpacityEnabled(true); + textRenderer->getTextFormat()->setFormat(format); + textRenderer->applyTextFormat(); + textRenderer->setString(text); + textRenderer->setUserData(element); + + float textRendererWidth = textRenderer->getContentSize().width; + _leftSpaceWidth -= textRendererWidth; + if (_leftSpaceWidth >= 0) + { + _elementRenders.back().push_back(textRenderer); + return; + } + + int leftLength = findSplitPositionForWord(textRenderer, text); + + //The minimum cut length is 1, otherwise will cause the infinite loop. + if (0 == leftLength) leftLength = 1; + std::string leftWords = getSubStringOfUTF8String(text, 0, leftLength); + int rightStart = leftLength; + if (std::isspace(text[rightStart], std::locale())) + rightStart++; + std::string cutWords = getSubStringOfUTF8String(text, rightStart, text.length() - leftLength); + if (leftLength > 0) + { + FUILabel* leftRenderer = FUILabel::create(); + leftRenderer->setCascadeOpacityEnabled(true); + leftRenderer->getTextFormat()->setFormat(format); + leftRenderer->applyTextFormat(); + leftRenderer->setString(getSubStringOfUTF8String(leftWords, 0, leftLength)); + leftRenderer->setUserData(element); + _elementRenders.back().push_back(leftRenderer); + } + + if (cutWords.length() > 0) + { + addNewLine(); + handleTextRenderer(element, format, cutWords); + } +} + +int FUIRichText::findSplitPositionForWord(cocos2d::Label* label, const std::string& text) +{ + auto originalLeftSpaceWidth = _leftSpaceWidth + label->getContentSize().width; + + bool startingNewLine = (_textRectWidth == originalLeftSpaceWidth); + if (!isWrappable(text)) + { + if (startingNewLine) + return (int)text.length(); + return 0; + } + + for (int idx = (int)text.size() - 1; idx >= 0; ) + { + int newidx = getPrevWord(text, idx); + if (newidx >= 0) + { + idx = newidx; + auto leftStr = getSubStringOfUTF8String(text, 0, idx); + label->setString(leftStr); + if (label->getContentSize().width <= originalLeftSpaceWidth) + return idx; + } + else + { + if (startingNewLine) + return idx; + return 0; + } + } + + // no spaces... return the original label + size + label->setString(text); + return (int)text.size(); +} + +void FUIRichText::handleImageRenderer(FUIRichElement* element) +{ + GLoader* loader = GLoader::create(); + _imageLoaders.pushBack(loader); + loader->setSize(element->width, element->height); + loader->setFill(LoaderFillType::SCALE_FREE); + loader->setURL(element->text); + loader->displayObject()->setUserData(element); + + _leftSpaceWidth -= (element->width + 4); + if (_leftSpaceWidth < 0.0f) + { + addNewLine(); + _elementRenders.back().push_back(loader->displayObject()); + _leftSpaceWidth -= (element->width + 4); + } + else + { + _elementRenders.back().push_back(loader->displayObject()); + } +} + +void FUIRichText::formarRenderers() +{ + float nextPosY = GUTTER_Y; + float textWidth = 0; + float textHeight = 0; + for (auto& row : _elementRenders) + { + if (nextPosY != GUTTER_Y) + nextPosY += _defaultTextFormat->lineSpacing - 3; + + float nextPosX = GUTTER_X; + float lineHeight = 0.0f; + float lineTextHeight = 0.0f; + for (auto& node : row) + { + lineHeight = MAX(node->getContentSize().height, lineHeight); + if (((FUIRichElement*)node->getUserData())->_type == FUIRichElement::Type::TEXT) + lineTextHeight = MAX(node->getContentSize().height, lineTextHeight); + } + + nextPosY += lineHeight; + + for (auto& node : row) + { + node->setAnchorPoint(Vec2::ZERO); + int adjustment = 0; + if (((FUIRichElement*)node->getUserData())->_type == FUIRichElement::Type::IMAGE) + { + nextPosX += 2; + adjustment = floor((lineHeight - node->getContentSize().height) / 2); + } + else //text + { + adjustment = floor((lineHeight - lineTextHeight) / 2); + } + node->setPosition(nextPosX, _dimensions.height - nextPosY + adjustment); + this->addChild(node, 1); + nextPosX += node->getContentSize().width; + if (((FUIRichElement*)node->getUserData())->_type == FUIRichElement::Type::IMAGE) + nextPosX += 2; + } + nextPosX += GUTTER_X; + if (nextPosX > textWidth) + textWidth = nextPosX; + + if (_overflow != Label::Overflow::NONE) + doHorizontalAlignment(row, nextPosX); + } + if (textWidth == GUTTER_X + GUTTER_X) + textWidth = 0; + else if (_numLines > 1 || (_defaultTextFormat->align != TextHAlignment::LEFT && _overflow != Label::Overflow::NONE)) + textWidth = MAX(_dimensions.width, textWidth); + if (nextPosY != GUTTER_Y) + textHeight = nextPosY + GUTTER_Y; + else + textHeight = 0; + setContentSize(Size(textWidth, textHeight)); + + float oldDimensionsHeight = _dimensions.height; + if (_overflow == Label::Overflow::NONE) + _dimensions = _contentSize; + else if (_overflow == Label::Overflow::RESIZE_HEIGHT) + _dimensions.height = _contentSize.height; + float delta = _contentSize.height - oldDimensionsHeight; + if (_defaultTextFormat->verticalAlign == TextVAlignment::CENTER) + delta -= floor((_dimensions.height - textHeight) * 0.5f); + else if (_defaultTextFormat->verticalAlign == TextVAlignment::BOTTOM) + delta -= _dimensions.height - textHeight; + if (delta != 0) + { + Vec2 offset(0, delta); + for (auto& row : _elementRenders) + { + for (auto& node : row) + { + node->setPosition(node->getPosition() + offset); + } + } + } + + _elementRenders.clear(); +} + +void FUIRichText::doHorizontalAlignment(const std::vector &row, float rowWidth) { + if (_defaultTextFormat->align != TextHAlignment::LEFT) { + const auto diff = stripTrailingWhitespace(row); + const auto leftOver = _dimensions.width - (rowWidth + diff); + const float leftPadding = getPaddingAmount(_defaultTextFormat->align, leftOver); + const Vec2 offset(leftPadding, 0.f); + for (auto& node : row) { + node->setPosition(node->getPosition() + offset); + } + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/display/FUIRichText.h b/extensions/fairygui/display/FUIRichText.h new file mode 100644 index 0000000000..7a869b4251 --- /dev/null +++ b/extensions/fairygui/display/FUIRichText.h @@ -0,0 +1,74 @@ +#ifndef __FUIRICHTEXT_H__ +#define __FUIRICHTEXT_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" +#include "TextFormat.h" + +NS_FGUI_BEGIN + +class FUIXMLVisitor; +class GLoader; +class FUIRichElement; + +class FUIRichText : public cocos2d::Node +{ +public: + FUIRichText(); + virtual ~FUIRichText(); + + CREATE_FUNC(FUIRichText); + + const cocos2d::Size& getDimensions() const { return _dimensions; } + void setDimensions(float width, float height); + + void setText(const std::string& value); + + TextFormat* getTextFormat() const { return _defaultTextFormat; } + void applyTextFormat(); + + cocos2d::Label::Overflow getOverflow()const { return _overflow; } + void setOverflow(cocos2d::Label::Overflow overflow); + + bool isAnchorTextUnderline() { return _anchorTextUnderline; } + void setAnchorTextUnderline(bool enable); + + const cocos2d::Color3B& getAnchorFontColor() { return _anchorFontColor; } + void setAnchorFontColor(const cocos2d::Color3B& color); + + const char* hitTestLink(const cocos2d::Vec2& worldPoint); + virtual void visit(cocos2d::Renderer *renderer, const cocos2d::Mat4 &parentTransform, uint32_t parentFlags) override; + + virtual const cocos2d::Size& getContentSize() const override; + +protected: + virtual bool init() override; + void formatText(); + void formarRenderers(); + void handleTextRenderer(FUIRichElement* element, const TextFormat& format, const std::string& text); + void handleImageRenderer(FUIRichElement* element); + void addNewLine(); + int findSplitPositionForWord(cocos2d::Label* label, const std::string& text); + void doHorizontalAlignment(const std::vector& row, float rowWidth); + + std::vector _richElements; + std::vector> _elementRenders; + cocos2d::Vector _imageLoaders; + bool _formatTextDirty; + bool _textChanged; + cocos2d::Size _dimensions; + float _leftSpaceWidth; + float _textRectWidth; + int _numLines; + cocos2d::Label::Overflow _overflow; + TextFormat* _defaultTextFormat; + bool _anchorTextUnderline; + cocos2d::Color3B _anchorFontColor; + std::string _text; + + friend class FUIXMLVisitor; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/display/FUISprite.cpp b/extensions/fairygui/display/FUISprite.cpp new file mode 100644 index 0000000000..19cb3823bd --- /dev/null +++ b/extensions/fairygui/display/FUISprite.cpp @@ -0,0 +1,535 @@ +#include "FUISprite.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +#define kProgressTextureCoordsCount 4 +// kProgressTextureCoords holds points {0,1} {0,0} {1,0} {1,1} we can represent it as bits +const char kProgressTextureCoords = 0x4b; + +Texture2D* FUISprite::_empty = nullptr; + +FUISprite::FUISprite() + : _fillMethod(FillMethod::None), + _fillOrigin(FillOrigin::Left), + _fillAmount(0), + _fillClockwise(false), + _vertexDataCount(0), + _vertexData(nullptr), + _vertexIndex(nullptr), + _scaleByTile(false) +{ +} + +FUISprite::~FUISprite() +{ + CC_SAFE_FREE(_vertexData); + CC_SAFE_FREE(_vertexIndex); +} + +void FUISprite::clearContent() +{ + setTexture(nullptr); + CC_SAFE_RELEASE_NULL(_spriteFrame); + setCenterRectNormalized(Rect(0, 0, 1, 1)); + + _empty = _texture; +} + +void FUISprite::setScale9Grid(Rect* value) +{ + if (value == nullptr) + { + setCenterRectNormalized(Rect(0, 0, 1, 1)); + return; + } + + Rect insets = *value; + + // When Insets == Zero --> we should use a 1/3 of its untrimmed size + if (insets.equals(Rect::ZERO)) + { + insets = Rect(_originalContentSize.width / 3.0f, + _originalContentSize.height / 3.0f, + _originalContentSize.width / 3.0f, + _originalContentSize.height / 3.0f); + } + + // emulate invalid insets. shouldn't be supported, but the original code supported it. + if (insets.origin.x > _originalContentSize.width) + insets.origin.x = 0; + if (insets.origin.y > _originalContentSize.height) + insets.origin.y = 0; + if (insets.size.width > _originalContentSize.width) + insets.size.width = 1; + if (insets.size.height > _originalContentSize.height) + insets.size.height = 1; + + // we have to convert from untrimmed to trimmed + // Sprite::setCenterRect is using trimmed values (to be compatible with Cocos Creator) + // Scale9Sprite::setCapInsects uses untrimmed values (which makes more sense) + + // use _rect coordinates. recenter origin to calculate the + // intersecting rectangle + // can't use _offsetPosition since it is calculated using bottom-left as origin, + // and the center rect is calculated using top-left + insets.origin.x -= (_originalContentSize.width - _rect.size.width) / 2 + _unflippedOffsetPositionFromCenter.x; + insets.origin.y -= (_originalContentSize.height - _rect.size.height) / 2 - _unflippedOffsetPositionFromCenter.y; + + // intersecting rectangle + const float x1 = std::max(insets.origin.x, 0.0f); + const float y1 = std::max(insets.origin.y, 0.0f); + const float x2 = std::min(insets.origin.x + insets.size.width, 0.0f + _rect.size.width); + const float y2 = std::min(insets.origin.y + insets.size.height, 0.0f + _rect.size.height); + + // centerRect uses the trimmed frame origin as 0,0. + // so, recenter inset rect + insets.setRect(x1, + y1, + x2 - x1, + y2 - y1); + + // Only update center rect while in slice mode. + setCenterRect(insets); +} + +void FUISprite::setScaleByTile(bool value) +{ + _scaleByTile = value; +} + +void FUISprite::setGrayed(bool value) +{ +#if defined(ENGINEX_VERSION) + auto isETC1 = getTexture(); + Sprite::setProgramState(cocos2d::backend::ProgramType::POSITION_TEXTURE); +#elif COCOS2D_VERSION >= 0x00040000 + auto isETC1 = getTexture() && getTexture()->getAlphaTextureName(); + if (value) { + Sprite::updateShaders(positionTextureColor_vert, (isETC1)?etc1Gray_frag:grayScale_frag); + } else { + Sprite::updateShaders(positionTextureColor_vert, (isETC1)?etc1_frag:positionTextureColor_frag); + } +#else + GLProgramState* glState = nullptr; + if (value) + glState = GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_GRAYSCALE, getTexture()); + else + glState = GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR_NO_MVP, getTexture()); + + setGLProgramState(glState); +#endif +} + +void FUISprite::setFillMethod(FillMethod value) +{ + if (_fillMethod != value) + { + _fillMethod = value; + + if (_fillMethod != FillMethod::None) + setupFill(); + else + { + CC_SAFE_FREE(_vertexData); + CC_SAFE_FREE(_vertexIndex); + } + } +} + +void FUISprite::setFillOrigin(FillOrigin value) +{ + if (_fillOrigin != value) + { + _fillOrigin = value; + + if (_fillMethod != FillMethod::None) + setupFill(); + } +} + +void FUISprite::setFillClockwise(bool value) +{ + if (_fillClockwise != value) + { + _fillClockwise = value; + + if (_fillMethod != FillMethod::None) + setupFill(); + } +} + +void FUISprite::setFillAmount(float value) +{ + if (_fillAmount != value) + { + _fillAmount = value; + + if (_fillMethod != FillMethod::None) + setupFill(); + } +} + +void FUISprite::setContentSize(const Size& size) +{ + if (_scaleByTile) + setTextureRect(Rect(Vec2::ZERO, size)); + else + Sprite::setContentSize(size); +} + +void FUISprite::setupFill() +{ + if (_fillMethod == FillMethod::Horizontal || _fillMethod == FillMethod::Vertical) + updateBar(); + else + updateRadial(); +} + +//from CCFUISprite.h +/// +// @returns the vertex position from the texture coordinate +/// +Tex2F FUISprite::textureCoordFromAlphaPoint(Vec2 alpha) +{ + Tex2F ret(0.0f, 0.0f); + + V3F_C4B_T2F_Quad quad = getQuad(); + Vec2 min(quad.bl.texCoords.u, quad.bl.texCoords.v); + Vec2 max(quad.tr.texCoords.u, quad.tr.texCoords.v); + // Fix bug #1303 so that progress timer handles sprite frame texture rotation + if (isTextureRectRotated()) + { + std::swap(alpha.x, alpha.y); + } + return Tex2F(min.x * (1.f - alpha.x) + max.x * alpha.x, min.y * (1.f - alpha.y) + max.y * alpha.y); +} + +Vec3 FUISprite::vertexFromAlphaPoint(Vec2 alpha) +{ + Vec3 ret(0.0f, 0.0f, 0.0f); + + V3F_C4B_T2F_Quad quad = getQuad(); + Vec2 min(quad.bl.vertices.x, quad.bl.vertices.y); + Vec2 max(quad.tr.vertices.x, quad.tr.vertices.y); + ret.x = min.x * (1.f - alpha.x) + max.x * alpha.x; + ret.y = min.y * (1.f - alpha.y) + max.y * alpha.y; + return ret; +} + +void FUISprite::updateColor(void) +{ + Sprite::updateColor(); + + if (_vertexData) + { + Color4B sc = getQuad().tl.colors; + for (int i = 0; i < _vertexDataCount; ++i) + { + _vertexData[i].colors = sc; + } + } +} + +/// +// Update does the work of mapping the texture onto the triangles +// It now doesn't occur the cost of free/alloc data every update cycle. +// It also only changes the percentage point but no other points if they have not +// been modified. +// +// It now deals with flipped texture. If you run into this problem, just use the +// sprite property and enable the methods flipX, flipY. +/// +void FUISprite::updateRadial(void) +{ + float angle = 2.f * ((float)M_PI) * (_fillClockwise ? (1.0f - _fillAmount) : _fillAmount); + + // We find the vector to do a hit detection based on the percentage + // We know the first vector is the one @ 12 o'clock (top,mid) so we rotate + // from that by the progress angle around the _midpoint pivot + Vec2 midpoint(0.5f, 0.5f); + Vec2 topMid(0.5f, 1.f); + Vec2 percentagePt = topMid.rotateByAngle(midpoint, angle); + + int index = 0; + Vec2 hit; + + if (_fillAmount == 0.f) + { + // More efficient since we don't always need to check intersection + // If the alpha is zero then the hit point is top mid and the index is 0. + hit = topMid; + index = 0; + } + else if (_fillAmount == 1.f) + { + // More efficient since we don't always need to check intersection + // If the alpha is one then the hit point is top mid and the index is 4. + hit = topMid; + index = 4; + } + else + { + // We run a for loop checking the edges of the texture to find the + // intersection point + // We loop through five points since the top is split in half + + float min_t = FLT_MAX; + + for (int i = 0; i <= kProgressTextureCoordsCount; ++i) + { + int pIndex = (i + (kProgressTextureCoordsCount - 1)) % kProgressTextureCoordsCount; + + Vec2 edgePtA = boundaryTexCoord(i % kProgressTextureCoordsCount); + Vec2 edgePtB = boundaryTexCoord(pIndex); + + // Remember that the top edge is split in half for the 12 o'clock position + // Let's deal with that here by finding the correct endpoints + if (i == 0) + { + edgePtB = edgePtA.lerp(edgePtB, 1 - midpoint.x); + } + else if (i == 4) + { + edgePtA = edgePtA.lerp(edgePtB, 1 - midpoint.x); + } + + // s and t are returned by ccpLineIntersect + float s = 0, t = 0; + if (Vec2::isLineIntersect(edgePtA, edgePtB, midpoint, percentagePt, &s, &t)) + { + + // Since our hit test is on rays we have to deal with the top edge + // being in split in half so we have to test as a segment + if ((i == 0 || i == 4)) + { + // s represents the point between edgePtA--edgePtB + if (!(0.f <= s && s <= 1.f)) + { + continue; + } + } + // As long as our t isn't negative we are at least finding a + // correct hitpoint from _midpoint to percentagePt. + if (t >= 0.f) + { + // Because the percentage line and all the texture edges are + // rays we should only account for the shortest intersection + if (t < min_t) + { + min_t = t; + index = i; + } + } + } + } + + // Now that we have the minimum magnitude we can use that to find our intersection + hit = midpoint + ((percentagePt - midpoint) * min_t); + } + + // The size of the vertex data is the index from the hitpoint + // the 3 is for the _midpoint, 12 o'clock point and hitpoint position. + + bool sameIndexCount = true; + int triangleCount = 0; + if (_vertexDataCount != index + 3) + { + sameIndexCount = false; + CC_SAFE_FREE(_vertexData); + CC_SAFE_FREE(_vertexIndex); + _vertexDataCount = 0; + } + + if (!_vertexData) + { + _vertexDataCount = index + 3; + triangleCount = _vertexDataCount - 2; + _vertexData = (V3F_C4B_T2F*)malloc(_vertexDataCount * sizeof(*_vertexData)); + _vertexIndex = (unsigned short *)malloc(triangleCount * 3 * sizeof(*_vertexIndex)); + CCASSERT(_vertexData, "FUISprite. Not enough memory"); + } + else + { + triangleCount = _vertexDataCount - 2; + } + + updateColor(); + + if (!sameIndexCount) + { + + // First we populate the array with the _midpoint, then all + // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint + _vertexData[0].texCoords = textureCoordFromAlphaPoint(midpoint); + _vertexData[0].vertices = vertexFromAlphaPoint(midpoint); + + _vertexData[1].texCoords = textureCoordFromAlphaPoint(topMid); + _vertexData[1].vertices = vertexFromAlphaPoint(topMid); + + for (int i = 0; i < index; ++i) + { + Vec2 alphaPoint = boundaryTexCoord(i); + _vertexData[i + 2].texCoords = textureCoordFromAlphaPoint(alphaPoint); + _vertexData[i + 2].vertices = vertexFromAlphaPoint(alphaPoint); + } + } + + // hitpoint will go last + _vertexData[_vertexDataCount - 1].texCoords = textureCoordFromAlphaPoint(hit); + _vertexData[_vertexDataCount - 1].vertices = vertexFromAlphaPoint(hit); + + for (int i = 0; i < triangleCount; i++) { + _vertexIndex[i * 3] = 0; + _vertexIndex[i * 3 + 1] = i + 1; + _vertexIndex[i * 3 + 2] = i + 2; + } + + _fillTriangles.verts = _vertexData; + _fillTriangles.vertCount = _vertexDataCount; + _fillTriangles.indices = _vertexIndex; + _fillTriangles.indexCount = triangleCount * 3; +} + +/// +// Update does the work of mapping the texture onto the triangles for the bar +// It now doesn't occur the cost of free/alloc data every update cycle. +// It also only changes the percentage point but no other points if they have not +// been modified. +// +// It now deals with flipped texture. If you run into this problem, just use the +// sprite property and enable the methods flipX, flipY. +/// +void FUISprite::updateBar(void) +{ + Vec2 min, max; + + if (_fillMethod == FillMethod::Horizontal) + { + if (_fillOrigin == FillOrigin::Left || _fillOrigin == FillOrigin::Top) + { + min = Vec2(0, 0); + max = Vec2(_fillAmount, 1); + } + else + { + min = Vec2(1 - _fillAmount, 0); + max = Vec2(1, 1); + } + } + else + { + if (_fillOrigin == FillOrigin::Left || _fillOrigin == FillOrigin::Top) + { + min = Vec2(0, 1 - _fillAmount); + max = Vec2(1, 1); + } + else + { + min = Vec2(0, 0); + max = Vec2(1, _fillAmount); + } + } + + if (!_vertexData) + { + _vertexDataCount = 4; + _vertexData = (V3F_C4B_T2F*)malloc(_vertexDataCount * sizeof(*_vertexData)); + _vertexIndex = (unsigned short*)malloc(6 * sizeof(*_vertexIndex)); + CCASSERT(_vertexData, "FUISprite. Not enough memory"); + } + // TOPLEFT + _vertexData[0].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, max.y)); + _vertexData[0].vertices = vertexFromAlphaPoint(Vec2(min.x, max.y)); + + // BOTLEFT + _vertexData[1].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, min.y)); + _vertexData[1].vertices = vertexFromAlphaPoint(Vec2(min.x, min.y)); + + // TOPRIGHT + _vertexData[2].texCoords = textureCoordFromAlphaPoint(Vec2(max.x, max.y)); + _vertexData[2].vertices = vertexFromAlphaPoint(Vec2(max.x, max.y)); + + // BOTRIGHT + _vertexData[3].texCoords = textureCoordFromAlphaPoint(Vec2(max.x, min.y)); + _vertexData[3].vertices = vertexFromAlphaPoint(Vec2(max.x, min.y)); + + _vertexIndex[0] = 0; + _vertexIndex[1] = 1; + _vertexIndex[2] = 2; + _vertexIndex[3] = 2; + _vertexIndex[4] = 1; + _vertexIndex[5] = 3; + + _fillTriangles.verts = _vertexData; + _fillTriangles.vertCount = 4; + _fillTriangles.indices = _vertexIndex; + _fillTriangles.indexCount = 6; + + updateColor(); +} + +Vec2 FUISprite::boundaryTexCoord(char index) +{ + if (index < kProgressTextureCoordsCount) + { + if (!_fillClockwise) + { + return Vec2((kProgressTextureCoords >> (7 - (index << 1))) & 1, (kProgressTextureCoords >> (7 - ((index << 1) + 1))) & 1); + } + else + { + return Vec2((kProgressTextureCoords >> ((index << 1) + 1)) & 1, (kProgressTextureCoords >> (index << 1)) & 1); + } + } + return Vec2::ZERO; +} + +void FUISprite::draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags) +{ + if (_texture == _empty) + return; + + if (_fillMethod == FillMethod::None) + Sprite::draw(renderer, transform, flags); + else + { +#if COCOS2D_VERSION >= 0x00040000 + setMVPMatrixUniform(); +#endif + +#if CC_USE_CULLING + // Don't calculate the culling if the transform was not updated + auto visitingCamera = Camera::getVisitingCamera(); + auto defaultCamera = Camera::getDefaultCamera(); + if (visitingCamera == nullptr) { + _insideBounds = true; + } + else if (visitingCamera == defaultCamera) { + _insideBounds = ((flags & FLAGS_TRANSFORM_DIRTY) || visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds; + } + else + { + // XXX: this always return true since + _insideBounds = renderer->checkVisibility(transform, _contentSize); + } + + if(_insideBounds) +#endif + { + _trianglesCommand.init(_globalZOrder, + _texture, +#if COCOS2D_VERSION < 0x00040000 + getGLProgramState(), +#endif + _blendFunc, + _fillTriangles, + transform, + flags); + + renderer->addCommand(&_trianglesCommand); + } + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/display/FUISprite.h b/extensions/fairygui/display/FUISprite.h new file mode 100644 index 0000000000..d93dc5c8f0 --- /dev/null +++ b/extensions/fairygui/display/FUISprite.h @@ -0,0 +1,66 @@ +#ifndef __FUISPRITE_H__ +#define __FUISPRITE_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +class FUISprite : public cocos2d::Sprite +{ +public: + FUISprite(); + virtual ~FUISprite(); + + CREATE_FUNC(FUISprite); + + void clearContent(); + void setScale9Grid(cocos2d::Rect* value); + void setGrayed(bool value); + + FillMethod getFillMethod() const { return _fillMethod; } + void setFillMethod(FillMethod value); + + FillOrigin getFillOrigin() const { return _fillOrigin; } + void setFillOrigin(FillOrigin value); + + bool isFillClockwise() const { return _fillClockwise; } + void setFillClockwise(bool value); + + float getFillAmount() const { return _fillAmount; } + void setFillAmount(float value); + + bool isScaleByTile() const { return _scaleByTile; } + void setScaleByTile(bool value); + + virtual void setContentSize(const cocos2d::Size& size) override; + +protected: + virtual void draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) override; + + cocos2d::Tex2F textureCoordFromAlphaPoint(cocos2d::Vec2 alpha); + cocos2d::Vec3 vertexFromAlphaPoint(cocos2d::Vec2 alpha); + void updateBar(void); + void updateRadial(void); + virtual void updateColor(void) override; + cocos2d::Vec2 boundaryTexCoord(char index); + + void setupFill(); + +private: + FillMethod _fillMethod; + FillOrigin _fillOrigin; + float _fillAmount; + bool _fillClockwise; + bool _scaleByTile; + int _vertexDataCount; + cocos2d::TrianglesCommand::Triangles _fillTriangles; + cocos2d::V3F_C4B_T2F *_vertexData; + unsigned short *_vertexIndex; + + static cocos2d::Texture2D* _empty; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/display/TextFormat.cpp b/extensions/fairygui/display/TextFormat.cpp new file mode 100644 index 0000000000..c748d033ff --- /dev/null +++ b/extensions/fairygui/display/TextFormat.cpp @@ -0,0 +1,59 @@ +#include "TextFormat.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +TextFormat::TextFormat() : + fontSize(12), + color(Color3B::BLACK), + bold(false), + italics(false), + underline(false), + lineSpacing(3), + letterSpacing(0), + align(TextHAlignment::LEFT), + verticalAlign(TextVAlignment::TOP), + effect(0), + outlineSize(1), + shadowBlurRadius(0), + _hasColor(false) +{ +} + +TextFormat::TextFormat(const TextFormat & other) +{ + *this = other; +} + +TextFormat & TextFormat::operator=(const TextFormat & other) +{ + if (this != &other) + { + face = other.face; + fontSize = other.fontSize; + color = other.color; + bold = other.bold; + italics = other.italics; + underline = other.underline; + lineSpacing = other.lineSpacing; + letterSpacing = other.letterSpacing; + align = other.align; + verticalAlign = other.verticalAlign; + effect = other.effect; + outlineColor = other.outlineColor; + outlineSize = other.outlineSize; + shadowColor = other.shadowColor; + shadowOffset = other.shadowOffset; + shadowBlurRadius = other.shadowBlurRadius; + glowColor = other.glowColor; + _hasColor = other._hasColor; + } + return *this; +} + +void TextFormat::setFormat(const TextFormat & format) +{ + *this = format; +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/display/TextFormat.h b/extensions/fairygui/display/TextFormat.h new file mode 100644 index 0000000000..d4f5f8b900 --- /dev/null +++ b/extensions/fairygui/display/TextFormat.h @@ -0,0 +1,50 @@ +#ifndef __TEXTFORMAT_H__ +#define __TEXTFORMAT_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +class TextFormat +{ +public: + TextFormat(); + TextFormat(const TextFormat & other); + TextFormat &operator =(const TextFormat & other); + + void setFormat(const TextFormat& format); + void enableEffect(int effectFlag) { effect |= effectFlag; } + void disableEffect(int effectFlag) { effect &= ~effectFlag; } + bool hasEffect(int effectFlag) const { return (effect & effectFlag) != 0; } + + static const int OUTLINE = 1; + static const int SHADOW = 2; + static const int GLOW = 4; + + std::string face; + float fontSize; + cocos2d::Color3B color; + bool bold; + bool italics; + bool underline; + int lineSpacing; + int letterSpacing; + cocos2d::TextHAlignment align; + cocos2d::TextVAlignment verticalAlign; + + int effect; + cocos2d::Color3B outlineColor; + int outlineSize; + cocos2d::Color3B shadowColor; + cocos2d::Size shadowOffset; + int shadowBlurRadius; + cocos2d::Color3B glowColor; + + //internal use + bool _hasColor; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/event/EventContext.cpp b/extensions/fairygui/event/EventContext.cpp new file mode 100644 index 0000000000..b5aa155c87 --- /dev/null +++ b/extensions/fairygui/event/EventContext.cpp @@ -0,0 +1,23 @@ +#include "EventContext.h" +#include "GObject.h" + +NS_FGUI_BEGIN + +EventContext::EventContext() : + _sender(nullptr), + _data(nullptr), + _inputEvent(nullptr), + _isStopped(false), + _defaultPrevented(false), + _touchCapture(0), + _type(0) +{ +} + +EventContext::~EventContext() +{ + +} + + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/event/EventContext.h b/extensions/fairygui/event/EventContext.h new file mode 100644 index 0000000000..dc56d1a4c5 --- /dev/null +++ b/extensions/fairygui/event/EventContext.h @@ -0,0 +1,46 @@ +#ifndef __EVENTCONTEXT_H__ +#define __EVENTCONTEXT_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "InputEvent.h" + +NS_FGUI_BEGIN + +class GObject; +class InputProcessor; + +class EventContext +{ +public: + EventContext(); + ~EventContext(); + + int getType() const { return _type; } + cocos2d::Ref* getSender() const { return _sender; } + InputEvent* getInput() const { return _inputEvent; } + void stopPropagation() { _isStopped = true; } + void preventDefault() { _defaultPrevented = true; } + bool isDefaultPrevented() { return _defaultPrevented; } + void captureTouch() { _touchCapture = 1; } + void uncaptureTouch() { _touchCapture = 2; } + + const cocos2d::Value& getDataValue() const { return _dataValue; } + void* getData() const { return _data; } + +private: + cocos2d::Ref* _sender; + InputEvent* _inputEvent; + cocos2d::Value _dataValue; + void* _data; + bool _isStopped; + bool _defaultPrevented; + int _touchCapture; + int _type; + + friend class UIEventDispatcher; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/event/HitTest.cpp b/extensions/fairygui/event/HitTest.cpp new file mode 100644 index 0000000000..36b59f2e01 --- /dev/null +++ b/extensions/fairygui/event/HitTest.cpp @@ -0,0 +1,58 @@ +#include "HitTest.h" +#include "GComponent.h" +#include "utils/ByteBuffer.h" + +USING_NS_CC; +NS_FGUI_BEGIN + +PixelHitTestData::PixelHitTestData() : + pixels(nullptr), + pixelsLength(0), + pixelWidth(0), + scale(1) +{ +} + +PixelHitTestData::~PixelHitTestData() +{ + CC_SAFE_DELETE(pixels); +} + +void PixelHitTestData::load(ByteBuffer* buffer) +{ + buffer->skip(4); + pixelWidth = buffer->readInt(); + scale = 1.0f / buffer->readByte(); + pixelsLength = buffer->readInt(); + pixels = new unsigned char[pixelsLength]; + for (size_t i = 0; i < pixelsLength; i++) + pixels[i] = buffer->readByte(); +} + +PixelHitTest::PixelHitTest(PixelHitTestData * data, int offsetX, int offsetY) : + offsetX(offsetX), + offsetY(offsetY), + scaleX(1), + scaleY(1), + _data(data) +{ +} + +bool PixelHitTest::hitTest(GComponent * obj, const cocos2d::Vec2 & localPoint) +{ + int x = floor((localPoint.x / scaleX - offsetX) * _data->scale); + int y = floor(((obj->getHeight() - localPoint.y) / scaleY - offsetY) * _data->scale); + if (x < 0 || y < 0 || x >= _data->pixelWidth) + return false; + + ssize_t pos = y * _data->pixelWidth + x; + ssize_t pos2 = pos / 8; + ssize_t pos3 = pos % 8; + + if (pos2 >= 0 && pos2 < (ssize_t)_data->pixelsLength) + return ((_data->pixels[pos2] >> pos3) & 0x1) > 0; + else + return false; +} + +NS_FGUI_END diff --git a/extensions/fairygui/event/HitTest.h b/extensions/fairygui/event/HitTest.h new file mode 100644 index 0000000000..4e8a36c7f0 --- /dev/null +++ b/extensions/fairygui/event/HitTest.h @@ -0,0 +1,53 @@ +#ifndef __HITTEST_H__ +#define __HITTEST_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GComponent; +class ByteBuffer; + +class IHitTest +{ +public: + virtual bool hitTest(GComponent* obj, const cocos2d::Vec2& localPoint) + { + return true; + } +}; + +class PixelHitTestData +{ +public: + int pixelWidth; + float scale; + unsigned char* pixels; + size_t pixelsLength; + + PixelHitTestData(); + ~PixelHitTestData(); + + void load(ByteBuffer* buffer); +}; + +class PixelHitTest : public IHitTest +{ +public: + PixelHitTest(PixelHitTestData* data, int offsetX, int offsetY); + + virtual bool hitTest(GComponent* obj, const cocos2d::Vec2& localPoint) override; + + int offsetX; + int offsetY; + float scaleX; + float scaleY; + +private: + PixelHitTestData* _data; +}; + +NS_FGUI_END + +#endif \ No newline at end of file diff --git a/extensions/fairygui/event/InputEvent.cpp b/extensions/fairygui/event/InputEvent.cpp new file mode 100644 index 0000000000..eb15ada058 --- /dev/null +++ b/extensions/fairygui/event/InputEvent.cpp @@ -0,0 +1,25 @@ +#include "InputEvent.h" +#include "GObject.h" + +NS_FGUI_BEGIN + +InputEvent::InputEvent() : + _target(nullptr), + _touch(nullptr), + _inputProcessor(nullptr), + _touchId(-1), + _clickCount(0), + _mouseWheelDelta(0), + _button(cocos2d::EventMouse::MouseButton::BUTTON_UNSET), + _keyCode(cocos2d::EventKeyboard::KeyCode::KEY_0), + _keyModifiers(0) +{ +} + +InputEvent::~InputEvent() +{ + +} + + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/event/InputEvent.h b/extensions/fairygui/event/InputEvent.h new file mode 100644 index 0000000000..1d136836a3 --- /dev/null +++ b/extensions/fairygui/event/InputEvent.h @@ -0,0 +1,51 @@ +#ifndef __INPUTEVENT_H__ +#define __INPUTEVENT_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; +class InputProcessor; + +class InputEvent +{ +public: + InputEvent(); + ~InputEvent(); + + GObject* getTarget() const { return _target; } + const int getX() const { return _pos.x; } + const int getY() const { return _pos.y; } + const cocos2d::Vec2& getPosition() const { return _pos; } + cocos2d::Touch* getTouch()const { return _touch; } + int getTouchId()const { return _touchId; } + int isDoubleClick()const { return _clickCount == 2; } + cocos2d::EventMouse::MouseButton getButton() const { return _button; } + cocos2d::EventKeyboard::KeyCode getKeyCode() const { return _keyCode; } + bool isCtrlDown() const { return (_keyModifiers & 1)!=0; } + bool isAltDown() const { return (_keyModifiers & 2) != 0; } + bool isShiftDown() const { return (_keyModifiers & 4) != 0; } + int getMouseWheelDelta() const { return _mouseWheelDelta; } + + InputProcessor* getProcessor() const { return _inputProcessor; } + +private: + GObject* _target; + cocos2d::Touch* _touch; + cocos2d::Vec2 _pos; + int _touchId; + int _clickCount; + int _mouseWheelDelta; + cocos2d::EventMouse::MouseButton _button; + cocos2d::EventKeyboard::KeyCode _keyCode; + uint16_t _keyModifiers; + InputProcessor* _inputProcessor; + + friend class InputProcessor; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/event/InputProcessor.cpp b/extensions/fairygui/event/InputProcessor.cpp new file mode 100644 index 0000000000..ee8effc31a --- /dev/null +++ b/extensions/fairygui/event/InputProcessor.cpp @@ -0,0 +1,754 @@ +#include "InputProcessor.h" +#include "GComponent.h" +#include "InputEvent.h" +#include "GRoot.h" +#include "GRichTextField.h" +#include "GTextInput.h" +#include "utils/WeakPtr.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +InputProcessor* InputProcessor::_activeProcessor = nullptr; +bool InputProcessor::_touchOnUI = false; +unsigned int InputProcessor::_touchOnUIFlagFrameId = 0; + +class TouchInfo +{ +public: + TouchInfo(); + ~TouchInfo(); + + void reset(); + + cocos2d::Touch* touch; + cocos2d::Vec2 pos; + int touchId; + int clickCount; + int mouseWheelDelta; + cocos2d::EventMouse::MouseButton button; + cocos2d::Vec2 downPos; + bool began; + bool clickCancelled; + clock_t lastClickTime; + WeakPtr lastRollOver; + std::vector downTargets; + std::vector touchMonitors; +}; + +InputProcessor::InputProcessor(GComponent* owner) : + _keyModifiers(0), + _mouseListener(nullptr), + _touchListener(nullptr), + _keyboardListener(nullptr) +{ + _owner = owner; + _recentInput._inputProcessor = this; + +#ifdef CC_PLATFORM_PC + _mouseListener = EventListenerMouse::create(); + CC_SAFE_RETAIN(_mouseListener); + _mouseListener->onMouseDown = CC_CALLBACK_1(InputProcessor::onMouseDown, this); + _mouseListener->onMouseUp = CC_CALLBACK_1(InputProcessor::onMouseUp, this); + _mouseListener->onMouseMove = CC_CALLBACK_1(InputProcessor::onMouseMove, this); + _mouseListener->onMouseScroll = CC_CALLBACK_1(InputProcessor::onMouseScroll, this); + _owner->displayObject()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(_mouseListener, _owner->displayObject()); +#endif + + _touchListener = EventListenerTouchOneByOne::create(); + CC_SAFE_RETAIN(_touchListener); + _touchListener->setSwallowTouches(false); + _touchListener->onTouchBegan = CC_CALLBACK_2(InputProcessor::onTouchBegan, this); + _touchListener->onTouchMoved = CC_CALLBACK_2(InputProcessor::onTouchMoved, this); + _touchListener->onTouchEnded = CC_CALLBACK_2(InputProcessor::onTouchEnded, this); + _touchListener->onTouchCancelled = CC_CALLBACK_2(InputProcessor::onTouchCancelled, this); + _owner->displayObject()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(_touchListener, _owner->displayObject()); + + _keyboardListener = EventListenerKeyboard::create(); + CC_SAFE_RETAIN(_keyboardListener); + _keyboardListener->onKeyPressed = CC_CALLBACK_2(InputProcessor::onKeyDown, this); + _keyboardListener->onKeyReleased = CC_CALLBACK_2(InputProcessor::onKeyUp, this); + _owner->displayObject()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(_keyboardListener, _owner->displayObject()); +} + +InputProcessor::~InputProcessor() +{ +#ifdef CC_PLATFORM_PC + _owner->displayObject()->getEventDispatcher()->removeEventListener(_mouseListener); +#endif + _owner->displayObject()->getEventDispatcher()->removeEventListener(_touchListener); + _owner->displayObject()->getEventDispatcher()->removeEventListener(_keyboardListener); + CC_SAFE_RELEASE_NULL(_touchListener); + CC_SAFE_RELEASE_NULL(_mouseListener); + CC_SAFE_RELEASE_NULL(_keyboardListener); + + for (auto &ti : _touches) + delete ti; +} + +cocos2d::Vec2 InputProcessor::getTouchPosition(int touchId) +{ + for (auto &ti : _touches) + { + if (ti->touchId == touchId) + return ti->pos; + } + return _recentInput.getPosition(); +} + +TouchInfo* InputProcessor::getTouch(int touchId, bool createIfNotExisits) +{ + TouchInfo* ret = nullptr; + for (auto &ti : _touches) + { + if (ti->touchId == touchId) + return ti; + else if (ti->touchId == -1) + ret = ti; + } + + if (!ret) + { + if (!createIfNotExisits) + return nullptr; + + ret = new TouchInfo(); + _touches.push_back(ret); + } + ret->touchId = touchId; + return ret; +} + +void InputProcessor::updateRecentInput(TouchInfo* ti, GObject* target) +{ + _recentInput._pos.x = (int)ti->pos.x; + _recentInput._pos.y = (int)ti->pos.y; + _recentInput._target = target; + _recentInput._clickCount = ti->clickCount; + _recentInput._button = ti->button; + _recentInput._mouseWheelDelta = ti->mouseWheelDelta; + _recentInput._touch = ti->touch; + _recentInput._touchId = ti->touch ? ti->touchId : -1; + + int curFrame = Director::getInstance()->getTotalFrames(); + bool flag = target != _owner; + if (curFrame != _touchOnUIFlagFrameId) + _touchOnUI = flag; + else if (flag) + _touchOnUI = true; + _touchOnUIFlagFrameId = curFrame; +} + +void InputProcessor::handleRollOver(TouchInfo* touch, GObject* target) +{ + if (touch->lastRollOver == target) + return; + + std::vector rollOutChain; + std::vector rollOverChain; + GObject* element = touch->lastRollOver.ptr(); + while (element != nullptr) + { + rollOutChain.push_back(WeakPtr(element)); + element = element->findParent(); + } + + element = target; + while (element != nullptr) + { + auto iter = std::find(rollOutChain.cbegin(), rollOutChain.cend(), element); + if (iter != rollOutChain.cend()) + { + rollOutChain.resize(iter - rollOutChain.cbegin()); + break; + } + rollOverChain.push_back(WeakPtr(element)); + + element = element->findParent(); + } + + touch->lastRollOver = target; + + for (auto &wptr : rollOutChain) + { + element = wptr.ptr(); + if (element && element->onStage()) + element->dispatchEvent(UIEventType::RollOut); + } + + for (auto &wptr : rollOverChain) + { + element = wptr.ptr(); + if (element && element->onStage()) + element->dispatchEvent(UIEventType::RollOver); + } +} + +void InputProcessor::addTouchMonitor(int touchId, GObject * target) +{ + TouchInfo* ti = getTouch(touchId, false); + if (!ti) + return; + + auto it = std::find(ti->touchMonitors.cbegin(), ti->touchMonitors.cend(), target); + if (it == ti->touchMonitors.cend()) + ti->touchMonitors.push_back(WeakPtr(target)); +} + +void InputProcessor::removeTouchMonitor(GObject * target) +{ + for (auto it = _touches.cbegin(); it != _touches.cend(); ++it) + { + auto it2 = std::find((*it)->touchMonitors.begin(), (*it)->touchMonitors.end(), target); + if (it2 != (*it)->touchMonitors.end()) + *it2 = nullptr; + } +} + +void InputProcessor::cancelClick(int touchId) +{ + TouchInfo* ti = getTouch(touchId, false); + if (ti) + ti->clickCancelled = true; +} + +void InputProcessor::simulateClick(GObject* target, int touchId) +{ + _activeProcessor = this; + + Vec2 pt = target->localToGlobal(Vec2::ZERO); + _recentInput._pos.x = pt.x; + _recentInput._pos.y = pt.y; + _recentInput._target = target; + _recentInput._clickCount = 1; + _recentInput._button = EventMouse::MouseButton::BUTTON_LEFT; + _recentInput._touch = nullptr; + _recentInput._touchId = touchId; + + if (_captureCallback) + _captureCallback(UIEventType::TouchBegin); + + WeakPtr wptr(target); + target->bubbleEvent(UIEventType::TouchBegin); + + target = wptr.ptr(); + if (target) + { + target->bubbleEvent(UIEventType::TouchEnd); + + target = wptr.ptr(); + if (target) + target->bubbleEvent(UIEventType::Click); + } + + _activeProcessor = nullptr; +} + +void InputProcessor::setBegin(TouchInfo* touch, GObject* target) +{ + touch->began = true; + touch->clickCancelled = false; + touch->downPos = touch->pos; + + touch->downTargets.clear(); + GObject* obj = target; + while (obj != nullptr) + { + touch->downTargets.push_back(WeakPtr(obj)); + obj = obj->findParent(); + } +} + +void InputProcessor::setEnd(TouchInfo* touch, GObject* target) +{ + touch->began = false; + + auto now = clock(); + float elapsed = (now - touch->lastClickTime) / (double)CLOCKS_PER_SEC; + + if (elapsed < 0.45f) + { + if (touch->clickCount == 2) + touch->clickCount = 1; + else + touch->clickCount++; + } + else + touch->clickCount = 1; + touch->lastClickTime = now; +} + +GObject* InputProcessor::clickTest(TouchInfo* touch, GObject* target) +{ + if (touch->downTargets.empty() + || touch->clickCancelled + || std::abs(touch->pos.x - touch->downPos.x) > 50 || std::abs(touch->pos.y - touch->downPos.y) > 50) + return nullptr; + + GObject* obj = touch->downTargets[0].ptr(); + if (obj && obj->onStage()) + return obj; + + obj = target; + while (obj != nullptr) + { + auto it = std::find(touch->downTargets.cbegin(), touch->downTargets.cend(), obj); + if (it != touch->downTargets.cend() && it->onStage()) + { + obj = it->ptr(); + break; + } + + obj = obj->findParent(); + } + + return obj; +} + +bool InputProcessor::isTouchOnUI() +{ + return _touchOnUI; +} + +void InputProcessor::disableDefaultTouchEvent() +{ + _owner->displayObject()->getEventDispatcher()->removeEventListener(_touchListener); +} + +bool InputProcessor::touchDown(cocos2d::Touch *touch, cocos2d::Event *event) +{ + return onTouchBegan(touch, event); +} + +void InputProcessor::touchMove(cocos2d::Touch *touch, cocos2d::Event *event) +{ + onTouchMoved(touch, event); +} + +void InputProcessor::touchUp(cocos2d::Touch *touch, cocos2d::Event *event) +{ + onTouchEnded(touch, event); +} + +bool InputProcessor::onTouchBegan(Touch *touch, Event* /*unusedEvent*/) +{ + if (!(_owner->isTouchable() && _owner->isVisible())) { + return false; + } + + auto camera = Camera::getVisitingCamera(); + Vec2 pt = touch->getLocation(); + GObject* target = _owner->hitTest(pt, camera); + if (!target) + target = _owner; + _touchListener->setSwallowTouches(target != _owner); + + TouchInfo* ti = getTouch(touch->getID()); + ti->pos = UIRoot->worldToRoot(pt); + ti->button = EventMouse::MouseButton::BUTTON_LEFT; + ti->touch = touch; + setBegin(ti, target); + + updateRecentInput(ti, target); + _activeProcessor = this; + + if (_captureCallback) + _captureCallback(UIEventType::TouchBegin); + + WeakPtr wptr(target); + target->bubbleEvent(UIEventType::TouchBegin); + target = wptr.ptr(); + + handleRollOver(ti, target); + + _activeProcessor = nullptr; + + return true; +} + +void InputProcessor::onTouchMoved(Touch *touch, Event* /*unusedEvent*/) +{ + auto camera = Camera::getVisitingCamera(); + Vec2 pt = touch->getLocation(); + GObject* target = _owner->hitTest(pt, camera); + if (!target) + target = _owner; + + TouchInfo* ti = getTouch(touch->getID()); + ti->pos = UIRoot->worldToRoot(pt); + ti->button = EventMouse::MouseButton::BUTTON_LEFT; + ti->touch = touch; + + updateRecentInput(ti, target); + _activeProcessor = this; + + if (_captureCallback) + _captureCallback(UIEventType::TouchMove); + + handleRollOver(ti, target); + + if (ti->began) + { + bool done = false; + size_t cnt = ti->touchMonitors.size(); + if (cnt > 0) + { + for (size_t i = 0; i < cnt; i++) + { + GObject* mm = ti->touchMonitors[i].ptr(); + if (!mm) + continue; + + mm->dispatchEvent(UIEventType::TouchMove); + if (mm == _owner) + done = true; + } + } + if (!done) + _owner->dispatchEvent(UIEventType::TouchMove); + } + + _activeProcessor = nullptr; +} + +void InputProcessor::onTouchEnded(Touch *touch, Event* /*unusedEvent*/) +{ + auto camera = Camera::getVisitingCamera(); + Vec2 pt = touch->getLocation(); + GObject* target = _owner->hitTest(pt, camera); + if (!target) + target = _owner; + + TouchInfo* ti = getTouch(touch->getID()); + ti->pos = UIRoot->worldToRoot(pt); + ti->button = EventMouse::MouseButton::BUTTON_LEFT; + ti->touch = touch; + setEnd(ti, target); + + updateRecentInput(ti, target); + _activeProcessor = this; + + if (_captureCallback) + _captureCallback(UIEventType::TouchEnd); + + WeakPtr wptr(target); + size_t cnt = ti->touchMonitors.size(); + if (cnt > 0) + { + for (size_t i = 0; i < cnt; i++) + { + GObject* mm = ti->touchMonitors[i].ptr(); + if (!mm) + continue; + + if (mm != target + && (!dynamic_cast(mm) || !((GComponent*)mm)->isAncestorOf(target))) + mm->dispatchEvent(UIEventType::TouchEnd); + } + ti->touchMonitors.clear(); + target = wptr.ptr(); + } + if (target) + { + target->bubbleEvent(UIEventType::TouchEnd); + target = wptr.ptr(); + } + + target = clickTest(ti, target); + if (target) + { + wptr = target; + updateRecentInput(ti, target); + + GRichTextField* tf = dynamic_cast(target); + if (tf) + { + const char* linkHref = dynamic_cast(tf->displayObject())->hitTestLink(pt); + if (linkHref) + { + tf->bubbleEvent(UIEventType::ClickLink, nullptr, Value(linkHref)); + target = wptr.ptr(); + } + } + + target->bubbleEvent(UIEventType::Click); + target = wptr.ptr(); + } + +#ifndef CC_PLATFORM_PC + //on mobile platform, trigger RollOut on up event, but not on PC + handleRollOver(ti, nullptr); +#else + handleRollOver(ti, target); +#endif + ti->touchId = -1; + ti->button = EventMouse::MouseButton::BUTTON_UNSET; + + _activeProcessor = nullptr; +} + +void InputProcessor::onTouchCancelled(Touch* touch, Event* /*unusedEvent*/) +{ + TouchInfo* ti = getTouch(touch->getID()); + if (ti == nullptr) + return; + + ti->touch = touch; + updateRecentInput(ti, _owner); + _activeProcessor = this; + + if (_captureCallback) + _captureCallback(UIEventType::TouchEnd); + + size_t cnt = ti->touchMonitors.size(); + if (cnt > 0) + { + for (size_t i = 0; i < cnt; i++) + { + GObject* mm = ti->touchMonitors[i].ptr(); + if (!mm) + continue; + + if (mm != _owner) + mm->dispatchEvent(UIEventType::TouchEnd); + } + ti->touchMonitors.clear(); + } + _owner->dispatchEvent(UIEventType::TouchEnd); + + handleRollOver(ti, nullptr); + + ti->touchId = -1; + ti->button = EventMouse::MouseButton::BUTTON_UNSET; + _activeProcessor = nullptr; +} + +void InputProcessor::onMouseDown(cocos2d::EventMouse * event) +{ + if (event->getMouseButton() == EventMouse::MouseButton::BUTTON_LEFT) + return; + + auto camera = Camera::getVisitingCamera(); + Vec2 pt(event->getCursorX(), event->getCursorY()); + GObject* target = _owner->hitTest(pt, camera); + if (!target) + target = _owner; + _touchListener->setSwallowTouches(target != _owner); + + TouchInfo* ti = getTouch(0); + ti->pos = UIRoot->worldToRoot(pt); + ti->button = event->getMouseButton(); + ti->touch = nullptr; + setBegin(ti, target); + + updateRecentInput(ti, target); + _activeProcessor = this; + + if (_captureCallback) + _captureCallback(UIEventType::TouchBegin); + + WeakPtr wptr(target); + target->bubbleEvent(UIEventType::TouchBegin); + + _activeProcessor = nullptr; +} + +void InputProcessor::onMouseUp(cocos2d::EventMouse * event) +{ + if (event->getMouseButton() == EventMouse::MouseButton::BUTTON_LEFT) + return; + + auto camera = Camera::getVisitingCamera(); + Vec2 pt(event->getCursorX(), event->getCursorY()); + GObject* target = _owner->hitTest(pt, camera); + if (!target) + target = _owner; + + TouchInfo* ti = getTouch(0); + ti->pos = UIRoot->worldToRoot(pt); + ti->button = event->getMouseButton(); + ti->touch = nullptr; + setEnd(ti, target); + + updateRecentInput(ti, target); + _activeProcessor = this; + + if (_captureCallback) + _captureCallback(UIEventType::TouchEnd); + + WeakPtr wptr(target); + size_t cnt = ti->touchMonitors.size(); + if (cnt > 0) + { + for (size_t i = 0; i < cnt; i++) + { + GObject* mm = ti->touchMonitors[i].ptr(); + if (!mm) + continue; + + if (mm != target + && (!dynamic_cast(mm) || !((GComponent*)mm)->isAncestorOf(target))) + mm->dispatchEvent(UIEventType::TouchEnd); + } + ti->touchMonitors.clear(); + target = wptr.ptr(); + } + if (target) + { + target->bubbleEvent(UIEventType::TouchEnd); + target = wptr.ptr(); + } + + target = clickTest(ti, target); + if (target) + { + wptr = target; + updateRecentInput(ti, target); + + if (ti->button == EventMouse::MouseButton::BUTTON_MIDDLE) + target->bubbleEvent(UIEventType::MiddleClick); + else + target->bubbleEvent(UIEventType::RightClick); + } + + ti->touchId = -1; + ti->button = EventMouse::MouseButton::BUTTON_UNSET; + + _activeProcessor = nullptr; +} + +void InputProcessor::onMouseMove(cocos2d::EventMouse * event) +{ + TouchInfo* ti = getTouch(0); + Vec2 npos = UIRoot->worldToRoot(Vec2(event->getCursorX(), event->getCursorY())); + if (std::abs(ti->pos.x - npos.x) < 1 + && std::abs(ti->pos.y - npos.y) < 1) + return; + + auto camera = Camera::getVisitingCamera(); + Vec2 pt(event->getCursorX(), event->getCursorY()); + GObject* target = _owner->hitTest(pt, camera); + if (!target) + target = _owner; + + ti->pos = UIRoot->worldToRoot(pt); + ti->touch = nullptr; + + updateRecentInput(ti, target); + _activeProcessor = this; + + if (_captureCallback) + _captureCallback(UIEventType::TouchMove); + + handleRollOver(ti, target); + + if (ti->began) + { + bool done = false; + size_t cnt = ti->touchMonitors.size(); + if (cnt > 0) + { + for (size_t i = 0; i < cnt; i++) + { + GObject* mm = ti->touchMonitors[i].ptr(); + if (!mm) + continue; + + mm->dispatchEvent(UIEventType::TouchMove); + if (mm == _owner) + done = true; + } + } + if (!done) + _owner->dispatchEvent(UIEventType::TouchMove); + } + + _activeProcessor = nullptr; +} + +void InputProcessor::onMouseScroll(cocos2d::EventMouse * event) +{ + auto camera = Camera::getVisitingCamera(); + Vec2 pt(event->getCursorX(), event->getCursorY()); + GObject* target = _owner->hitTest(pt, camera); + if (!target) + target = _owner; + + TouchInfo* ti = getTouch(0); + ti->pos = UIRoot->worldToRoot(pt); + ti->touch = nullptr; + ti->mouseWheelDelta = MAX(event->getScrollX(), event->getScrollY()); + + updateRecentInput(ti, target); + _activeProcessor = this; + + target->bubbleEvent(UIEventType::MouseWheel); + ti->mouseWheelDelta = 0; + + _activeProcessor = nullptr; +} + +void InputProcessor::onKeyDown(cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event * event) +{ + if (keyCode == EventKeyboard::KeyCode::KEY_LEFT_CTRL || keyCode == EventKeyboard::KeyCode::KEY_RIGHT_CTRL) + _keyModifiers |= 1; + else if (keyCode == EventKeyboard::KeyCode::KEY_LEFT_ALT || keyCode == EventKeyboard::KeyCode::KEY_RIGHT_ALT) + _keyModifiers |= 2; + else if (keyCode == EventKeyboard::KeyCode::KEY_LEFT_SHIFT || keyCode == EventKeyboard::KeyCode::KEY_RIGHT_SHIFT) + _keyModifiers |= 4; + + _recentInput._keyCode = keyCode; + _recentInput._target = _owner; + _recentInput._target->dispatchEvent(UIEventType::KeyDown); +} + +void InputProcessor::onKeyUp(cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event *) +{ + if (keyCode == EventKeyboard::KeyCode::KEY_LEFT_CTRL || keyCode == EventKeyboard::KeyCode::KEY_RIGHT_CTRL) + _keyModifiers &= ~1; + else if (keyCode == EventKeyboard::KeyCode::KEY_LEFT_ALT || keyCode == EventKeyboard::KeyCode::KEY_RIGHT_ALT) + _keyModifiers &= ~2; + else if (keyCode == EventKeyboard::KeyCode::KEY_LEFT_SHIFT || keyCode == EventKeyboard::KeyCode::KEY_RIGHT_SHIFT) + _keyModifiers &= ~4; + + _recentInput._keyCode = keyCode; + _recentInput._target = _owner; + _recentInput._target->dispatchEvent(UIEventType::KeyUp); +} + +TouchInfo::TouchInfo() : + touch(nullptr), + touchId(-1), + clickCount(0), + mouseWheelDelta(0), + button(EventMouse::MouseButton::BUTTON_UNSET), + began(false), + lastClickTime(0), + clickCancelled(false) +{ +} + +TouchInfo::~TouchInfo() +{ + downTargets.clear(); + touchMonitors.clear(); +} + +void TouchInfo::reset() +{ + touchId = -1; + mouseWheelDelta = 0; + button = EventMouse::MouseButton::BUTTON_UNSET; + touch = nullptr; + pos.setZero(); + downPos.setZero(); + clickCount = 0; + lastClickTime = 0; + began = false; + downTargets.clear(); + lastRollOver = nullptr; + clickCancelled = false; + touchMonitors.clear(); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/event/InputProcessor.h b/extensions/fairygui/event/InputProcessor.h new file mode 100644 index 0000000000..4e5a8caa44 --- /dev/null +++ b/extensions/fairygui/event/InputProcessor.h @@ -0,0 +1,79 @@ +#ifndef __INPUTPROCESSOR_H__ +#define __INPUTPROCESSOR_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "UIEventDispatcher.h" +#include "InputEvent.h" + +NS_FGUI_BEGIN + +class GComponent; +class TouchInfo; + +class InputProcessor +{ +public: + typedef std::function CaptureEventCallback; + + InputEvent* getRecentInput() { return &_recentInput; } + static bool isTouchOnUI(); + + InputProcessor(GComponent* owner); + ~InputProcessor(); + + cocos2d::Vec2 getTouchPosition(int touchId); + + void addTouchMonitor(int touchId, GObject* target); + void removeTouchMonitor(GObject* target); + + void cancelClick(int touchId); + void simulateClick(GObject* target, int touchId = -1); + + void setCaptureCallback(CaptureEventCallback value) { _captureCallback = value; } + + void disableDefaultTouchEvent(); + bool touchDown(cocos2d::Touch *touch, cocos2d::Event *event); + void touchMove(cocos2d::Touch *touch, cocos2d::Event *event); + void touchUp(cocos2d::Touch *touch, cocos2d::Event *event); + +private: + bool onTouchBegan(cocos2d::Touch * touch, cocos2d::Event *); + void onTouchMoved(cocos2d::Touch * touch, cocos2d::Event *); + void onTouchEnded(cocos2d::Touch * touch, cocos2d::Event *); + void onTouchCancelled(cocos2d::Touch * touch, cocos2d::Event *); + + void onMouseDown(cocos2d::EventMouse* event); + void onMouseUp(cocos2d::EventMouse* event); + void onMouseMove(cocos2d::EventMouse* event); + void onMouseScroll(cocos2d::EventMouse* event); + + void onKeyDown(cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event*); + void onKeyUp(cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event*); + + TouchInfo* getTouch(int touchId, bool createIfNotExisits = true); + void updateRecentInput(TouchInfo* touch, GObject* target); + void handleRollOver(TouchInfo* touch, GObject* target); + void setBegin(TouchInfo* touch, GObject* target); + void setEnd(TouchInfo* touch, GObject* target); + GObject* clickTest(TouchInfo* touch, GObject* target); + + cocos2d::EventListenerTouchOneByOne* _touchListener; + cocos2d::EventListenerMouse* _mouseListener; + cocos2d::EventListenerKeyboard* _keyboardListener; + std::vector _touches; + GComponent* _owner; + CaptureEventCallback _captureCallback; + InputEvent _recentInput; + uint16_t _keyModifiers; + + static bool _touchOnUI; + static unsigned int _touchOnUIFlagFrameId; + static InputProcessor* _activeProcessor; + + friend class UIEventDispatcher; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/event/UIEventDispatcher.cpp b/extensions/fairygui/event/UIEventDispatcher.cpp new file mode 100644 index 0000000000..11def0fa13 --- /dev/null +++ b/extensions/fairygui/event/UIEventDispatcher.cpp @@ -0,0 +1,285 @@ +#include "UIEventDispatcher.h" +#include "GComponent.h" +#include "InputProcessor.h" +#include "utils/WeakPtr.h" + +USING_NS_CC; +NS_FGUI_BEGIN + +const EventTag EventTag::None; + +EventTag::EventTag() :_value(0) +{ +} + +EventTag::EventTag(void * ptr) : _value((uintptr_t)ptr) +{ +} + +EventTag::EventTag(int value) : _value(value) +{ +} + +EventTag::EventTag(const EventTag & other) +{ + *this = other; +} + +EventTag::EventTag(EventTag && other) +{ + *this = std::move(other); +} + +EventTag::~EventTag() +{ +} + +EventTag & EventTag::operator=(const EventTag & other) +{ + if (this != &other) + _value = other._value; + return *this; +} + +EventTag & EventTag::operator=(EventTag && other) +{ + if (this != &other) + { + _value = other._value; + other._value = 0; + } + return *this; +} + +EventTag & EventTag::operator=(void * ptr) +{ + _value = (uintptr_t)ptr; + return *this; +} + +EventTag & EventTag::operator=(int v) +{ + _value = v; + return *this; +} + +bool EventTag::operator!=(const EventTag & v) +{ + return _value != v._value; +} + +bool EventTag::operator!=(const EventTag & v) const +{ + return _value != v._value; +} + +bool EventTag::operator==(const EventTag & v) +{ + return _value == v._value; +} + +bool EventTag::operator==(const EventTag & v) const +{ + return _value == v._value; +} + +UIEventDispatcher::UIEventDispatcher() :_dispatching(0) +{ +} + +UIEventDispatcher::~UIEventDispatcher() +{ + _dispatching = 0; + removeEventListeners(); +} + +void UIEventDispatcher::addEventListener(int eventType, const EventCallback& callback, const EventTag& tag) +{ + if (!tag.isNone()) + { + for (auto it = _callbacks.begin(); it != _callbacks.end(); it++) + { + if ((*it)->eventType == eventType && (*it)->tag == tag) + { + (*it)->callback = callback; + return; + } + } + } + + EventCallbackItem* item = new EventCallbackItem(); + item->callback = callback; + item->eventType = eventType; + item->tag = tag; + item->dispatching = 0; + _callbacks.push_back(item); +} + +void UIEventDispatcher::removeEventListener(int eventType, const EventTag& tag) +{ + if (_callbacks.empty()) + return; + + for (auto it = _callbacks.begin(); it != _callbacks.end(); ) + { + if ((*it)->eventType == eventType && ((*it)->tag == tag || tag.isNone())) + { + if (_dispatching > 0) + { + (*it)->callback = nullptr; + it++; + } + else + { + delete (*it); + it = _callbacks.erase(it); + } + } + else + it++; + } +} + +void UIEventDispatcher::removeEventListeners() +{ + if (_callbacks.empty()) + return; + + if (_dispatching > 0) + { + for (auto it = _callbacks.begin(); it != _callbacks.end(); ++it) + (*it)->callback = nullptr; + } + else + { + for (auto it = _callbacks.begin(); it != _callbacks.end(); it++) + delete (*it); + _callbacks.clear(); + } +} + +bool UIEventDispatcher::hasEventListener(int eventType, const EventTag& tag) const +{ + if (_callbacks.empty()) + return false; + + for (auto it = _callbacks.cbegin(); it != _callbacks.cend(); ++it) + { + if ((*it)->eventType == eventType && ((*it)->tag == tag || tag.isNone()) && (*it)->callback != nullptr) + return true; + } + return false; +} + +bool UIEventDispatcher::dispatchEvent(int eventType, void* data, const Value& dataValue) +{ + if (_callbacks.size() == 0) + return false; + + EventContext context; + context._sender = this; + context._type = eventType; + if (InputProcessor::_activeProcessor) + context._inputEvent = InputProcessor::_activeProcessor->getRecentInput(); + context._dataValue = dataValue; + context._data = data; + + doDispatch(eventType, &context); + + return context._defaultPrevented; +} + +bool UIEventDispatcher::bubbleEvent(int eventType, void* data, const Value& dataValue) +{ + EventContext context; + if (InputProcessor::_activeProcessor) + context._inputEvent = InputProcessor::_activeProcessor->getRecentInput(); + context._type = eventType; + context._dataValue = dataValue; + context._data = data; + + doBubble(eventType, &context); + + return context._defaultPrevented; +} + +bool UIEventDispatcher::isDispatchingEvent(int eventType) +{ + for (auto it = _callbacks.begin(); it != _callbacks.end(); ++it) + { + if ((*it)->eventType == eventType) + return (*it)->dispatching > 0; + } + return false; +} + +void UIEventDispatcher::doDispatch(int eventType, EventContext* context) +{ + retain(); + + _dispatching++; + context->_sender = this; + bool hasDeletedItems = false; + + size_t cnt = _callbacks.size(); //dont use iterator, because new item would be added in callback. + for (size_t i = 0; i < cnt; i++) + { + EventCallbackItem* ci = _callbacks[i]; + if (ci->callback == nullptr) + { + hasDeletedItems = true; + continue; + } + if (ci->eventType == eventType) + { + ci->dispatching++; + context->_touchCapture = 0; + ci->callback(context); + ci->dispatching--; + if (context->_touchCapture != 0 && dynamic_cast(this)) + { + if (context->_touchCapture == 1 && eventType == UIEventType::TouchBegin) + context->getInput()->getProcessor()->addTouchMonitor(context->getInput()->getTouchId(), dynamic_cast(this)); + else if (context->_touchCapture == 2) + context->getInput()->getProcessor()->removeTouchMonitor(dynamic_cast(this)); + } + } + } + + _dispatching--; + if (hasDeletedItems && _dispatching == 0) + { + for (auto it = _callbacks.begin(); it != _callbacks.end(); ) + { + if ((*it)->callback == nullptr) + { + delete (*it); + it = _callbacks.erase(it); + } + else + it++; + } + } + + release(); +} + +void UIEventDispatcher::doBubble(int eventType, EventContext* context) +{ + //parent maybe disposed in callbacks + WeakPtr wptr(((GObject*)this)->findParent()); + + if (!_callbacks.empty()) + { + context->_isStopped = false; + doDispatch(eventType, context); + if (context->_isStopped) + return; + } + + GObject* p = wptr.ptr(); + if (p) + p->doBubble(eventType, context); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/event/UIEventDispatcher.h b/extensions/fairygui/event/UIEventDispatcher.h new file mode 100644 index 0000000000..411dd6b140 --- /dev/null +++ b/extensions/fairygui/event/UIEventDispatcher.h @@ -0,0 +1,79 @@ +#ifndef __UIEVENTDISPATCHER_H__ +#define __UIEVENTDISPATCHER_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "EventContext.h" +#include "UIEventType.h" + +NS_FGUI_BEGIN + +typedef std::function EventCallback; + +class EventTag +{ +public: + static const EventTag None; + + EventTag(); + explicit EventTag(void* ptr); + explicit EventTag(int value); + explicit EventTag(const EventTag& other); + explicit EventTag(EventTag&& other); + ~EventTag(); + + EventTag& operator= (const EventTag& other); + EventTag& operator= (EventTag&& other); + EventTag& operator= (void* ptr); + EventTag& operator= (int v); + + bool operator!= (const EventTag& v); + bool operator!= (const EventTag& v) const; + bool operator== (const EventTag& v); + bool operator== (const EventTag& v) const; + + bool isNone() const { return _value == 0; } + +private: + uintptr_t _value; +}; + +class InputProcessor; + +class UIEventDispatcher : public cocos2d::Ref +{ +public: + UIEventDispatcher(); + virtual ~UIEventDispatcher(); + + void addEventListener(int eventType, const EventCallback& callback) { return addEventListener(eventType, callback, EventTag::None); } + void addEventListener(int eventType, const EventCallback& callback, const EventTag& tag); + void removeEventListener(int eventType) { removeEventListener(eventType, EventTag::None); } + void removeEventListener(int eventType, const EventTag& tag); + void removeEventListeners(); + bool hasEventListener(int eventType) const { return hasEventListener(eventType, EventTag::None); } + bool hasEventListener(int eventType, const EventTag& tag) const; + + bool dispatchEvent(int eventType, void* data = nullptr, const cocos2d::Value& dataValue = cocos2d::Value::Null); + bool bubbleEvent(int eventType, void* data = nullptr, const cocos2d::Value& dataValue = cocos2d::Value::Null); + + bool isDispatchingEvent(int eventType); + +private: + void doDispatch(int eventType, EventContext* context); + void doBubble(int eventType, EventContext* context); + + struct EventCallbackItem + { + EventCallback callback; + int eventType; + EventTag tag; + int dispatching; + }; + std::vector _callbacks; + int _dispatching; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/event/UIEventType.h b/extensions/fairygui/event/UIEventType.h new file mode 100644 index 0000000000..c0b2a33798 --- /dev/null +++ b/extensions/fairygui/event/UIEventType.h @@ -0,0 +1,52 @@ +#ifndef __UIEVENTTYPE_H__ +#define __UIEVENTTYPE_H__ + +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +class UIEventType +{ +public: + static const int Enter = 0; + static const int Exit = 1; + static const int Changed = 2; + static const int Submit = 3; + + static const int TouchBegin = 10; + static const int TouchMove = 11; + static const int TouchEnd = 12; + static const int Click = 13; + static const int RollOver = 14; + static const int RollOut = 15; + static const int MouseWheel = 16; + static const int RightClick = 17; + static const int MiddleClick = 18; + + static const int PositionChange = 20; + static const int SizeChange = 21; + + static const int KeyDown = 30; + static const int KeyUp = 31; + + static const int Scroll = 40; + static const int ScrollEnd = 41; + static const int PullDownRelease = 42; + static const int PullUpRelease = 43; + + static const int ClickItem = 50; + static const int ClickLink = 51; + static const int ClickMenu = 52; + static const int RightClickItem = 53; + + static const int DragStart = 60; + static const int DragMove = 61; + static const int DragEnd = 62; + static const int Drop = 63; + + static const int GearStop = 70; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearAnimation.cpp b/extensions/fairygui/gears/GearAnimation.cpp new file mode 100644 index 0000000000..39acd6a9be --- /dev/null +++ b/extensions/fairygui/gears/GearAnimation.cpp @@ -0,0 +1,67 @@ +#include "GearAnimation.h" +#include "GObject.h" +#include "UIPackage.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearAnimation::GearAnimationValue::GearAnimationValue() : frame(0), playing(false) +{ +} + +GearAnimation::GearAnimationValue::GearAnimationValue(bool playing, int frame) +{ + this->playing = playing; + this->frame = frame; +} + +GearAnimation::GearAnimation(GObject* owner) : GearBase(owner) +{ +} + +GearAnimation::~GearAnimation() +{ +} + +void GearAnimation::init() +{ + _default = GearAnimationValue(_owner->getProp(ObjectPropID::Playing).asBool(), _owner->getProp(ObjectPropID::Frame).asInt()); + _storage.clear(); +} + +void GearAnimation::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ + GearAnimationValue gv; + gv.playing = buffer->readBool(); + gv.frame = buffer->readInt(); + if (pageId.size() == 0) + _default = gv; + else + _storage[pageId] = gv; +} + +void GearAnimation::apply() +{ + _owner->_gearLocked = true; + + GearAnimationValue gv; + auto it = _storage.find(_controller->getSelectedPageId()); + if (it != _storage.end()) + gv = it->second; + else + gv = _default; + + _owner->setProp(ObjectPropID::Playing, Value(gv.playing)); + _owner->setProp(ObjectPropID::Frame, Value(gv.frame)); + + _owner->_gearLocked = false; +} + +void GearAnimation::updateState() +{ + _storage[_controller->getSelectedPageId()] = GearAnimationValue( + _owner->getProp(ObjectPropID::Playing).asBool(), _owner->getProp(ObjectPropID::Frame).asInt()); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearAnimation.h b/extensions/fairygui/gears/GearAnimation.h new file mode 100644 index 0000000000..3693905022 --- /dev/null +++ b/extensions/fairygui/gears/GearAnimation.h @@ -0,0 +1,41 @@ +#ifndef __GEARANIMATION_H__ +#define __GEARANIMATION_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "GearBase.h" + +NS_FGUI_BEGIN + +class GObject; + +class GearAnimation : public GearBase +{ +public: + GearAnimation(GObject* owner); + virtual ~GearAnimation(); + + void apply() override; + void updateState() override; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + class GearAnimationValue + { + public: + bool playing; + int frame; + + GearAnimationValue(); + GearAnimationValue(bool playing, int frame); + }; + std::unordered_map _storage; + GearAnimationValue _default; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearBase.cpp b/extensions/fairygui/gears/GearBase.cpp new file mode 100644 index 0000000000..7282b3445c --- /dev/null +++ b/extensions/fairygui/gears/GearBase.cpp @@ -0,0 +1,186 @@ +#include "GearBase.h" +#include "GComponent.h" +#include "GearDisplay.h" +#include "gears/GearAnimation.h" +#include "gears/GearColor.h" +#include "gears/GearDisplay2.h" +#include "gears/GearFontSize.h" +#include "gears/GearIcon.h" +#include "gears/GearLook.h" +#include "gears/GearSize.h" +#include "gears/GearText.h" +#include "gears/GearXY.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +bool GearBase::disableAllTweenEffect = false; + +GearBase* GearBase::create(GObject* owner, int index) +{ + GearBase* gear = nullptr; + switch (index) + { + case 0: + gear = new GearDisplay(owner); + break; + case 1: + gear = new GearXY(owner); + break; + case 2: + gear = new GearSize(owner); + break; + case 3: + gear = new GearLook(owner); + break; + case 4: + gear = new GearColor(owner); + break; + case 5: + gear = new GearAnimation(owner); + break; + case 6: + gear = new GearText(owner); + break; + case 7: + gear = new GearIcon(owner); + break; + case 8: + gear = new GearDisplay2(owner); + break; + case 9: + gear = new GearFontSize(owner); + break; + } + + return gear; +} + +GearTweenConfig::GearTweenConfig() + : tween(true), + easeType(EaseType::QuadOut), + duration(0.3f), + delay(0), + _tweener(nullptr), + _displayLockToken(0) +{ +} + +GearBase::GearBase(GObject* owner) : _owner(owner), _tweenConfig(nullptr) +{ +} + +GearBase::~GearBase() +{ + if (_tweenConfig && _tweenConfig->_tweener) + _tweenConfig->_tweener->kill(); + CC_SAFE_DELETE(_tweenConfig); +} + +void GearBase::setController(GController* value) +{ + if (value != _controller) + { + _controller = value; + if (_controller != nullptr) + init(); + } +} + +GearTweenConfig* GearBase::getTweenConfig() +{ + if (!_tweenConfig) + _tweenConfig = new GearTweenConfig(); + + return _tweenConfig; +} + +void GearBase::init() +{ +} + +void GearBase::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ +} + +void GearBase::apply() +{ +} + +void GearBase::updateState() +{ +} + +void GearBase::updateFromRelations(float dx, float dy) +{ +} + +void GearBase::setup(ByteBuffer* buffer) +{ + _controller = _owner->getParent()->getControllerAt(buffer->readShort()); + init(); + + int cnt = buffer->readShort(); + GearDisplay* g0 = dynamic_cast(this); + GearDisplay2* g1 = dynamic_cast(this); + GearXY* g2 = nullptr; + g0 = dynamic_cast(this); + if (g0) + buffer->readSArray(g0->pages, cnt); + else if (g1) + buffer->readSArray(g1->pages, cnt); + else + { + for (int i = 0; i < cnt; i++) + { + const std::string& page = buffer->readS(); + if (page.empty()) + continue; + + addStatus(page, buffer); + } + + if (buffer->readBool()) + addStatus(STD_STRING_EMPTY, buffer); + } + + if (buffer->readBool()) + { + _tweenConfig = new GearTweenConfig(); + _tweenConfig->tween = true; + _tweenConfig->easeType = (EaseType)buffer->readByte(); + _tweenConfig->duration = buffer->readFloat(); + _tweenConfig->delay = buffer->readFloat(); + } + + if (buffer->version >= 2) + { + if ((g2 = dynamic_cast(this)) != nullptr) + { + if (buffer->readBool()) + { + if (g2) + { + g2->positionsInPercent = true; + for (int i = 0; i < cnt; i++) + { + const std::string& page = buffer->readS(); + if (page.empty()) + continue; + + g2->addExtStatus(page, buffer); + } + + if (buffer->readBool()) + g2->addExtStatus(STD_STRING_EMPTY, buffer); + } + } + } + else if (g1 != nullptr) + g1->condition = buffer->readByte(); + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearBase.h b/extensions/fairygui/gears/GearBase.h new file mode 100644 index 0000000000..939ee0f276 --- /dev/null +++ b/extensions/fairygui/gears/GearBase.h @@ -0,0 +1,60 @@ +#ifndef __GEARBASE_H__ +#define __GEARBASE_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "tween/EaseType.h" + +NS_FGUI_BEGIN + +class GObject; +class GController; +class GTweener; +class ByteBuffer; + +class GearTweenConfig +{ +public: + GearTweenConfig(); + + bool tween; + EaseType easeType; + float duration; + float delay; + + uint32_t _displayLockToken; + GTweener* _tweener; +}; + +class GearBase +{ +public: + GearBase(GObject* owner); + virtual ~GearBase(); + + GController* getController() const { return _controller; } + void setController(GController* value); + + GearTweenConfig* getTweenConfig(); + + virtual void updateFromRelations(float dx, float dy); + virtual void apply(); + virtual void updateState(); + + void setup(ByteBuffer* buffer); + + static GearBase* create(GObject* owner, int index); + static bool disableAllTweenEffect; + +protected: + virtual void addStatus(const std::string& pageId, ByteBuffer* buffer); + virtual void init(); + + GObject* _owner; + GController* _controller; + GearTweenConfig* _tweenConfig; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearColor.cpp b/extensions/fairygui/gears/GearColor.cpp new file mode 100644 index 0000000000..6bd847e7e6 --- /dev/null +++ b/extensions/fairygui/gears/GearColor.cpp @@ -0,0 +1,127 @@ +#include "GearColor.h" +#include "GObject.h" +#include "GTextField.h" +#include "UIPackage.h" +#include "tween/GTween.h" +#include "utils/ByteBuffer.h" +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearColor::GearColorValue::GearColorValue() +{ +} + +GearColor::GearColorValue::GearColorValue(const Color3B& color, const Color3B& strokeColor) +{ + this->color = color; + this->outlineColor = strokeColor; +} + +GearColor::GearColor(GObject* owner) : GearBase(owner) +{ +} + +GearColor::~GearColor() +{ +} + +void GearColor::init() +{ + _default = GearColorValue(ToolSet::intToColor(_owner->getProp(ObjectPropID::Color).asUnsignedInt()), + ToolSet::intToColor(_owner->getProp(ObjectPropID::OutlineColor).asUnsignedInt())); + _storage.clear(); +} + +void GearColor::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ + GearColorValue gv; + gv.color = (Color3B)buffer->readColor(); + gv.outlineColor = (Color3B)buffer->readColor(); + if (pageId.size() == 0) + _default = gv; + else + _storage[pageId] = gv; +} + +void GearColor::apply() +{ + GearColorValue gv; + auto it = _storage.find(_controller->getSelectedPageId()); + if (it != _storage.end()) + gv = it->second; + else + gv = _default; + + if (_tweenConfig && _tweenConfig->tween && UIPackage::_constructing == 0 && !disableAllTweenEffect) + { + Color3B curColor = ToolSet::intToColor(_owner->getProp(ObjectPropID::Color).asUnsignedInt()); + Color3B curStrokeColor = ToolSet::intToColor(_owner->getProp(ObjectPropID::OutlineColor).asUnsignedInt()); + + if (gv.outlineColor != curStrokeColor) + { + _owner->_gearLocked = true; + _owner->setProp(ObjectPropID::OutlineColor, Value(ToolSet::colorToInt(gv.outlineColor))); + _owner->_gearLocked = false; + } + + if (_tweenConfig->_tweener != nullptr) + { + if (_tweenConfig->_tweener->endValue.getColor() != gv.color) + { + _tweenConfig->_tweener->kill(true); + _tweenConfig->_tweener = nullptr; + } + else + return; + } + + if (gv.color != curColor) + { + if (_owner->checkGearController(0, _controller)) + _tweenConfig->_displayLockToken = _owner->addDisplayLock(); + + _tweenConfig->_tweener = GTween::to((Color4B)curColor, (Color4B)gv.color, _tweenConfig->duration) + ->setDelay(_tweenConfig->delay) + ->setEase(_tweenConfig->easeType) + ->setTargetAny(this) + ->onUpdate(CC_CALLBACK_1(GearColor::onTweenUpdate, this)) + ->onComplete(CC_CALLBACK_0(GearColor::onTweenComplete, this)); + } + } + else + { + _owner->_gearLocked = true; + _owner->setProp(ObjectPropID::Color, Value(ToolSet::colorToInt(gv.color))); + _owner->setProp(ObjectPropID::OutlineColor, Value(ToolSet::colorToInt(gv.outlineColor))); + _owner->_gearLocked = false; + } +} + +void GearColor::onTweenUpdate(GTweener* tweener) +{ + _owner->_gearLocked = true; + _owner->setProp(ObjectPropID::Color, Value(ToolSet::colorToInt((Color3B)_tweenConfig->_tweener->value.getColor()))); + _owner->_gearLocked = false; +} + +void GearColor::onTweenComplete() +{ + if (_tweenConfig->_displayLockToken != 0) + { + _owner->releaseDisplayLock(_tweenConfig->_displayLockToken); + _tweenConfig->_displayLockToken = 0; + } + _tweenConfig->_tweener = nullptr; + _owner->dispatchEvent(UIEventType::GearStop); +} + +void GearColor::updateState() +{ + _storage[_controller->getSelectedPageId()] = GearColorValue( + ToolSet::intToColor(_owner->getProp(ObjectPropID::Color).asUnsignedInt()), + ToolSet::intToColor(_owner->getProp(ObjectPropID::OutlineColor).asUnsignedInt())); +} + +NS_FGUI_END diff --git a/extensions/fairygui/gears/GearColor.h b/extensions/fairygui/gears/GearColor.h new file mode 100644 index 0000000000..a887d830f8 --- /dev/null +++ b/extensions/fairygui/gears/GearColor.h @@ -0,0 +1,46 @@ +#ifndef __GEARCOLOR_H__ +#define __GEARCOLOR_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "GearBase.h" + +NS_FGUI_BEGIN + +class GObject; +class GTweener; + +class GearColor : public GearBase +{ +public: + GearColor(GObject* owner); + virtual ~GearColor(); + + void apply() override; + void updateState() override; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + void onTweenUpdate(GTweener* tweener); + void onTweenComplete(); + + class GearColorValue + { + public: + cocos2d::Color3B color; + cocos2d::Color3B outlineColor; + + GearColorValue(); + GearColorValue(const cocos2d::Color3B& color, const cocos2d::Color3B& outlineColor); + }; + + std::unordered_map _storage; + GearColorValue _default; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearDisplay.cpp b/extensions/fairygui/gears/GearDisplay.cpp new file mode 100644 index 0000000000..302f327d1d --- /dev/null +++ b/extensions/fairygui/gears/GearDisplay.cpp @@ -0,0 +1,67 @@ +#include "GearDisplay.h" +#include "Controller.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearDisplay::GearDisplay(GObject* owner) + : GearBase(owner), + _visible(0), + _displayLockToken(1) +{ +} + +GearDisplay::~GearDisplay() +{ +} + +void GearDisplay::apply() +{ + _displayLockToken++; + if (_displayLockToken == 0) + _displayLockToken = 1; + + if (pages.size() == 0) + _visible = 1; + else + { + auto iter = std::find(pages.begin(), pages.end(), _controller->getSelectedPageId()); + if (iter != pages.end()) + _visible = 1; + else + _visible = 0; + } +} + +void GearDisplay::updateState() +{ +} + +void GearDisplay::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ +} + +void GearDisplay::init() +{ + pages.clear(); +} + +uint32_t GearDisplay::addLock() +{ + _visible++; + return _displayLockToken; +} + +void GearDisplay::releaseLock(uint32_t token) +{ + if (token == _displayLockToken) + _visible--; +} + +bool GearDisplay::isConnected() +{ + return _controller == nullptr || _visible > 0; +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearDisplay.h b/extensions/fairygui/gears/GearDisplay.h new file mode 100644 index 0000000000..eb1f54a54d --- /dev/null +++ b/extensions/fairygui/gears/GearDisplay.h @@ -0,0 +1,38 @@ +#ifndef __GEARDISPLAY_H__ +#define __GEARDISPLAY_H__ + +#include "FairyGUIMacros.h" +#include "GearBase.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; + +class GearDisplay : public GearBase +{ +public: + GearDisplay(GObject* owner); + virtual ~GearDisplay(); + + void apply() override; + void updateState() override; + + uint32_t addLock(); + void releaseLock(uint32_t token); + bool isConnected(); + + std::vector pages; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + int _visible; + uint32_t _displayLockToken; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearDisplay2.cpp b/extensions/fairygui/gears/GearDisplay2.cpp new file mode 100644 index 0000000000..44671d882c --- /dev/null +++ b/extensions/fairygui/gears/GearDisplay2.cpp @@ -0,0 +1,56 @@ +#include "GearDisplay2.h" +#include "Controller.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearDisplay2::GearDisplay2(GObject* owner) + : GearBase(owner), + _visible(0), + condition(0) +{ +} + +GearDisplay2::~GearDisplay2() +{ +} + +void GearDisplay2::apply() +{ + if (_controller == nullptr || pages.size() == 0) + _visible = 1; + else + { + auto iter = std::find(pages.begin(), pages.end(), _controller->getSelectedPageId()); + if (iter != pages.end()) + _visible = 1; + else + _visible = 0; + } +} + +bool GearDisplay2::evaluate(bool connected) +{ + bool v = _controller == nullptr || _visible > 0; + if (condition == 0) + v = v && connected; + else + v = v || connected; + return v; +} + +void GearDisplay2::updateState() +{ +} + +void GearDisplay2::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ +} + +void GearDisplay2::init() +{ + pages.clear(); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearDisplay2.h b/extensions/fairygui/gears/GearDisplay2.h new file mode 100644 index 0000000000..1728cc079b --- /dev/null +++ b/extensions/fairygui/gears/GearDisplay2.h @@ -0,0 +1,35 @@ +#ifndef __GEARDISPLAY2_H__ +#define __GEARDISPLAY2_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "GearBase.h" + +NS_FGUI_BEGIN + +class GObject; + +class GearDisplay2 : public GearBase +{ +public: + GearDisplay2(GObject* owner); + virtual ~GearDisplay2(); + + void apply() override; + void updateState() override; + bool evaluate(bool connected); + + std::vector pages; + int condition; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + int _visible; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearFontSize.cpp b/extensions/fairygui/gears/GearFontSize.cpp new file mode 100644 index 0000000000..e29cc93b33 --- /dev/null +++ b/extensions/fairygui/gears/GearFontSize.cpp @@ -0,0 +1,48 @@ +#include "GearFontSize.h" +#include "GObject.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearFontSize::GearFontSize(GObject* owner) : GearBase(owner) +{ +} + +GearFontSize::~GearFontSize() +{ +} + +void GearFontSize::init() +{ + _default = _owner->getProp(ObjectPropID::FontSize).asInt(); + _storage.clear(); +} + +void GearFontSize::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ + if (pageId.length() == 0) + _default = buffer->readInt(); + else + _storage[pageId] = buffer->readInt(); +} + +void GearFontSize::apply() +{ + _owner->_gearLocked = true; + + auto it = _storage.find(_controller->getSelectedPageId()); + if (it != _storage.end()) + _owner->setProp(ObjectPropID::FontSize, Value(it->second)); + else + _owner->setProp(ObjectPropID::FontSize, Value(_default)); + + _owner->_gearLocked = false; +} + +void GearFontSize::updateState() +{ + _storage[_controller->getSelectedPageId()] = _owner->getProp(ObjectPropID::FontSize).asInt(); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearFontSize.h b/extensions/fairygui/gears/GearFontSize.h new file mode 100644 index 0000000000..b696735ccf --- /dev/null +++ b/extensions/fairygui/gears/GearFontSize.h @@ -0,0 +1,32 @@ +#ifndef __GEARFONTSIZE_H__ +#define __GEARFONTSIZE_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "GearBase.h" + +NS_FGUI_BEGIN + +class GObject; + +class GearFontSize : public GearBase +{ +public: + GearFontSize(GObject* owner); + virtual ~GearFontSize(); + + void apply() override; + void updateState() override; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + std::unordered_map _storage; + int _default; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearIcon.cpp b/extensions/fairygui/gears/GearIcon.cpp new file mode 100644 index 0000000000..d67f70e853 --- /dev/null +++ b/extensions/fairygui/gears/GearIcon.cpp @@ -0,0 +1,49 @@ +#include "GearIcon.h" +#include "GObject.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearIcon::GearIcon(GObject * owner) :GearBase(owner) +{ + +} + +GearIcon::~GearIcon() +{ +} + +void GearIcon::init() +{ + _default = _owner->getIcon(); + _storage.clear(); +} + +void GearIcon::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ + if (pageId.length() == 0) + _default = buffer->readS(); + else + _storage[pageId] = buffer->readS(); +} + +void GearIcon::apply() +{ + _owner->_gearLocked = true; + + auto it = _storage.find(_controller->getSelectedPageId()); + if (it != _storage.end()) + _owner->setIcon(it->second); + else + _owner->setIcon(_default); + + _owner->_gearLocked = false; +} + +void GearIcon::updateState() +{ + _storage[_controller->getSelectedPageId()] = _owner->getIcon(); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearIcon.h b/extensions/fairygui/gears/GearIcon.h new file mode 100644 index 0000000000..33010341b5 --- /dev/null +++ b/extensions/fairygui/gears/GearIcon.h @@ -0,0 +1,32 @@ +#ifndef __GEARICON_H__ +#define __GEARICON_H__ + +#include "FairyGUIMacros.h" +#include "GearBase.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; + +class GearIcon : public GearBase +{ +public: + GearIcon(GObject* owner); + virtual ~GearIcon(); + + void apply() override; + void updateState() override; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + std::unordered_map _storage; + std::string _default; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearLook.cpp b/extensions/fairygui/gears/GearLook.cpp new file mode 100644 index 0000000000..d8cae791de --- /dev/null +++ b/extensions/fairygui/gears/GearLook.cpp @@ -0,0 +1,133 @@ +#include "GearLook.h" +#include "GObject.h" +#include "UIPackage.h" +#include "tween/GTween.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearLook::GearLookValue::GearLookValue() : alpha(0), rotation(0), grayed(false), touchable(false) +{ +} + +GearLook::GearLookValue::GearLookValue(float alpha, float rotation, bool grayed, bool touchable) +{ + this->alpha = alpha; + this->rotation = rotation; + this->grayed = grayed; + this->touchable = touchable; +} + +GearLook::GearLook(GObject* owner) : GearBase(owner) +{ +} + +GearLook::~GearLook() +{ +} + +void GearLook::init() +{ + _default = GearLookValue(_owner->getAlpha(), _owner->getRotation(), + _owner->isGrayed(), _owner->isTouchable()); + _storage.clear(); +} + +void GearLook::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ + GearLookValue gv; + gv.alpha = buffer->readFloat(); + gv.rotation = buffer->readFloat(); + gv.grayed = buffer->readBool(); + gv.touchable = buffer->readBool(); + + if (pageId.size() == 0) + _default = gv; + else + _storage[pageId] = gv; +} + +void GearLook::apply() +{ + GearLookValue gv; + auto it = _storage.find(_controller->getSelectedPageId()); + if (it != _storage.end()) + gv = it->second; + else + gv = _default; + + if (_tweenConfig && _tweenConfig->tween && UIPackage::_constructing == 0 && !disableAllTweenEffect) + { + _owner->_gearLocked = true; + _owner->setGrayed(gv.grayed); + _owner->setTouchable(gv.touchable); + _owner->_gearLocked = false; + + if (_tweenConfig->_tweener != nullptr) + { + if (_tweenConfig->_tweener->endValue.x != gv.alpha || _tweenConfig->_tweener->endValue.y != gv.rotation) + { + _tweenConfig->_tweener->kill(true); + _tweenConfig->_tweener = nullptr; + } + else + return; + } + + bool a = gv.alpha != _owner->getAlpha(); + bool b = gv.rotation != _owner->getRotation(); + if (a || b) + { + if (_owner->checkGearController(0, _controller)) + _tweenConfig->_displayLockToken = _owner->addDisplayLock(); + + _tweenConfig->_tweener = GTween::to(Vec2(_owner->getAlpha(), _owner->getRotation()), Vec2(gv.alpha, gv.rotation), _tweenConfig->duration) + ->setDelay(_tweenConfig->delay) + ->setEase(_tweenConfig->easeType) + ->setTargetAny(this) + ->setUserData(Value((a ? 1 : 0) + (b ? 2 : 0))) + ->onUpdate(CC_CALLBACK_1(GearLook::onTweenUpdate, this)) + ->onComplete(CC_CALLBACK_0(GearLook::onTweenComplete, this)); + } + } + else + { + _owner->_gearLocked = true; + _owner->setAlpha(gv.alpha); + _owner->setRotation(gv.rotation); + _owner->setGrayed(gv.grayed); + _owner->setTouchable(gv.touchable); + _owner->_gearLocked = false; + } +} + +void GearLook::onTweenUpdate(GTweener* tweener) +{ + int flag = _tweenConfig->_tweener->getUserData().asInt(); + _owner->_gearLocked = true; + if ((flag & 1) != 0) + _owner->setAlpha(_tweenConfig->_tweener->value.x); + if ((flag & 2) != 0) + _owner->setRotation(_tweenConfig->_tweener->value.y); + _owner->_gearLocked = false; +} + +void GearLook::onTweenComplete() +{ + if (_tweenConfig->_displayLockToken != 0) + { + _owner->releaseDisplayLock(_tweenConfig->_displayLockToken); + _tweenConfig->_displayLockToken = 0; + } + _tweenConfig->_tweener = nullptr; + _owner->dispatchEvent(UIEventType::GearStop); +} + +void GearLook::updateState() +{ + _storage[_controller->getSelectedPageId()] = GearLookValue(_owner->getAlpha(), _owner->getRotation(), + _owner->isGrayed(), _owner->isTouchable()); +} + +NS_FGUI_END diff --git a/extensions/fairygui/gears/GearLook.h b/extensions/fairygui/gears/GearLook.h new file mode 100644 index 0000000000..38a5754416 --- /dev/null +++ b/extensions/fairygui/gears/GearLook.h @@ -0,0 +1,48 @@ +#ifndef __GEARLOOK_H__ +#define __GEARLOOK_H__ + +#include "FairyGUIMacros.h" +#include "GearBase.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; +class GTweener; + +class GearLook : public GearBase +{ +public: + GearLook(GObject* owner); + virtual ~GearLook(); + + void apply() override; + void updateState() override; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + void onTweenUpdate(GTweener* tweener); + void onTweenComplete(); + + class GearLookValue + { + public: + float alpha; + float rotation; + bool grayed; + bool touchable; + + GearLookValue(); + GearLookValue(float alpha, float rotation, bool grayed, bool touchable); + }; + + std::unordered_map _storage; + GearLookValue _default; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearSize.cpp b/extensions/fairygui/gears/GearSize.cpp new file mode 100644 index 0000000000..37637baf4d --- /dev/null +++ b/extensions/fairygui/gears/GearSize.cpp @@ -0,0 +1,130 @@ +#include "GearSize.h" +#include "GObject.h" +#include "UIPackage.h" +#include "tween/GTween.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearSize::GearSize(GObject* owner) : GearBase(owner) +{ +} + +GearSize::~GearSize() +{ +} + +void GearSize::init() +{ + _default = Vec4(_owner->getWidth(), _owner->getHeight(), + _owner->getScaleX(), _owner->getScaleY()); + _storage.clear(); +} + +void GearSize::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ + Vec4 gv; + gv.x = buffer->readInt(); + gv.y = buffer->readInt(); + gv.z = buffer->readFloat(); + gv.w = buffer->readFloat(); + + if (pageId.size() == 0) + _default = gv; + else + _storage[pageId] = gv; +} + +void GearSize::apply() +{ + Vec4 gv; + auto it = _storage.find(_controller->getSelectedPageId()); + if (it != _storage.end()) + gv = it->second; + else + gv = _default; + + if (_tweenConfig && _tweenConfig->tween && UIPackage::_constructing == 0 && !disableAllTweenEffect) + { + if (_tweenConfig->_tweener != nullptr) + { + if (_tweenConfig->_tweener->endValue.getVec4() != gv) + { + _tweenConfig->_tweener->kill(true); + _tweenConfig->_tweener = nullptr; + } + else + return; + } + + bool a = gv.x != _owner->getWidth() || gv.y != _owner->getHeight(); + bool b = gv.z != _owner->getScaleX() || gv.w != _owner->getScaleY(); + if (a || b) + { + if (_owner->checkGearController(0, _controller)) + _tweenConfig->_displayLockToken = _owner->addDisplayLock(); + + _tweenConfig->_tweener = GTween::to(Vec4(_owner->getWidth(), _owner->getHeight(), _owner->getScaleX(), _owner->getScaleY()), gv, _tweenConfig->duration) + ->setDelay(_tweenConfig->delay) + ->setEase(_tweenConfig->easeType) + ->setTargetAny(this) + ->setUserData(Value((a ? 1 : 0) + (b ? 2 : 0))) + ->onUpdate(CC_CALLBACK_1(GearSize::onTweenUpdate, this)) + ->onComplete(CC_CALLBACK_0(GearSize::onTweenComplete, this)); + } + } + else + { + _owner->_gearLocked = true; + _owner->setSize(gv.x, gv.y, _owner->checkGearController(1, _controller)); + _owner->setScale(gv.z, gv.w); + _owner->_gearLocked = false; + } +} + +void GearSize::onTweenUpdate(GTweener* tweener) +{ + int flag = tweener->getUserData().asInt(); + _owner->_gearLocked = true; + if ((flag & 1) != 0) + _owner->setSize(tweener->value.x, tweener->value.y, _owner->checkGearController(1, _controller)); + if ((flag & 2) != 0) + _owner->setScale(tweener->value.z, tweener->value.w); + _owner->_gearLocked = false; +} + +void GearSize::onTweenComplete() +{ + if (_tweenConfig->_displayLockToken != 0) + { + _owner->releaseDisplayLock(_tweenConfig->_displayLockToken); + _tweenConfig->_displayLockToken = 0; + } + _tweenConfig->_tweener = nullptr; + _owner->dispatchEvent(UIEventType::GearStop); +} + +void GearSize::updateState() +{ + _storage[_controller->getSelectedPageId()] = Vec4(_owner->getWidth(), _owner->getHeight(), + _owner->getScaleX(), _owner->getScaleY()); +} + +void GearSize::updateFromRelations(float dx, float dy) +{ + if (_controller != nullptr && !_storage.empty()) + { + for (auto it = _storage.begin(); it != _storage.end(); ++it) + { + it->second = Vec4(it->second.x + dx, it->second.y + dy, + it->second.z, it->second.w); + } + _default.x += dx; + _default.y += dy; + + updateState(); + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearSize.h b/extensions/fairygui/gears/GearSize.h new file mode 100644 index 0000000000..ec832fae65 --- /dev/null +++ b/extensions/fairygui/gears/GearSize.h @@ -0,0 +1,37 @@ +#ifndef __GEARSIZE_H__ +#define __GEARSIZE_H__ + +#include "FairyGUIMacros.h" +#include "GearBase.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; +class GTweener; + +class GearSize : public GearBase +{ +public: + GearSize(GObject* owner); + virtual ~GearSize(); + + void apply() override; + void updateState() override; + void updateFromRelations(float dx, float dy) override; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + void onTweenUpdate(GTweener* tweener); + void onTweenComplete(); + + std::unordered_map _storage; + cocos2d::Vec4 _default; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearText.cpp b/extensions/fairygui/gears/GearText.cpp new file mode 100644 index 0000000000..f8711a99f4 --- /dev/null +++ b/extensions/fairygui/gears/GearText.cpp @@ -0,0 +1,48 @@ +#include "GearText.h" +#include "GObject.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearText::GearText(GObject* owner) : GearBase(owner) +{ +} + +GearText::~GearText() +{ +} + +void GearText::init() +{ + _default = _owner->getText(); + _storage.clear(); +} + +void GearText::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ + if (pageId.length() == 0) + _default = buffer->readS(); + else + _storage[pageId] = buffer->readS(); +} + +void GearText::apply() +{ + _owner->_gearLocked = true; + + auto it = _storage.find(_controller->getSelectedPageId()); + if (it != _storage.end()) + _owner->setText(it->second); + else + _owner->setText(_default); + + _owner->_gearLocked = false; +} + +void GearText::updateState() +{ + _storage[_controller->getSelectedPageId()] = _owner->getText(); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearText.h b/extensions/fairygui/gears/GearText.h new file mode 100644 index 0000000000..81b18c3358 --- /dev/null +++ b/extensions/fairygui/gears/GearText.h @@ -0,0 +1,32 @@ +#ifndef __GEARTEXT_H__ +#define __GEARTEXT_H__ + +#include "FairyGUIMacros.h" +#include "GearBase.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; + +class GearText : public GearBase +{ +public: + GearText(GObject* owner); + virtual ~GearText(); + + void apply() override; + void updateState() override; + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + std::unordered_map _storage; + std::string _default; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/gears/GearXY.cpp b/extensions/fairygui/gears/GearXY.cpp new file mode 100644 index 0000000000..7977c41217 --- /dev/null +++ b/extensions/fairygui/gears/GearXY.cpp @@ -0,0 +1,151 @@ +#include "GearXY.h" +#include "GComponent.h" +#include "UIPackage.h" +#include "tween/GTween.h" +#include "utils/ByteBuffer.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GearXY::GearXY(GObject* owner) + : GearBase(owner), + positionsInPercent(false) +{ +} + +GearXY::~GearXY() +{ +} + +void GearXY::init() +{ + _default = Vec4(_owner->getX(), + _owner->getY(), + _owner->getX() / _owner->getParent()->getWidth(), + _owner->getY() / _owner->getParent()->getHeight()); + _storage.clear(); +} + +void GearXY::addStatus(const std::string& pageId, ByteBuffer* buffer) +{ + Vec4 gv; + gv.x = buffer->readInt(); + gv.y = buffer->readInt(); + + if (pageId.size() == 0) + _default = gv; + else + _storage[pageId] = gv; +} + +void GearXY::addExtStatus(const std::string& pageId, ByteBuffer* buffer) +{ + Vec4* gv; + if (pageId.empty()) + gv = &_default; + else + gv = &_storage[pageId]; + gv->z = buffer->readFloat(); + gv->w = buffer->readFloat(); +} + +void GearXY::apply() +{ + Vec4 gv; + auto it = _storage.find(_controller->getSelectedPageId()); + if (it != _storage.end()) + gv = it->second; + else + gv = _default; + + Vec2 endPt; + + if (positionsInPercent && _owner->getParent()) + { + endPt.x = gv.z * _owner->getParent()->getWidth(); + endPt.y = gv.w * _owner->getParent()->getHeight(); + } + else + { + endPt.x = gv.x; + endPt.y = gv.y; + } + + if (_tweenConfig && _tweenConfig->tween && UIPackage::_constructing == 0 && !disableAllTweenEffect) + { + if (_tweenConfig->_tweener != nullptr) + { + if (_tweenConfig->_tweener->endValue.x != endPt.x || _tweenConfig->_tweener->endValue.y != endPt.y) + { + _tweenConfig->_tweener->kill(true); + _tweenConfig->_tweener = nullptr; + } + else + return; + } + Vec2 originPt(_owner->getX(), _owner->getY()); + + if (originPt != endPt) + { + if (_owner->checkGearController(0, _controller)) + _tweenConfig->_displayLockToken = _owner->addDisplayLock(); + + _tweenConfig->_tweener = GTween::to(originPt, endPt, _tweenConfig->duration) + ->setDelay(_tweenConfig->delay) + ->setEase(_tweenConfig->easeType) + ->setTargetAny(this) + ->onUpdate(CC_CALLBACK_1(GearXY::onTweenUpdate, this)) + ->onComplete(CC_CALLBACK_0(GearXY::onTweenComplete, this)); + } + } + else + { + _owner->_gearLocked = true; + _owner->setPosition(endPt.x, endPt.y); + _owner->_gearLocked = false; + } +} + +void GearXY::onTweenUpdate(GTweener* tweener) +{ + _owner->_gearLocked = true; + _owner->setPosition(_tweenConfig->_tweener->value.x, _tweenConfig->_tweener->value.y); + _owner->_gearLocked = false; +} + +void GearXY::onTweenComplete() +{ + if (_tweenConfig->_displayLockToken != 0) + { + _owner->releaseDisplayLock(_tweenConfig->_displayLockToken); + _tweenConfig->_displayLockToken = 0; + } + _tweenConfig->_tweener = nullptr; + _owner->dispatchEvent(UIEventType::GearStop); +} + +void GearXY::updateState() +{ + _storage[_controller->getSelectedPageId()] = Vec4( + _owner->getX(), + _owner->getY(), + _owner->getX() / _owner->getParent()->getWidth(), + _owner->getY() / _owner->getParent()->getHeight()); +} + +void GearXY::updateFromRelations(float dx, float dy) +{ + if (_controller != nullptr && !_storage.empty() && !positionsInPercent) + { + for (auto it = _storage.begin(); it != _storage.end(); ++it) + { + it->second = Vec4(it->second.x + dx, it->second.y + dy, it->second.z, it->second.w); + } + _default.x += dx; + _default.y += dy; + + updateState(); + } +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/gears/GearXY.h b/extensions/fairygui/gears/GearXY.h new file mode 100644 index 0000000000..25fdcb4b3f --- /dev/null +++ b/extensions/fairygui/gears/GearXY.h @@ -0,0 +1,40 @@ +#ifndef __GEARXY_H__ +#define __GEARXY_H__ + +#include "FairyGUIMacros.h" +#include "GearBase.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GObject; +class GTweener; + +class GearXY : public GearBase +{ +public: + GearXY(GObject* owner); + virtual ~GearXY(); + + void apply() override; + void updateState() override; + void updateFromRelations(float dx, float dy) override; + + bool positionsInPercent; + void addExtStatus(const std::string& pageId, ByteBuffer* buffer); + +protected: + void addStatus(const std::string& pageId, ByteBuffer* buffer) override; + void init() override; + +private: + void onTweenUpdate(GTweener* tweener); + void onTweenComplete(); + + std::unordered_map _storage; + cocos2d::Vec4 _default; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/tween/EaseManager.cpp b/extensions/fairygui/tween/EaseManager.cpp new file mode 100644 index 0000000000..3105bc5873 --- /dev/null +++ b/extensions/fairygui/tween/EaseManager.cpp @@ -0,0 +1,200 @@ +#include "EaseManager.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +static const float _PiOver2 = (float)(M_PI * 0.5f); +static const float _TwoPi = (float)(M_PI * 2); + +class Bounce +{ +public: + static float easeIn(float time, float duration); + static float easeOut(float time, float duration); + static float easeInOut(float time, float duration); +}; + +float EaseManager::evaluate(EaseType easeType, float time, float duration, float overshootOrAmplitude, float period) +{ + switch (easeType) + { + case EaseType::Linear: + return time / duration; + case EaseType::SineIn: + return -(float)cos(time / duration * _PiOver2) + 1; + case EaseType::SineOut: + return (float)sin(time / duration * _PiOver2); + case EaseType::SineInOut: + return -0.5f * ((float)cos(M_PI * time / duration) - 1); + case EaseType::QuadIn: + time /= duration; + return time * time; + case EaseType::QuadOut: + time /= duration; + return -time * (time - 2); + case EaseType::QuadInOut: + time /= duration * 0.5f; + if (time < 1) return 0.5f * time * time; + --time; + return -0.5f * (time * (time - 2) - 1); + case EaseType::CubicIn: + time /= duration; + return time * time * time; + case EaseType::CubicOut: + time = time / duration - 1; + return time * time * time + 1; + case EaseType::CubicInOut: + time /= duration * 0.5f; + if (time < 1) return 0.5f * time * time * time; + time -= 2; + return 0.5f * (time * time * time + 2); + case EaseType::QuartIn: + time /= duration; + return time * time * time * time; + case EaseType::QuartOut: + time = time / duration - 1; + return -(time * time * time * time - 1); + case EaseType::QuartInOut: + time /= duration * 0.5f; + if (time < 1) return 0.5f * time * time * time * time; + time -= 2; + return -0.5f * (time * time * time * time - 2); + case EaseType::QuintIn: + time /= duration; + return time * time * time * time * time; + case EaseType::QuintOut: + time = time / duration - 1; + return (time * time * time * time * time + 1); + case EaseType::QuintInOut: + time /= duration * 0.5f; + if (time < 1) return 0.5f * time * time * time * time * time; + time -= 2; + return 0.5f * (time * time * time * time * time + 2); + case EaseType::ExpoIn: + return (time == 0) ? 0 : (float)pow(2, 10 * (time / duration - 1)); + case EaseType::ExpoOut: + if (time == duration) return 1; + return (-(float)pow(2, -10 * time / duration) + 1); + case EaseType::ExpoInOut: + if (time == 0) return 0; + if (time == duration) return 1; + if ((time /= duration * 0.5f) < 1) return 0.5f * (float)pow(2, 10 * (time - 1)); + return 0.5f * (-(float)pow(2, -10 * --time) + 2); + case EaseType::CircIn: + time /= duration; + return -((float)sqrt(1 - time * time) - 1); + case EaseType::CircOut: + time = time / duration - 1; + return (float)sqrt(1 - time * time); + case EaseType::CircInOut: + time /= duration * 0.5f; + if (time < 1) return -0.5f * ((float)sqrt(1 - time * time) - 1); + time -= 2; + return 0.5f * ((float)sqrt(1 - time * time) + 1); + case EaseType::ElasticIn: + float s0; + if (time == 0) return 0; + if ((time /= duration) == 1) return 1; + if (period == 0) period = duration * 0.3f; + if (overshootOrAmplitude < 1) + { + overshootOrAmplitude = 1; + s0 = period / 4; + } + else s0 = period / _TwoPi * (float)asin(1 / overshootOrAmplitude); + time -= 1; + return -(overshootOrAmplitude * (float)pow(2, 10 * time) * (float)sin((time * duration - s0) * _TwoPi / period)); + case EaseType::ElasticOut: + float s1; + if (time == 0) return 0; + if ((time /= duration) == 1) return 1; + if (period == 0) period = duration * 0.3f; + if (overshootOrAmplitude < 1) + { + overshootOrAmplitude = 1; + s1 = period / 4; + } + else s1 = period / _TwoPi * (float)asin(1 / overshootOrAmplitude); + return (overshootOrAmplitude * (float)pow(2, -10 * time) * (float)sin((time * duration - s1) * _TwoPi / period) + 1); + case EaseType::ElasticInOut: + float s; + if (time == 0) return 0; + if ((time /= duration * 0.5f) == 2) return 1; + if (period == 0) period = duration * (0.3f * 1.5f); + if (overshootOrAmplitude < 1) + { + overshootOrAmplitude = 1; + s = period / 4; + } + else s = period / _TwoPi * (float)asin(1 / overshootOrAmplitude); + if (time < 1) + { + time -= 1; + return -0.5f * (overshootOrAmplitude * (float)pow(2, 10 * time) * (float)sin((time * duration - s) * _TwoPi / period)); + } + + time -= 1; + return overshootOrAmplitude * (float)pow(2, -10 * time) * (float)sin((time * duration - s) * _TwoPi / period) * 0.5f + 1; + case EaseType::BackIn: + time /= duration; + return time * time * ((overshootOrAmplitude + 1) * time - overshootOrAmplitude); + case EaseType::BackOut: + time = time / duration - 1; + return (time * time * ((overshootOrAmplitude + 1) * time + overshootOrAmplitude) + 1); + case EaseType::BackInOut: + time /= duration * 0.5f; + overshootOrAmplitude *= (1.525f); + if (time < 1) return 0.5f * (time * time * ((overshootOrAmplitude + 1) * time - overshootOrAmplitude)); + time -= 2; + return 0.5f * (time * time * ((overshootOrAmplitude + 1) * time + overshootOrAmplitude) + 2); + case EaseType::BounceIn: + return Bounce::easeIn(time, duration); + case EaseType::BounceOut: + return Bounce::easeOut(time, duration); + case EaseType::BounceInOut: + return Bounce::easeInOut(time, duration); + + default: + time /= duration; + return -time * (time - 2); + } +} + +float Bounce::easeIn(float time, float duration) +{ + return 1 - easeOut(duration - time, duration); +} + +float Bounce::easeOut(float time, float duration) +{ + time /= duration; + if (time < (1 / 2.75f)) + { + return (7.5625f * time * time); + } + if (time < (2 / 2.75f)) + { + time -= (1.5f / 2.75f); + return (7.5625f * time * time + 0.75f); + } + if (time < (2.5f / 2.75f)) + { + time -= (2.25f / 2.75f); + return (7.5625f * time * time + 0.9375f); + } + time -= (2.625f / 2.75f); + return (7.5625f * time * time + 0.984375f); +} + +float Bounce::easeInOut(float time, float duration) +{ + if (time < duration * 0.5f) + { + return easeIn(time * 2, duration) * 0.5f; + } + return easeOut(time * 2 - duration, duration) * 0.5f + 0.5f; +} + +NS_FGUI_END + diff --git a/extensions/fairygui/tween/EaseManager.h b/extensions/fairygui/tween/EaseManager.h new file mode 100644 index 0000000000..4757820568 --- /dev/null +++ b/extensions/fairygui/tween/EaseManager.h @@ -0,0 +1,17 @@ +#ifndef __EASE_MANAGER_H__ +#define __EASE_MANAGER_H__ + +#include "FairyGUIMacros.h" +#include "EaseType.h" + +NS_FGUI_BEGIN + +class EaseManager +{ +public: + static float evaluate(EaseType easeType, float time, float duration, float overshootOrAmplitude, float period); +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/tween/EaseType.h b/extensions/fairygui/tween/EaseType.h new file mode 100644 index 0000000000..cb3f7a84e5 --- /dev/null +++ b/extensions/fairygui/tween/EaseType.h @@ -0,0 +1,46 @@ +#ifndef __EASETYPE_H__ +#define __EASETYPE_H__ + +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +enum class EaseType +{ + Linear, + SineIn, + SineOut, + SineInOut, + QuadIn, + QuadOut, + QuadInOut, + CubicIn, + CubicOut, + CubicInOut, + QuartIn, + QuartOut, + QuartInOut, + QuintIn, + QuintOut, + QuintInOut, + ExpoIn, + ExpoOut, + ExpoInOut, + CircIn, + CircOut, + CircInOut, + ElasticIn, + ElasticOut, + ElasticInOut, + BackIn, + BackOut, + BackInOut, + BounceIn, + BounceOut, + BounceInOut, + Custom +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/tween/GPath.cpp b/extensions/fairygui/tween/GPath.cpp new file mode 100644 index 0000000000..e3da6034d4 --- /dev/null +++ b/extensions/fairygui/tween/GPath.cpp @@ -0,0 +1,292 @@ +#include "GPath.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +static std::vector splinePoints; + +GPathPoint::GPathPoint(const Vec3& pos) +{ + this->pos = pos; + this->control1 = Vec3::ZERO; + this->control2 = Vec3::ZERO; + this->curveType = CurveType::CRSpline; +} + +GPathPoint::GPathPoint(const Vec3& pos, const Vec3& control) +{ + this->pos = pos; + this->control1 = control; + this->control2 = Vec3::ZERO; + this->curveType = CurveType::Bezier; +} + +GPathPoint::GPathPoint(const Vec3& pos, const Vec3& control1, const Vec3& control2) +{ + this->pos = pos; + this->control1 = control1; + this->control2 = control2; + this->curveType = CurveType::CubicBezier; +} + +GPathPoint::GPathPoint(const Vec3& pos, CurveType curveType) +{ + this->pos = pos; + this->control1 = Vec3::ZERO; + this->control2 = Vec3::ZERO; + this->curveType = curveType; +} + +GPath::GPath() + : _fullLength(0) +{ +} + +void GPath::create(GPathPoint* points, int count) +{ + _segments.clear(); + _points.clear(); + splinePoints.clear(); + _fullLength = 0; + + if (count == 0) + return; + + const GPathPoint* prev = points; + if (prev->curveType == GPathPoint::CurveType::CRSpline) + splinePoints.push_back(prev->pos); + + for (int i = 1; i < count; i++) + { + const GPathPoint* current = points + i; + + if (prev->curveType != GPathPoint::CurveType::CRSpline) + { + Segment seg; + seg.type = prev->curveType; + seg.ptStart = (int)_points.size(); + if (prev->curveType == GPathPoint::CurveType::Straight) + { + seg.ptCount = 2; + _points.push_back(prev->pos); + _points.push_back(current->pos); + } + else if (prev->curveType == GPathPoint::CurveType::Bezier) + { + seg.ptCount = 3; + _points.push_back(prev->pos); + _points.push_back(current->pos); + _points.push_back(prev->control1); + } + else if (prev->curveType == GPathPoint::CurveType::CubicBezier) + { + seg.ptCount = 4; + _points.push_back(prev->pos); + _points.push_back(current->pos); + _points.push_back(prev->control1); + _points.push_back(prev->control2); + } + seg.length = prev->pos.distance(current->pos); + _fullLength += seg.length; + _segments.push_back(seg); + } + + if (current->curveType != GPathPoint::CurveType::CRSpline) + { + if (splinePoints.size() > 0) + { + splinePoints.push_back(current->pos); + createSplineSegment(); + } + } + else + splinePoints.push_back(current->pos); + + prev = current; + } + + if (splinePoints.size() > 1) + createSplineSegment(); +} + +void GPath::createSplineSegment() +{ + int cnt = (int)splinePoints.size(); + splinePoints.insert(splinePoints.begin(), splinePoints[0]); + splinePoints.push_back(splinePoints[cnt]); + splinePoints.push_back(splinePoints[cnt]); + cnt += 3; + + Segment seg; + seg.type = GPathPoint::CurveType::CRSpline; + seg.ptStart = (int)_points.size(); + seg.ptCount = cnt; + for (auto& it : splinePoints) + _points.push_back(it); + + seg.length = 0; + for (int i = 1; i < cnt; i++) + seg.length += splinePoints[i - 1].distance(splinePoints[i]); + _fullLength += seg.length; + _segments.push_back(seg); + splinePoints.clear(); +} + +void GPath::clear() +{ + _segments.clear(); + _points.clear(); +} + +Vec3 GPath::getPointAt(float t) +{ + t = clampf(t, 0, 1); + int cnt = (int)_segments.size(); + if (cnt == 0) + return Vec3::ZERO; + + Segment seg; + if (t == 1) + { + seg = _segments[cnt - 1]; + + if (seg.type == GPathPoint::CurveType::Straight) + return _points[seg.ptStart].lerp(_points[seg.ptStart + 1], t); + else if (seg.type == GPathPoint::CurveType::Bezier || seg.type == GPathPoint::CurveType::CubicBezier) + return onBezierCurve(seg.ptStart, seg.ptCount, t); + else + return onCRSplineCurve(seg.ptStart, seg.ptCount, t); + } + + float len = t * _fullLength; + Vec3 pt; + for (int i = 0; i < cnt; i++) + { + seg = _segments[i]; + + len -= seg.length; + if (len < 0) + { + t = 1 + len / seg.length; + + if (seg.type == GPathPoint::CurveType::Straight) + pt = _points[seg.ptStart].lerp(_points[seg.ptStart + 1], t); + else if (seg.type == GPathPoint::CurveType::Bezier || seg.type == GPathPoint::CurveType::CubicBezier) + pt = onBezierCurve(seg.ptStart, seg.ptCount, t); + else + pt = onCRSplineCurve(seg.ptStart, seg.ptCount, t); + + break; + } + } + + return pt; +} + +float GPath::getSegmentLength(int segmentIndex) +{ + return _segments[segmentIndex].length; +} + +void GPath::getPointsInSegment(int segmentIndex, float t0, float t1, + std::vector& points, std::vector* ts, float pointDensity) +{ + if (ts != nullptr) + ts->push_back(t0); + Segment seg = _segments[segmentIndex]; + if (seg.type == GPathPoint::CurveType::Straight) + { + points.push_back(_points[seg.ptStart].lerp(_points[seg.ptStart + 1], t0)); + points.push_back(_points[seg.ptStart].lerp(_points[seg.ptStart + 1], t1)); + } + else if (seg.type == GPathPoint::CurveType::Bezier || seg.type == GPathPoint::CurveType::CubicBezier) + { + points.push_back(onBezierCurve(seg.ptStart, seg.ptCount, t0)); + int SmoothAmount = (int)MIN(seg.length * pointDensity, 50); + for (int j = 0; j <= SmoothAmount; j++) + { + float t = (float)j / SmoothAmount; + if (t > t0 && t < t1) + { + points.push_back(onBezierCurve(seg.ptStart, seg.ptCount, t)); + if (ts != nullptr) + ts->push_back(t); + } + } + points.push_back(onBezierCurve(seg.ptStart, seg.ptCount, t1)); + } + else + { + points.push_back(onCRSplineCurve(seg.ptStart, seg.ptCount, t0)); + int SmoothAmount = (int)MIN(seg.length * pointDensity, 50); + for (int j = 0; j <= SmoothAmount; j++) + { + float t = (float)j / SmoothAmount; + if (t > t0 && t < t1) + { + points.push_back(onCRSplineCurve(seg.ptStart, seg.ptCount, t)); + if (ts != nullptr) + ts->push_back(t); + } + } + points.push_back(onCRSplineCurve(seg.ptStart, seg.ptCount, t1)); + } + + if (ts != nullptr) + ts->push_back(t1); +} + +void GPath::getAllPoints(std::vector& points, float pointDensity) +{ + int cnt = (int)_segments.size(); + for (int i = 0; i < cnt; i++) + getPointsInSegment(i, 0, 1, points, nullptr, pointDensity); +} + +static float repeat(float t, float length) +{ + return t - floor(t / length) * length; +} + +Vec3 GPath::onCRSplineCurve(int ptStart, int ptCount, float t) +{ + int adjustedIndex = floor(t * (ptCount - 4)) + ptStart; //Since the equation works with 4 points, we adjust the starting point depending on t to return a point on the specific segment + + Vec3 result; + + Vec3 p0 = _points[adjustedIndex]; + Vec3 p1 = _points[adjustedIndex + 1]; + Vec3 p2 = _points[adjustedIndex + 2]; + Vec3 p3 = _points[adjustedIndex + 3]; + + float adjustedT = (t == 1.f) ? 1.f : repeat(t * (ptCount - 4), 1.f); // Then we adjust t to be that value on that new piece of segment... for t == 1f don't use repeat (that would return 0f); + + float t0 = ((-adjustedT + 2.f) * adjustedT - 1.f) * adjustedT * 0.5f; + float t1 = (((3.f * adjustedT - 5.f) * adjustedT) * adjustedT + 2.f) * 0.5f; + float t2 = ((-3.f * adjustedT + 4.f) * adjustedT + 1.f) * adjustedT * 0.5f; + float t3 = ((adjustedT - 1.f) * adjustedT * adjustedT) * 0.5f; + + result.x = p0.x * t0 + p1.x * t1 + p2.x * t2 + p3.x * t3; + result.y = p0.y * t0 + p1.y * t1 + p2.y * t2 + p3.y * t3; + result.z = p0.z * t0 + p1.z * t1 + p2.z * t2 + p3.z * t3; + + return result; +} + +Vec3 GPath::onBezierCurve(int ptStart, int ptCount, float t) +{ + float t2 = 1.0f - t; + Vec3 p0 = _points[ptStart]; + Vec3 p1 = _points[ptStart + 1]; + Vec3 cp0 = _points[ptStart + 2]; + + if (ptCount == 4) + { + Vec3 cp1 = _points[ptStart + 3]; + return t2 * t2 * t2 * p0 + 3.f * t2 * t2 * t * cp0 + 3.f * t2 * t * t * cp1 + t * t * t * p1; + } + else + return t2 * t2 * p0 + 2.f * t2 * t * cp0 + t * t * p1; +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/tween/GPath.h b/extensions/fairygui/tween/GPath.h new file mode 100644 index 0000000000..87b11234d9 --- /dev/null +++ b/extensions/fairygui/tween/GPath.h @@ -0,0 +1,65 @@ +#ifndef __GPATH_H__ +#define __GPATH_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +struct GPathPoint +{ + enum class CurveType + { + CRSpline, + Bezier, + CubicBezier, + Straight + }; + + cocos2d::Vec3 pos; + cocos2d::Vec3 control1; + cocos2d::Vec3 control2; + CurveType curveType; + + GPathPoint(const cocos2d::Vec3& pos); + GPathPoint(const cocos2d::Vec3& pos, const cocos2d::Vec3& control); + GPathPoint(const cocos2d::Vec3& pos, const cocos2d::Vec3& control1, const cocos2d::Vec3& control2); + GPathPoint(const cocos2d::Vec3& pos, CurveType curveType); +}; + +class GPath +{ +public: + GPath(); + void create(GPathPoint* points, int count); + void clear(); + cocos2d::Vec3 getPointAt(float t); + + float getLength() { return _fullLength; } + int getSegmentCount() { return (int)_segments.size(); } + float getSegmentLength(int segmentIndex); + void getPointsInSegment(int segmentIndex, float t0, float t1, + std::vector& points, std::vector* ts = nullptr, float pointDensity = 0.1f); + void getAllPoints(std::vector& points, float pointDensity = 0.1f); + + struct Segment + { + GPathPoint::CurveType type; + float length; + int ptStart; + int ptCount; + }; + +private: + void createSplineSegment(); + cocos2d::Vec3 onCRSplineCurve(int ptStart, int ptCount, float t); + cocos2d::Vec3 onBezierCurve(int ptStart, int ptCount, float t); + + std::vector _segments; + std::vector _points; + float _fullLength; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/tween/GTween.cpp b/extensions/fairygui/tween/GTween.cpp new file mode 100644 index 0000000000..88da64a226 --- /dev/null +++ b/extensions/fairygui/tween/GTween.cpp @@ -0,0 +1,87 @@ +#include "GTween.h" +#include "TweenManager.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GTweener* GTween::to(float startValue, float endValue, float duration) +{ + return TweenManager::createTween()->_to(startValue, endValue, duration); +} + +GTweener* GTween::to(const cocos2d::Vec2& startValue, const cocos2d::Vec2 & endValue, float duration) +{ + return TweenManager::createTween()->_to(startValue, endValue, duration); +} + +GTweener* GTween::to(const cocos2d::Vec3& startValue, const cocos2d::Vec3 & endValue, float duration) +{ + return TweenManager::createTween()->_to(startValue, endValue, duration); +} + +GTweener* GTween::to(const cocos2d::Vec4& startValue, const cocos2d::Vec4 & endValue, float duration) +{ + return TweenManager::createTween()->_to(startValue, endValue, duration); +} + +GTweener* GTween::to(const cocos2d::Color4B& startValue, const cocos2d::Color4B & endValue, float duration) +{ + return TweenManager::createTween()->_to(startValue, endValue, duration); +} + +GTweener* GTween::toDouble(double startValue, double endValue, float duration) +{ + return TweenManager::createTween()->_to(startValue, endValue, duration); +} + +GTweener* GTween::delayedCall(float delay) +{ + return TweenManager::createTween()->setDelay(delay); +} + +GTweener* GTween::shake(const cocos2d::Vec2& startValue, float amplitude, float duration) +{ + return TweenManager::createTween()->_shake(startValue, amplitude, duration); +} + +bool GTween::isTweening(cocos2d::Ref * target) +{ + return TweenManager::isTweening(target, TweenPropType::None); +} + +bool GTween::isTweening(cocos2d::Ref * target, TweenPropType propType) +{ + return TweenManager::isTweening(target, propType); +} + +void GTween::kill(cocos2d::Ref * target) +{ + TweenManager::killTweens(target, TweenPropType::None, false); +} + +void GTween::kill(cocos2d::Ref * target, bool complete) +{ + TweenManager::killTweens(target, TweenPropType::None, complete); +} + +void GTween::kill(cocos2d::Ref * target, TweenPropType propType, bool complete) +{ + TweenManager::killTweens(target, propType, complete); +} + +GTweener* GTween::getTween(cocos2d::Ref * target) +{ + return TweenManager::getTween(target, TweenPropType::None); +} + +GTweener* GTween::getTween(cocos2d::Ref * target, TweenPropType propType) +{ + return TweenManager::getTween(target, propType); +} + +void GTween::clean() +{ + TweenManager::clean(); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/tween/GTween.h b/extensions/fairygui/tween/GTween.h new file mode 100644 index 0000000000..8e6d16a208 --- /dev/null +++ b/extensions/fairygui/tween/GTween.h @@ -0,0 +1,36 @@ +#ifndef __GTWEEN_H__ +#define __GTWEEN_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "GTweener.h" +#include "EaseType.h" +#include "TweenValue.h" +#include "TweenPropType.h" + +NS_FGUI_BEGIN + +class GTween +{ +public: + static GTweener* to(float startValue, float endValue, float duration); + static GTweener* to(const cocos2d::Vec2& startValue, const cocos2d::Vec2& endValue, float duration); + static GTweener* to(const cocos2d::Vec3& startValue, const cocos2d::Vec3& endValue, float duration); + static GTweener* to(const cocos2d::Vec4& startValue, const cocos2d::Vec4& endValue, float duration); + static GTweener* to(const cocos2d::Color4B& startValue, const cocos2d::Color4B& endValue, float duration); + static GTweener* toDouble(double startValue, double endValue, float duration); + static GTweener* delayedCall(float delay); + static GTweener* shake(const cocos2d::Vec2& startValue, float amplitude, float duration); + static bool isTweening(cocos2d::Ref* target); + static bool isTweening(cocos2d::Ref* target, TweenPropType propType); + static void kill(cocos2d::Ref* target); + static void kill(cocos2d::Ref* target, bool complete); + static void kill(cocos2d::Ref* target, TweenPropType propType, bool complete); + static GTweener* getTween(cocos2d::Ref* target); + static GTweener* getTween(cocos2d::Ref* target, TweenPropType propType); + static void clean(); +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/tween/GTweener.cpp b/extensions/fairygui/tween/GTweener.cpp new file mode 100644 index 0000000000..479707f28f --- /dev/null +++ b/extensions/fairygui/tween/GTweener.cpp @@ -0,0 +1,465 @@ +#include "GTweener.h" +#include "EaseManager.h" +#include "GObject.h" +#include "GPath.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GTweener::GTweener() : _target(nullptr), + _refTarget(nullptr), + _userData(nullptr), + _onStart(nullptr), + _onUpdate(nullptr), + _onComplete(nullptr), + _onComplete0(nullptr), + _path(nullptr) +{ +} + +GTweener::~GTweener() +{ +} + +GTweener* GTweener::setDelay(float value) +{ + _delay = value; + return this; +} + +GTweener* GTweener::setDuration(float value) +{ + _duration = value; + return this; +} + +GTweener* GTweener::setBreakpoint(float value) +{ + _breakpoint = value; + return this; +} + +GTweener* GTweener::setEase(EaseType value) +{ + _easeType = value; + return this; +} + +GTweener* GTweener::setEasePeriod(float value) +{ + _easePeriod = value; + return this; +} + +GTweener* GTweener::setEaseOvershootOrAmplitude(float value) +{ + _easeOvershootOrAmplitude = value; + return this; +} + +GTweener* GTweener::setRepeat(int repeat, bool yoyo) +{ + _repeat = repeat; + _yoyo = yoyo; + return this; +} + +GTweener* GTweener::setTimeScale(float value) +{ + _timeScale = value; + return this; +} + +GTweener* GTweener::setSnapping(bool value) +{ + _snapping = value; + return this; +} + +GTweener* GTweener::setTargetAny(void* value) +{ + CC_SAFE_RELEASE(_refTarget); + _refTarget = nullptr; + _target = value; + return this; +} + +GTweener* GTweener::setTarget(cocos2d::Ref* value) +{ + return setTarget(value, TweenPropType::None); +} + +GTweener* GTweener::setTarget(cocos2d::Ref* target, TweenPropType propType) +{ + CC_SAFE_RELEASE(_refTarget); + _target = _refTarget = target; + _propType = propType; + CC_SAFE_RETAIN(_refTarget); + return this; +} + +GTweener* GTweener::setUserData(const Value& value) +{ + _userData = value; + return this; +} + +GTweener* GTweener::setPath(GPath* path) +{ + _path = path; + return this; +} + +GTweener* GTweener::onUpdate(GTweenCallback callback) +{ + _onUpdate = callback; + return this; +} + +GTweener* GTweener::onStart(GTweenCallback callback) +{ + _onStart = callback; + return this; +} + +GTweener* GTweener::onComplete(GTweenCallback0 callback) +{ + _onComplete0 = callback; + return this; +} + +GTweener* GTweener::onComplete1(GTweenCallback callback) +{ + _onComplete = callback; + return this; +} + +GTweener* GTweener::setPaused(bool paused) +{ + _paused = paused; + return this; +} + +void GTweener::seek(float time) +{ + if (_killed) + return; + + _elapsedTime = time; + if (_elapsedTime < _delay) + { + if (_started) + _elapsedTime = _delay; + else + return; + } + + update(); +} + +void GTweener::kill(bool complete) +{ + if (_killed) + return; + + if (complete) + { + if (_ended == 0) + { + if (_breakpoint >= 0) + _elapsedTime = _delay + _breakpoint; + else if (_repeat >= 0) + _elapsedTime = _delay + _duration * (_repeat + 1); + else + _elapsedTime = _delay + _duration * 2; + update(); + } + + callCompleteCallback(); + } + + _killed = true; +} + +GTweener* GTweener::_to(float start, float end, float duration) +{ + _valueSize = 1; + startValue.x = start; + endValue.x = end; + value.x = start; + _duration = duration; + return this; +} + +GTweener* GTweener::_to(const cocos2d::Vec2& start, const cocos2d::Vec2& end, float duration) +{ + _valueSize = 2; + startValue.setVec2(start); + endValue.setVec2(end); + value.setVec2(start); + _duration = duration; + return this; +} + +GTweener* GTweener::_to(const cocos2d::Vec3& start, const cocos2d::Vec3& end, float duration) +{ + _valueSize = 3; + startValue.setVec3(start); + endValue.setVec3(end); + value.setVec3(start); + _duration = duration; + return this; +} + +GTweener* GTweener::_to(const cocos2d::Vec4& start, const cocos2d::Vec4& end, float duration) +{ + _valueSize = 4; + startValue.setVec4(start); + endValue.setVec4(end); + value.setVec4(start); + _duration = duration; + return this; +} + +GTweener* GTweener::_to(const cocos2d::Color4B& start, const cocos2d::Color4B& end, float duration) +{ + _valueSize = 4; + startValue.setColor(start); + endValue.setColor(end); + value.setColor(start); + _duration = duration; + return this; +} + +GTweener* GTweener::_to(double start, double end, float duration) +{ + _valueSize = 5; + startValue.d = start; + endValue.d = end; + value.d = start; + _duration = duration; + return this; +} + +GTweener* GTweener::_shake(const cocos2d::Vec2& start, float amplitude, float duration) +{ + _valueSize = 6; + startValue.setVec2(start); + startValue.w = amplitude; + _duration = duration; + _easeType = EaseType::Linear; + return this; +} + +void GTweener::_init() +{ + _delay = 0; + _duration = 0; + _breakpoint = -1; + _easeType = EaseType::QuadOut; + _timeScale = 1; + _easePeriod = 0; + _easeOvershootOrAmplitude = 1.70158f; + _snapping = false; + _repeat = 0; + _yoyo = false; + _valueSize = 0; + _started = false; + _paused = false; + _killed = false; + _elapsedTime = 0; + _normalizedTime = 0; + _ended = 0; + startValue.setZero(); + endValue.setZero(); + value.setZero(); + deltaValue.setZero(); +} + +void GTweener::_reset() +{ + CC_SAFE_RELEASE(_refTarget); + _target = nullptr; + _refTarget = nullptr; + _userData = nullptr; + _path = nullptr; + _onStart = _onUpdate = _onComplete = nullptr; + _onComplete0 = nullptr; +} + +void GTweener::_update(float dt) +{ + if (_ended != 0) //Maybe completed by seek + { + callCompleteCallback(); + _killed = true; + return; + } + + if (_timeScale != 1) + dt *= _timeScale; + if (dt == 0) + return; + + _elapsedTime += dt; + update(); + + if (_ended != 0) + { + if (!_killed) + { + callCompleteCallback(); + _killed = true; + } + } +} + +void GTweener::update() +{ + _ended = 0; + + if (_valueSize == 0) //DelayedCall + { + if (_elapsedTime >= _delay + _duration) + _ended = 1; + + return; + } + + if (!_started) + { + if (_elapsedTime < _delay) + return; + + _started = true; + callStartCallback(); + if (_killed) + return; + } + + bool reversed = false; + float tt = _elapsedTime - _delay; + if (_breakpoint >= 0 && tt >= _breakpoint) + { + tt = _breakpoint; + _ended = 2; + } + + if (_repeat != 0) + { + int round = (int)floor(tt / _duration); + tt -= _duration * round; + if (_yoyo) + reversed = round % 2 == 1; + + if (_repeat > 0 && _repeat - round < 0) + { + if (_yoyo) + reversed = _repeat % 2 == 1; + tt = _duration; + _ended = 1; + } + } + else if (tt >= _duration) + { + tt = _duration; + _ended = 1; + } + + _normalizedTime = EaseManager::evaluate(_easeType, reversed ? (_duration - tt) : tt, _duration, + _easeOvershootOrAmplitude, _easePeriod); + + value.setZero(); + deltaValue.setZero(); + + if (_valueSize == 5) + { + double d = startValue.d + (endValue.d - startValue.d) * _normalizedTime; + if (_snapping) + d = round(d); + deltaValue.d = d - value.d; + value.d = d; + value.x = (float)d; + } + else if (_valueSize == 6) + { + if (_ended == 0) + { + float r = startValue.w * (1 - _normalizedTime); + float rx = (rand_0_1() * 2 - 1) * r; + float ry = (rand_0_1() * 2 - 1) * r; + rx = rx > 0 ? ceil(rx) : floor(rx); + ry = ry > 0 ? ceil(ry) : floor(ry); + + deltaValue.x = rx; + deltaValue.y = ry; + value.x = startValue.x + rx; + value.y = startValue.y + ry; + } + else + value.setVec3(startValue.getVec3()); + } + else if (_path != nullptr) + { + Vec3 vec3 = _path->getPointAt(_normalizedTime); + if (_snapping) + { + vec3.x = round(vec3.x); + vec3.y = round(vec3.y); + vec3.z = round(vec3.z); + } + deltaValue.setVec3(vec3 - value.getVec3()); + value.setVec3(vec3); + } + else + { + for (int i = 0; i < _valueSize; i++) + { + float n1 = startValue[i]; + float n2 = endValue[i]; + float f = n1 + (n2 - n1) * _normalizedTime; + if (_snapping) + f = round(f); + deltaValue[i] = f - value[i]; + value[i] = f; + } + value.d = value.x; + } + + if (_refTarget != nullptr && _propType != TweenPropType::None) + { + GObject* gobj = dynamic_cast(_refTarget); + if (gobj != nullptr) + TweenPropTypeUtils::setProps(gobj, _propType, value); + else + { + Node* node = dynamic_cast(_refTarget); + if (node != nullptr) + TweenPropTypeUtils::setProps(node, _propType, value); + } + } + + callUpdateCallback(); +} + +void GTweener::callStartCallback() +{ + if (_onStart != nullptr) + _onStart(this); +} + +void GTweener::callUpdateCallback() +{ + if (_onUpdate != nullptr) + _onUpdate(this); +} + +void GTweener::callCompleteCallback() +{ + if (_onComplete != nullptr) + _onComplete(this); + if (_onComplete0 != nullptr) + _onComplete0(); +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/tween/GTweener.h b/extensions/fairygui/tween/GTweener.h new file mode 100644 index 0000000000..03b0439261 --- /dev/null +++ b/extensions/fairygui/tween/GTweener.h @@ -0,0 +1,111 @@ +#ifndef __GTWEENER_H__ +#define __GTWEENER_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" +#include "EaseType.h" +#include "TweenValue.h" +#include "TweenPropType.h" + +NS_FGUI_BEGIN + +class GPath; + +class GTweener : public cocos2d::Ref +{ +public: + typedef std::function GTweenCallback; + typedef std::function GTweenCallback0; + + GTweener(); + ~GTweener(); + GTweener* setDelay(float value); + float getDelay() const { return _delay; } + GTweener* setDuration(float value); + float getDuration() const { return _duration; } + GTweener* setBreakpoint(float value); + GTweener* setEase(EaseType value); + GTweener* setEasePeriod(float value); + GTweener* setEaseOvershootOrAmplitude(float value); + GTweener* setRepeat(int repeat, bool yoyo = false); + int getRepeat() const { return _repeat; } + GTweener* setTimeScale(float value); + GTweener* setSnapping(bool value); + GTweener* setTargetAny(void* value); + GTweener* setTarget(cocos2d::Ref* target); + GTweener* setTarget(cocos2d::Ref* target, TweenPropType propType); + void* getTarget() const { return _target; } + GTweener* setUserData(const cocos2d::Value& value); + GTweener* setPath(GPath* path); + const cocos2d::Value& getUserData() const { return _userData; } + GTweener* onUpdate(GTweenCallback callback); + GTweener* onStart(GTweenCallback callback); + GTweener* onComplete(GTweenCallback0 callback); + GTweener* onComplete1(GTweenCallback callback); + + float getNormalizedTime() const { return _normalizedTime; } + bool isCompleted() const { return _ended != 0; } + bool allCompleted() const { return _ended == 1; } + GTweener* setPaused(bool paused); + void seek(float time); + void kill(bool complete = false); + + TweenValue startValue; + TweenValue endValue; + TweenValue value; + TweenValue deltaValue; + +private: + GTweener* _to(float start, float end, float duration); + GTweener* _to(const cocos2d::Vec2& start, const cocos2d::Vec2& end, float duration); + GTweener* _to(const cocos2d::Vec3& start, const cocos2d::Vec3& end, float duration); + GTweener* _to(const cocos2d::Vec4& start, const cocos2d::Vec4& end, float duration); + GTweener* _to(const cocos2d::Color4B& start, const cocos2d::Color4B& end, float duration); + GTweener* _to(double start, double end, float duration); + GTweener* _shake(const cocos2d::Vec2& start, float amplitude, float duration); + void _init(); + void _reset(); + void _update(float dt); + void update(); + void callStartCallback(); + void callUpdateCallback(); + void callCompleteCallback(); + +private: + void* _target; + cocos2d::Ref* _refTarget; + TweenPropType _propType; + bool _killed; + bool _paused; + + float _delay; + float _duration; + float _breakpoint; + EaseType _easeType; + float _easeOvershootOrAmplitude; + float _easePeriod; + int _repeat; + bool _yoyo; + float _timeScale; + bool _snapping; + cocos2d::Value _userData; + int _valueSize; + GPath* _path; + + GTweenCallback _onUpdate; + GTweenCallback _onStart; + GTweenCallback _onComplete; + GTweenCallback0 _onComplete0; + + bool _started; + int _ended; + float _elapsedTime; + float _normalizedTime; + + friend class GTween; + friend class TweenManager; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/tween/TweenManager.cpp b/extensions/fairygui/tween/TweenManager.cpp new file mode 100644 index 0000000000..d5b1fae8b1 --- /dev/null +++ b/extensions/fairygui/tween/TweenManager.cpp @@ -0,0 +1,198 @@ +#include "TweenManager.h" +#include "GTweener.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +GTweener** TweenManager::_activeTweens = nullptr; +std::vector TweenManager::_tweenerPool; +int TweenManager::_totalActiveTweens = 0; +int TweenManager::_arrayLength = 0; +bool TweenManager::_inited = false; + +class TweenEngine +{ +public: + void update(float dt) + { + TweenManager::update(dt); + } +}; +static TweenEngine tweenEngine; + +GTweener* TweenManager::createTween() +{ + if (!_inited) + init(); + + GTweener* tweener; + int cnt = (int)_tweenerPool.size(); + if (cnt > 0) + { + tweener = _tweenerPool[cnt - 1]; + _tweenerPool.pop_back(); + } + else + tweener = new GTweener(); + tweener->_init(); + _activeTweens[_totalActiveTweens++] = tweener; + + if (_totalActiveTweens == _arrayLength) + { + int newLen = _arrayLength + ceil(_arrayLength * 0.5f); + GTweener** newArray = new GTweener*[newLen]; + memcpy(newArray, _activeTweens, _arrayLength * sizeof(GTweener*)); + delete _activeTweens; + _activeTweens = newArray; + _arrayLength = newLen; + } + + return tweener; +} + +bool TweenManager::isTweening(cocos2d::Ref* target, TweenPropType propType) +{ + if (target == nullptr) + return false; + + bool anyType = propType == TweenPropType::None; + for (int i = 0; i < _totalActiveTweens; i++) + { + GTweener* tweener = _activeTweens[i]; + if (tweener != nullptr && tweener->_target == target && !tweener->_killed && (anyType || tweener->_propType == propType)) + return true; + } + + return false; +} + +bool TweenManager::killTweens(cocos2d::Ref* target, TweenPropType propType, bool completed) +{ + if (target == nullptr) + return false; + + bool flag = false; + int cnt = _totalActiveTweens; + bool anyType = propType == TweenPropType::None; + for (int i = 0; i < cnt; i++) + { + GTweener* tweener = _activeTweens[i]; + if (tweener != nullptr && tweener->_target == target && !tweener->_killed && (anyType || tweener->_propType == propType)) + { + tweener->kill(completed); + flag = true; + } + } + + return flag; +} + +GTweener* TweenManager::getTween(cocos2d::Ref* target, TweenPropType propType) +{ + if (target == nullptr) + return nullptr; + + int cnt = _totalActiveTweens; + bool anyType = propType == TweenPropType::None; + for (int i = 0; i < cnt; i++) + { + GTweener* tweener = _activeTweens[i]; + if (tweener != nullptr && tweener->_target == target && !tweener->_killed && (anyType || tweener->_propType == propType)) + { + return tweener; + } + } + + return nullptr; +} + +void TweenManager::update(float dt) +{ + int cnt = _totalActiveTweens; + int freePosStart = -1; + for (int i = 0; i < cnt; i++) + { + GTweener* tweener = _activeTweens[i]; + if (tweener == nullptr) + { + if (freePosStart == -1) + freePosStart = i; + } + else if (tweener->_killed) + { + tweener->_reset(); + _tweenerPool.push_back(tweener); + _activeTweens[i] = nullptr; + + if (freePosStart == -1) + freePosStart = i; + } + else + { + if (tweener->_refTarget != nullptr && tweener->_refTarget->getReferenceCount() == 1) + tweener->_killed = true; + else if (!tweener->_paused) + tweener->_update(dt); + + if (freePosStart != -1) + { + _activeTweens[freePosStart] = tweener; + _activeTweens[i] = nullptr; + freePosStart++; + } + } + } + + if (freePosStart >= 0) + { + if (_totalActiveTweens != cnt) //new tweens added + { + int j = cnt; + cnt = _totalActiveTweens - cnt; + for (int i = 0; i < cnt; i++) + _activeTweens[freePosStart++] = _activeTweens[j++]; + } + _totalActiveTweens = freePosStart; + } +} + +void TweenManager::clean() +{ + for (auto it = _tweenerPool.begin(); it != _tweenerPool.end(); it++) + (*it)->release(); + _tweenerPool.clear(); +} + +void TweenManager::init() +{ + _inited = true; + + if (_activeTweens == nullptr) + { + _arrayLength = 30; + _activeTweens = new GTweener*[_arrayLength]; + } + + cocos2d::Director::getInstance()->getScheduler()->scheduleUpdate(&tweenEngine, INT_MIN + 10, false); + cocos2d::Director::getInstance()->getEventDispatcher()->addCustomEventListener(cocos2d::Director::EVENT_RESET, &reset); +} + +void TweenManager::reset(cocos2d::EventCustom*) +{ + int cnt = _totalActiveTweens; + for (int i = 0; i < cnt; i++) + { + GTweener* tweener = _activeTweens[i]; + if (tweener != nullptr) + { + tweener->_reset(); + _tweenerPool.push_back(tweener); + _activeTweens[i] = nullptr; + } + } + + _totalActiveTweens = 0; + _inited = false; +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/tween/TweenManager.h b/extensions/fairygui/tween/TweenManager.h new file mode 100644 index 0000000000..d59395d705 --- /dev/null +++ b/extensions/fairygui/tween/TweenManager.h @@ -0,0 +1,35 @@ +#ifndef __TWEENMANAGER_H__ +#define __TWEENMANAGER_H__ + +#include "FairyGUIMacros.h" +#include "TweenPropType.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class GTweener; + +class TweenManager +{ +public: + static GTweener* createTween(); + static bool isTweening(cocos2d::Ref* target, TweenPropType propType); + static bool killTweens(cocos2d::Ref* target, TweenPropType propType, bool completed); + static GTweener* getTween(cocos2d::Ref* target, TweenPropType propType); + static void update(float dt); + static void clean(); + static void init(); + +private: + static void reset(cocos2d::EventCustom*); + + static GTweener** _activeTweens; + static std::vector _tweenerPool; + static int _totalActiveTweens; + static int _arrayLength; + static bool _inited; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/tween/TweenPropType.cpp b/extensions/fairygui/tween/TweenPropType.cpp new file mode 100644 index 0000000000..35e850104e --- /dev/null +++ b/extensions/fairygui/tween/TweenPropType.cpp @@ -0,0 +1,130 @@ +#include "TweenPropType.h" +#include "TweenValue.h" +#include "GObject.h" +#include "GProgressBar.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +void TweenPropTypeUtils::setProps(GObject * target, TweenPropType propType, const TweenValue & value) +{ + GObject* g = dynamic_cast(target); + if (g == nullptr) + return; + + switch (propType) + { + case TweenPropType::X: + g->setX(value.x); + break; + + case TweenPropType::Y: + g->setY(value.x); + break; + + case TweenPropType::Position: + g->setPosition(value.x, value.y); + break; + + case TweenPropType::Width: + g->setWidth(value.x); + break; + + case TweenPropType::Height: + g->setHeight(value.x); + break; + + case TweenPropType::Size: + g->setSize(value.x, value.y); + break; + + case TweenPropType::ScaleX: + g->setScaleX(value.x); + break; + + case TweenPropType::ScaleY: + g->setScaleY(value.x); + break; + + case TweenPropType::Scale: + g->setScale(value.x, value.y); + break; + + case TweenPropType::Rotation: + g->setRotation(value.x); + break; + + case TweenPropType::Alpha: + g->setAlpha(value.x); + break; + + case TweenPropType::Progress: + g->as()->update(value.d); + break; + default: + break; + } +} + +void TweenPropTypeUtils::setProps(cocos2d::Node * target, TweenPropType propType, const TweenValue & value) +{ + GObject* g = dynamic_cast(target); + if (g == nullptr) + return; + + switch (propType) + { + case TweenPropType::X: + g->setX(value.x); + break; + + case TweenPropType::Y: + g->setY(value.x); + break; + + case TweenPropType::Position: + g->setPosition(value.x, value.y); + break; + + case TweenPropType::Width: + g->setWidth(value.x); + break; + + case TweenPropType::Height: + g->setHeight(value.x); + break; + + case TweenPropType::Size: + g->setSize(value.x, value.y); + break; + + case TweenPropType::ScaleX: + g->setScaleX(value.x); + break; + + case TweenPropType::ScaleY: + g->setScaleY(value.x); + break; + + case TweenPropType::Scale: + g->setScale(value.x, value.y); + break; + + case TweenPropType::Rotation: + g->setRotation(value.x); + break; + + case TweenPropType::Alpha: + g->setAlpha(value.x); + break; + + case TweenPropType::Progress: + g->as()->update(value.d); + break; + default: + break; + } +} + +NS_FGUI_END + diff --git a/extensions/fairygui/tween/TweenPropType.h b/extensions/fairygui/tween/TweenPropType.h new file mode 100644 index 0000000000..fe21dabbc9 --- /dev/null +++ b/extensions/fairygui/tween/TweenPropType.h @@ -0,0 +1,39 @@ +#ifndef __TWEENPROPTYPE_H__ +#define __TWEENPROPTYPE_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class TweenValue; +class GObject; + +enum class TweenPropType +{ + None, + X, + Y, + Position, + Width, + Height, + Size, + ScaleX, + ScaleY, + Scale, + Rotation, + Alpha, + Progress +}; + +class TweenPropTypeUtils +{ +public: + static void setProps(GObject* target, TweenPropType propType, const TweenValue& value); + static void setProps(cocos2d::Node* target, TweenPropType propType, const TweenValue& value); +}; + + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/tween/TweenValue.cpp b/extensions/fairygui/tween/TweenValue.cpp new file mode 100644 index 0000000000..f59b557416 --- /dev/null +++ b/extensions/fairygui/tween/TweenValue.cpp @@ -0,0 +1,98 @@ +#include "TweenValue.h" + +NS_FGUI_BEGIN +USING_NS_CC; + +TweenValue::TweenValue():x(0),y(0),z(0),w(0),d(0) +{ +} + +cocos2d::Vec2 TweenValue::getVec2() const +{ + return cocos2d::Vec2(x, y); +} + +void TweenValue::setVec2(const cocos2d::Vec2 & value) +{ + x = value.x; + y = value.y; +} + +cocos2d::Vec3 TweenValue::getVec3() const +{ + return cocos2d::Vec3(x, y, z); +} + +void TweenValue::setVec3(const cocos2d::Vec3 & value) +{ + x = value.x; + y = value.y; + z = value.z; +} + +cocos2d::Vec4 TweenValue::getVec4() const +{ + return cocos2d::Vec4(x, y, z, w); +} + +void TweenValue::setVec4(const cocos2d::Vec4 & value) +{ + x = value.x; + y = value.y; + z = value.z; + w = value.w; +} + +cocos2d::Color4B TweenValue::getColor() const +{ + return cocos2d::Color4B(x, y, z, w); +} + +void TweenValue::setColor(const cocos2d::Color4B & value) +{ + x = value.r; + y = value.g; + z = value.b; + w = value.a; +} + +float TweenValue::operator[](int index) const +{ + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw "Index out of bounds: " + std::to_string(index); + } +} + +float & TweenValue::operator[](int index) +{ + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw "Index out of bounds: " + std::to_string(index); + } +} + +void TweenValue::setZero() +{ + x = y = z = w = d = 0; +} + +NS_FGUI_END \ No newline at end of file diff --git a/extensions/fairygui/tween/TweenValue.h b/extensions/fairygui/tween/TweenValue.h new file mode 100644 index 0000000000..a3a42b4b2f --- /dev/null +++ b/extensions/fairygui/tween/TweenValue.h @@ -0,0 +1,35 @@ +#ifndef __TWEENVALUE_H__ +#define __TWEENVALUE_H__ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class TweenValue +{ +public: + float x; + float y; + float z; + float w; + double d; + + TweenValue(); + + cocos2d::Vec2 getVec2() const; + void setVec2(const cocos2d::Vec2& value); + cocos2d::Vec3 getVec3() const; + void setVec3(const cocos2d::Vec3& value); + cocos2d::Vec4 getVec4() const; + void setVec4(const cocos2d::Vec4& value); + cocos2d::Color4B getColor() const; + void setColor(const cocos2d::Color4B& value); + float operator[] (int index) const; + float& operator[] (int index); + void setZero(); +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/utils/ByteBuffer.cpp b/extensions/fairygui/utils/ByteBuffer.cpp new file mode 100644 index 0000000000..f52fa0c77b --- /dev/null +++ b/extensions/fairygui/utils/ByteBuffer.cpp @@ -0,0 +1,228 @@ +#include "ByteBuffer.h" + +NS_FGUI_BEGIN +using namespace std; + +ByteBuffer::ByteBuffer(char* buffer, int offset, int len, bool transferOwnerShip = false) + : _buffer(buffer), + _position(0), + _offset(offset), + _length(len), + _littleEndian(false), + _ownsBuffer(transferOwnerShip), + _stringTable(nullptr), + version(0) +{ +} + +ByteBuffer::~ByteBuffer() +{ + if (_ownsBuffer && _buffer != nullptr) + delete _buffer; +} + +int ByteBuffer::getBytesAvailable() const +{ + return _length - _position; +} + +char ByteBuffer::readByte() +{ + signed char val = _buffer[_offset + _position]; + if (val > 127) + val = val - 255; + _position += 1; + return val; +} + +unsigned char ByteBuffer::readUbyte() +{ + unsigned char val = _buffer[_offset + _position]; + _position += 1; + return val; +} + +bool ByteBuffer::readBool() +{ + return readByte() == 1; +} + +short ByteBuffer::readShort() +{ + int startIndex = _offset + _position; + _position += 2; + unsigned char* pbyte = (unsigned char*)(_buffer + startIndex); + if (_littleEndian) + return (short)((*pbyte) | (*(pbyte + 1) << 8)); + else + return (short)((*pbyte << 8) | (*(pbyte + 1))); +} + +unsigned short ByteBuffer::readUshort() +{ + return (unsigned short)readShort(); +} + +int ByteBuffer::readInt() +{ + int startIndex = _offset + _position; + _position += 4; + unsigned char* pbyte = (unsigned char*)(_buffer + startIndex); + if (_littleEndian) + return (*pbyte) | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24); + else + return (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | (*(pbyte + 3)); +} + +unsigned int ByteBuffer::readUint() +{ + return (unsigned int)readInt(); +} + +float ByteBuffer::readFloat() +{ + int val = readInt(); + return *(float*)&val; +} + +std::string ByteBuffer::readString() +{ + int len = readUshort(); + return readString(len); +} + +std::string ByteBuffer::readString(int len) +{ + char* value = new char[len + 1]; + + value[len] = '\0'; + memcpy(value, _buffer + _position, len); + _position += len; + + string str(value); + delete[] value; + value = nullptr; + + return str; +} + +const std::string& ByteBuffer::readS() +{ + int index = readUshort(); + if (index == 65534 || index == 65533) + return cocos2d::STD_STRING_EMPTY; + else + return (*_stringTable)[index]; +} + +bool ByteBuffer::readS(std::string& result) +{ + int index = readUshort(); + if (index == 65534) //null + return false; + else if (index == 65533) + { + result.clear(); + return true; + } + else + { + result = (*_stringTable)[index]; + return true; + } +} + +const string* ByteBuffer::readSP() +{ + int index = readUshort(); + if (index == 65534) //null + return nullptr; + else if (index == 65533) + return &cocos2d::STD_STRING_EMPTY; + else + return &(*_stringTable)[index]; +} + +void ByteBuffer::readSArray(std::vector& arr, int count) +{ + for (int i = 0; i < count; i++) + arr.push_back(readS()); +} + +void ByteBuffer::writeS(const std::string& value) +{ + int index = readUshort(); + if (index != 65534 && index != 65533) + (*_stringTable)[index] = value; +} + +cocos2d::Color4B ByteBuffer::readColor() +{ + int startIndex = _offset + _position; +#if COCOS2D_VERSION >= 0x00040000 + uint8_t r = _buffer[startIndex]; + uint8_t g = _buffer[startIndex + 1]; + uint8_t b = _buffer[startIndex + 2]; + uint8_t a = _buffer[startIndex + 3]; +#else + GLubyte r = _buffer[startIndex]; + GLubyte g = _buffer[startIndex + 1]; + GLubyte b = _buffer[startIndex + 2]; + GLubyte a = _buffer[startIndex + 3]; +#endif + _position += 4; + + return cocos2d::Color4B(r, g, b, a); +} + +ByteBuffer* ByteBuffer::readBuffer() +{ + int count = readInt(); + char* p = (char*)malloc(count); + memcpy(p, _buffer + _position, count); + ByteBuffer* ba = new ByteBuffer(p, 0, count, true); + ba->_stringTable = _stringTable; + ba->version = version; + _position += count; + return ba; +} + +bool ByteBuffer::seek(int indexTablePos, int blockIndex) +{ + int tmp = _position; + _position = indexTablePos; + int segCount = _buffer[_offset + _position++]; + if (blockIndex < segCount) + { + bool useShort = _buffer[_offset + _position++] == 1; + int newPos; + if (useShort) + { + _position += 2 * blockIndex; + newPos = readShort(); + } + else + { + _position += 4 * blockIndex; + newPos = readInt(); + } + + if (newPos > 0) + { + _position = indexTablePos + newPos; + return true; + } + else + { + _position = tmp; + return false; + } + } + else + { + _position = tmp; + return false; + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/utils/ByteBuffer.h b/extensions/fairygui/utils/ByteBuffer.h new file mode 100644 index 0000000000..6eaa9931d2 --- /dev/null +++ b/extensions/fairygui/utils/ByteBuffer.h @@ -0,0 +1,63 @@ +#ifndef __BYTEBUFFER_H_ +#define __BYTEBUFFER_H_ + +#include "FairyGUIMacros.h" +#include "cocos2d.h" + +NS_FGUI_BEGIN + +class ByteBuffer +{ +public: + ByteBuffer(char* buffer, int offset, int len, bool transferOwnerShip); + ~ByteBuffer(); + + const char* getBuffer() const { return _buffer; } + + bool isLittleEndian() const { return _littleEndian; } + void setLittleEndian(bool value) { _littleEndian = value; } + + int getBytesAvailable() const; + int getLength() const { return _length; } + + int getPos() const { return _position; } + void setPos(int value) { _position = value; } + void skip(int count) { _position += count; } + + char readByte(); + unsigned char readUbyte(); + bool readBool(); + short readShort(); + unsigned short readUshort(); + int readInt(); + unsigned int readUint(); + float readFloat(); + std::string readString(); + std::string readString(int len); + const std::string& readS(); + void readSArray(std::vector& arr, int count); + bool readS(std::string& result); + const std::string* readSP(); + void writeS(const std::string& value); + cocos2d::Color4B readColor(); + ByteBuffer* readBuffer(); + bool seek(int indexTablePos, int blockIndex); + + std::vector* getStringTable() const { return _stringTable; } + void setStringTable(std::vector* value) { _stringTable = value; } + + int version; + +private: + char* _buffer; + int _offset; + int _length; + bool _littleEndian; + bool _ownsBuffer; + int _position; + std::vector* _stringTable; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/utils/ToolSet.cpp b/extensions/fairygui/utils/ToolSet.cpp new file mode 100644 index 0000000000..2e9533de14 --- /dev/null +++ b/extensions/fairygui/utils/ToolSet.cpp @@ -0,0 +1,154 @@ +#include "utils/ToolSet.h" + +NS_FGUI_BEGIN +USING_NS_CC; +using namespace std; + +Color4B ToolSet::hexToColor(const char* str) +{ + ssize_t len = strlen(str); + if (len < 7 || str[0] != '#') + return Color4B::BLACK; + + char temp[3]; + memset(temp, 0, 3); + + if (len == 9) + { + return Color4B(strtol(strncpy(temp, str + 3, 2), NULL, 16), + strtol(strncpy(temp, str + 5, 2), NULL, 16), + strtol(strncpy(temp, str + 7, 2), NULL, 16), + strtol(strncpy(temp, str + 1, 2), NULL, 16)); + } + else + { + return Color4B(strtol(strncpy(temp, str + 1, 2), NULL, 16), + strtol(strncpy(temp, str + 3, 2), NULL, 16), + strtol(strncpy(temp, str + 5, 2), NULL, 16), + 255); + } +} + +cocos2d::Color3B ToolSet::intToColor(unsigned int rgb) +{ + return Color3B((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); +} + +unsigned int ToolSet::colorToInt(const cocos2d::Color3B& color) +{ + return (color.r << 16) + (color.g << 8) + color.b; +} + +Rect ToolSet::intersection(const Rect& rect1, const Rect& rect2) +{ + if (rect1.size.width == 0 || rect1.size.height == 0 || rect2.size.width == 0 || rect2.size.height == 0) + return Rect(0, 0, 0, 0); + + float left = rect1.getMinX() > rect2.getMinX() ? rect1.getMinX() : rect2.getMinX(); + float right = rect1.getMaxX() < rect2.getMaxX() ? rect1.getMaxX() : rect2.getMaxX(); + float top = rect1.getMinY() > rect2.getMinY() ? rect1.getMinY() : rect2.getMinY(); + float bottom = rect1.getMaxY() < rect2.getMaxY() ? rect1.getMaxY() : rect2.getMaxY(); + + if (left > right || top > bottom) + return Rect(0, 0, 0, 0); + else + return Rect(left, top, right - left, bottom - top); +} + +int ToolSet::findInStringArray(const std::vector& arr, const std::string& str) +{ + auto iter = std::find(arr.begin(), arr.end(), str); + if (iter != arr.end()) + return (int)(iter - arr.begin()); + + return -1; +} + +bool ToolSet::isFileExist(const std::string & fileName) +{ + bool tmp = FileUtils::getInstance()->isPopupNotify(); + FileUtils::getInstance()->setPopupNotify(false); + bool ret = FileUtils::getInstance()->isFileExist(fileName); + FileUtils::getInstance()->setPopupNotify(tmp); + return ret; +} + +FastSplitter::FastSplitter() : data(nullptr), dataLength(-1), delimiter('\0') +{ +} + +void FastSplitter::start(const char* data, ssize_t dataLength, char delimiter) +{ + this->data = data; + this->dataLength = dataLength; + this->delimiter = delimiter; + this->textLength = -1; +} + +bool FastSplitter::next() +{ + if (dataLength < 0) + return false; + + if (dataLength == 0) + { + dataLength = -1; + textLength = 0; + return true; + } + + data += textLength + 1; + char* found = (char*)memchr(data, (int)delimiter, dataLength); + if (found) + textLength = found - data; + else + textLength = dataLength; + dataLength -= (textLength + 1); + + return true; +} + +const char* FastSplitter::getText() +{ + if (textLength > 0) + return data; + else + return nullptr; +} + +ssize_t FastSplitter::getTextLength() +{ + return textLength; +} + +void FastSplitter::getKeyValuePair(char* keyBuf, ssize_t keyBufSize, char* valueBuf, ssize_t valueBufSize) +{ + if (textLength == 0) + { + keyBuf[0] = '\0'; + valueBuf[0] = '\0'; + } + else + { + char* found = (char*)memchr(data, (int)'=', textLength); + if (found) + { + ssize_t len = MIN(keyBufSize - 1, found - data); + memcpy(keyBuf, data, len); + keyBuf[len] = '\0'; + + len = MIN(valueBufSize - 1, textLength - (found - data) - 1); + memcpy(valueBuf, found + 1, len); + valueBuf[len] = '\0'; + } + else + { + ssize_t len = MIN(valueBufSize - 1, textLength); + memcpy(keyBuf, data, len); + keyBuf[len] = '\0'; + valueBuf[0] = '\0'; + } + } +} + +NS_FGUI_END diff --git a/extensions/fairygui/utils/ToolSet.h b/extensions/fairygui/utils/ToolSet.h new file mode 100644 index 0000000000..02f8b06a29 --- /dev/null +++ b/extensions/fairygui/utils/ToolSet.h @@ -0,0 +1,41 @@ +#ifndef __TOOLSET_H__ +#define __TOOLSET_H__ + +#include "cocos2d.h" +#include "FairyGUI.h" + +NS_FGUI_BEGIN + +class ToolSet +{ +public: + static cocos2d::Color4B hexToColor(const char* str); + static cocos2d::Color3B intToColor(unsigned int rgb); + static unsigned int colorToInt(const cocos2d::Color3B& color); + + static cocos2d::Rect intersection(const cocos2d::Rect& rect1, const cocos2d::Rect& rect2); + static int findInStringArray(const std::vector& arr, const std::string& str); + + static bool isFileExist(const std::string& fileName); +}; + +class FastSplitter +{ +public: + FastSplitter(); + void start(const char* data, ssize_t dataLength, char delimiter); + bool next(); + const char* getText(); + ssize_t getTextLength(); + void getKeyValuePair(char* keyBuf, ssize_t keyBufSize, char* valueBuf, ssize_t valueBufSize); + +private: + const char* data; + ssize_t dataLength; + ssize_t textLength; + char delimiter; +}; + +NS_FGUI_END + +#endif diff --git a/extensions/fairygui/utils/UBBParser.cpp b/extensions/fairygui/utils/UBBParser.cpp new file mode 100644 index 0000000000..7adef3fefd --- /dev/null +++ b/extensions/fairygui/utils/UBBParser.cpp @@ -0,0 +1,205 @@ +#include "UBBParser.h" + +NS_FGUI_BEGIN +USING_NS_CC; +using namespace std; + +UBBParser* UBBParser::_inst = nullptr; + +UBBParser * UBBParser::getInstance() +{ + if (!_inst) + _inst = new UBBParser(); + return _inst; +} + +UBBParser::UBBParser() : + defaultImgWidth(0), + defaultImgHeight(0), + _pString(nullptr), + _readPos(0) +{ + _handlers["url"] = UBB_TAG_HANDLER(UBBParser::onTag_URL, this); + _handlers["img"] = UBB_TAG_HANDLER(UBBParser::onTag_IMG, this); + _handlers["b"] = UBB_TAG_HANDLER(UBBParser::onTag_Simple, this); + _handlers["i"] = UBB_TAG_HANDLER(UBBParser::onTag_Simple, this); + _handlers["u"] = UBB_TAG_HANDLER(UBBParser::onTag_Simple, this); + _handlers["sup"] = UBB_TAG_HANDLER(UBBParser::onTag_Simple, this); + _handlers["sub"] = UBB_TAG_HANDLER(UBBParser::onTag_Simple, this); + _handlers["color"] = UBB_TAG_HANDLER(UBBParser::onTag_COLOR, this); + _handlers["font"] = UBB_TAG_HANDLER(UBBParser::onTag_FONT, this); + _handlers["size"] = UBB_TAG_HANDLER(UBBParser::onTag_SIZE, this); + _handlers["align"] = UBB_TAG_HANDLER(UBBParser::onTag_ALIGN, this); +} + +UBBParser::~UBBParser() +{ + +} + +std::string UBBParser::parse(const char * text, bool remove) +{ + _pString = text; + _readPos = 0; + lastColor.clear(); + lastFontSize.clear(); + + ssize_t pos; + bool end; + string tag, attr; + string repl; + string out; + + while (*_pString != '\0') + { + const char* p = strchr(_pString, '['); + if (!p) + { + out.append(_pString); + break; + } + + pos = p - _pString; + if (pos > 0 && *(p - 1) == '\\') + { + out.append(_pString, pos - 1); + out.append("["); + _pString += pos + 1; + continue; + } + + out.append(_pString, pos); + _pString += pos; + + p = strchr(_pString, ']'); + if (!p) + { + out.append(_pString); + break; + } + + pos = p - _pString; + if (pos == 1) + { + out.append(_pString, 0, 2); + _pString += 2; + continue; + } + + end = _pString[1] == '/'; + if (end) + tag.assign(_pString + 2, pos - 2); + else + tag.assign(_pString + 1, pos - 1); + _readPos = pos + 1; + + attr.clear(); + repl.clear(); + pos = tag.find('='); + if (pos != -1) + { + attr = tag.substr(pos + 1); + tag = tag.substr(0, pos); + } + transform(tag.begin(), tag.end(), tag.begin(), ::tolower); + auto it = _handlers.find(tag); + if (it != _handlers.end()) + { + it->second(tag, end, attr, repl); + if (!remove) + out.append(repl); + } + else + out.append(_pString, _readPos); + _pString += _readPos; + } + return out; +} + +void UBBParser::getTagText(std::string& out, bool remove) +{ + const char* p = strchr(_pString + _readPos, '['); + if (!p) + return; + + ssize_t pos = p - _pString; + out.assign(_pString, _readPos, pos - _readPos); + if (remove) + _readPos = pos; +} + +void UBBParser::onTag_URL(const std::string & tagName, bool end, const std::string & attr, std::string& replacement) +{ + if (!end) + { + if (!attr.empty()) + replacement = ""; + else + { + string href; + getTagText(href, false); + replacement = ""; + } + } + else + replacement = ""; +} + +void UBBParser::onTag_IMG(const std::string & tagName, bool end, const std::string & attr, std::string& replacement) +{ + if (!end) + { + string src; + getTagText(src, true); + if (src.empty()) + return; + + if (defaultImgWidth != 0) + replacement = ""; + else + replacement = ""; + } +} + +void UBBParser::onTag_Simple(const std::string & tagName, bool end, const std::string & attr, std::string& replacement) +{ + replacement = end ? ("") : ("<" + tagName + ">"); +} + +void UBBParser::onTag_COLOR(const std::string & tagName, bool end, const std::string & attr, std::string& replacement) +{ + if (!end) { + replacement = ""; + lastColor = attr; + } + else + replacement = ""; +} + +void UBBParser::onTag_FONT(const std::string & tagName, bool end, const std::string & attr, std::string& replacement) +{ + if (!end) + replacement = ""; + else + replacement = ""; +} + +void UBBParser::onTag_SIZE(const std::string & tagName, bool end, const std::string & attr, std::string& replacement) +{ + if (!end) { + replacement = ""; + lastFontSize = attr; + } + else + replacement = ""; +} + +void UBBParser::onTag_ALIGN(const std::string & tagName, bool end, const std::string & attr, std::string& replacement) +{ + if (!end) + replacement = "

"; + else + replacement = "

"; +} + +NS_FGUI_END diff --git a/extensions/fairygui/utils/UBBParser.h b/extensions/fairygui/utils/UBBParser.h new file mode 100644 index 0000000000..ead5439f79 --- /dev/null +++ b/extensions/fairygui/utils/UBBParser.h @@ -0,0 +1,49 @@ +#ifndef __UBBPARSER_H__ +#define __UBBPARSER_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +#define UBB_TAG_HANDLER(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, ##__VA_ARGS__) + +class UBBParser +{ +public: + UBBParser(); + virtual ~UBBParser(); + + static UBBParser* getInstance(); + + std::string parse(const char *text, bool remove = false); + + int defaultImgWidth; + int defaultImgHeight; + std::string lastColor; + std::string lastFontSize; + +protected: + virtual void onTag_URL(const std::string& tagName, bool end, const std::string& attr, std::string& replacement); + virtual void onTag_IMG(const std::string& tagName, bool end, const std::string& attr, std::string& replacement); + virtual void onTag_Simple(const std::string& tagName, bool end, const std::string& attr, std::string& replacement); + virtual void onTag_COLOR(const std::string& tagName, bool end, const std::string& attr, std::string& replacement); + virtual void onTag_FONT(const std::string& tagName, bool end, const std::string& attr, std::string& replacement); + virtual void onTag_SIZE(const std::string& tagName, bool end, const std::string& attr, std::string& replacement); + virtual void onTag_ALIGN(const std::string& tagName, bool end, const std::string& attr, std::string& replacement); + + void getTagText(std::string& out, bool remove); + + typedef std::function TagHandler; + std::unordered_map _handlers; + + const char* _pString; + ssize_t _readPos; + +private: + static UBBParser* _inst; +}; + +NS_FGUI_END + +#endif \ No newline at end of file diff --git a/extensions/fairygui/utils/WeakPtr.cpp b/extensions/fairygui/utils/WeakPtr.cpp new file mode 100644 index 0000000000..573242866a --- /dev/null +++ b/extensions/fairygui/utils/WeakPtr.cpp @@ -0,0 +1,144 @@ +#include "WeakPtr.h" +#include "GObject.h" + +NS_FGUI_BEGIN +USING_NS_CC; +using namespace std; + +static std::unordered_map _weakPointers; + +WeakPtr::WeakPtr() :_id(0) +{ +} + +WeakPtr::WeakPtr(GObject * obj) +{ + _id = add(obj); +} + +WeakPtr::WeakPtr(const WeakPtr & other) :_id(0) +{ + *this = other; +} + +WeakPtr::WeakPtr(WeakPtr && other) : _id(0) +{ + *this = std::move(other); +} + +WeakPtr::~WeakPtr() +{ + if (_id != 0) + remove(_id); +} + +WeakPtr & WeakPtr::operator=(const WeakPtr & other) +{ + if (_id != 0) + remove(_id); + _id = add(other.ptr()); + return *this; +} + +WeakPtr & WeakPtr::operator=(WeakPtr && other) +{ + if (this != &other) + { + if (_id != 0) + remove(_id); + _id = other._id; + other._id = 0; + } + + return *this; +} + +WeakPtr & WeakPtr::operator=(GObject * obj) +{ + if (_id != 0) + remove(_id); + _id = add(obj); + return *this; +} + +bool WeakPtr::operator!=(const WeakPtr & v) +{ + return !(*this == v); +} + +bool WeakPtr::operator!=(const WeakPtr & v) const +{ + return !(*this == v); +} + +bool WeakPtr::operator==(const WeakPtr & v) +{ + const auto &t = *this; + return t == v; +} + +bool WeakPtr::operator==(const WeakPtr & v) const +{ + return _id == v._id; +} + +bool WeakPtr::operator==(const GObject * v) +{ + const auto &t = *this; + return t == v; +} + +GObject * WeakPtr::ptr() const +{ + if (_id == 0) + return nullptr; + + auto it = _weakPointers.find(_id); + if (it != _weakPointers.end()) + return it->second; + else + return nullptr; +} + +bool WeakPtr::onStage() const +{ + GObject *p = ptr(); + return p && p->onStage(); +} + +uint64_t WeakPtr::add(GObject * obj) +{ + if (obj) + { + if (obj->_weakPtrRef == 0) + _weakPointers[obj->_uid] = obj; + obj->_weakPtrRef++; + return obj->_uid; + } + else + return 0; +} + +GObject* WeakPtr::remove(uint64_t id) +{ + auto it = _weakPointers.find(id); + if (it != _weakPointers.end()) + { + GObject* obj = it->second; + obj->_weakPtrRef--; + if (obj->_weakPtrRef == 0) + _weakPointers.erase(it); + return obj; + } + else + return nullptr; +} + +void WeakPtr::markDisposed(GObject * obj) +{ + auto it = _weakPointers.find(obj->_uid); + if (it != _weakPointers.end()) + _weakPointers.erase(it); +} + +NS_FGUI_END diff --git a/extensions/fairygui/utils/WeakPtr.h b/extensions/fairygui/utils/WeakPtr.h new file mode 100644 index 0000000000..7fcbc77544 --- /dev/null +++ b/extensions/fairygui/utils/WeakPtr.h @@ -0,0 +1,45 @@ +#ifndef __WEAKPTR_H__ +#define __WEAKPTR_H__ + +#include "cocos2d.h" +#include "FairyGUIMacros.h" + +NS_FGUI_BEGIN + +class GObject; + +class WeakPtr +{ +public: + WeakPtr(); + explicit WeakPtr(GObject* obj); + explicit WeakPtr(const WeakPtr& other); + explicit WeakPtr(WeakPtr&& other); + ~WeakPtr(); + + WeakPtr& operator= (const WeakPtr& other); + WeakPtr& operator= (WeakPtr&& other); + WeakPtr& operator= (GObject* obj); + bool operator!= (const WeakPtr& v); + bool operator!= (const WeakPtr& v) const; + bool operator== (const WeakPtr& v); + bool operator== (const WeakPtr& v) const; + bool operator== (const GObject* v); + bool operator== (const GObject* v) const { return ptr() == v; } + + GObject* ptr() const; + bool onStage() const; + +private: + uint64_t _id; + + static uint64_t add(GObject * obj); + static GObject* remove(uint64_t id); + static void markDisposed(GObject* obj); + + friend class GObject; +}; + +NS_FGUI_END + +#endif \ No newline at end of file