From dae3aa1728cd08c46c965c75d2cee9e413b6019e Mon Sep 17 00:00:00 2001 From: minerjr Date: Mon, 18 May 2026 16:35:04 -0300 Subject: [PATCH] FIRE-35859 - Group Script Dialogs into one Multi-Floater window Add new Multi-Floater and class for LLScriptFloaters. Allows LLScriptDialogs to dock into a single window instead of the on screen chicklets. New Class named "FSFloaterScriptDialogContainer" and new UI XML named "floater_fs_script_dialog_container.xml" Based around the IM floater container. Option in Preferences -> User Interface -> 2D Overlay - "Use Script Dialog dock window" that is used to turn on the feature. If unckecked, all normal behavior will work. When enabled, the Script Dialog Position combobox is disabled as the docked option is forced. Docked Script Dialog floaters are able to tear off the dock, dock again and close. --- indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 27 + .../fsfloaterscriptdialogcontainer.cpp | 512 ++++++++++++++++++ .../newview/fsfloaterscriptdialogcontainer.h | 113 ++++ indra/newview/llfloaterpreference.cpp | 33 ++ indra/newview/llfloaterpreference.h | 3 + indra/newview/llscriptfloater.cpp | 172 ++++++ indra/newview/llscriptfloater.h | 6 + indra/newview/llviewerfloaterreg.cpp | 2 + .../en/floater_fs_script_dialog_container.xml | 44 ++ .../default/xui/en/panel_preferences_UI.xml | 14 + 11 files changed, 928 insertions(+) create mode 100644 indra/newview/fsfloaterscriptdialogcontainer.cpp create mode 100644 indra/newview/fsfloaterscriptdialogcontainer.h create mode 100644 indra/newview/skins/default/xui/en/floater_fs_script_dialog_container.xml diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 34e5a0fa2d..7da8ffb95f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -138,6 +138,7 @@ set(viewer_SOURCE_FILES fsfloaterposestand.cpp fsfloaterprotectedfolders.cpp fsfloaterradar.cpp + fsfloaterscriptdialogcontainer.cpp fsfloatersearch.cpp fsfloatersplashscreensettings.cpp fsfloaterstatistics.cpp @@ -998,6 +999,7 @@ set(viewer_HEADER_FILES fsfloaterposestand.h fsfloaterprotectedfolders.h fsfloaterradar.h + fsfloaterscriptdialogcontainer.h fsfloatersearch.h fsfloatersplashscreensettings.h fsfloaterstatistics.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 74c5c756b2..3ec8901c72 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -18992,6 +18992,9 @@ Change of this parameter will affect the layout of buttons in notification toast avatar_picker stats script_floater + + fs_script_floater_container + world_map preferences flickr @@ -25969,6 +25972,30 @@ Change of this parameter will affect the layout of buttons in notification toast Value 0 + + FSSDUseDockFloater + + Comment + If enabled, Script Dialogs will dock in a shared window. Also if enabled, the Script Dialogs Position feature will be disabled. + Persist + 1 + Type + Boolean + Value + 0 + + FSSDPositionBackup + + Comment + Stores a backup value of the Script Dialog position which is overwriten when FSSDUseDockFloater is enabled. + Persist + 1 + Type + S32 + Value + 3 + + FSRowsPerScriptDialog Comment diff --git a/indra/newview/fsfloaterscriptdialogcontainer.cpp b/indra/newview/fsfloaterscriptdialogcontainer.cpp new file mode 100644 index 0000000000..403e1032ce --- /dev/null +++ b/indra/newview/fsfloaterscriptdialogcontainer.cpp @@ -0,0 +1,512 @@ +/** + * @file fsfloaterscriptdialogcontainer.cpp + * @brief Multifloater containing active Script Dialog floaters in separate tab container. + * @author minerjr@firestorm + * + * $LicenseInfo:firstyear=2026&license=fsviewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (c) 2026 The Phoenix Firestorm Project, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llviewercontrol.h" +#include "llchiclet.h" +#include "llchicletbar.h" +#include "llfloaterreg.h" +#include "llscriptfloater.h" +#include "fsfloaterscriptdialogcontainer.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llemojihelper.h" +#include "lltoolbarview.h" +#include "llcontrol.h" +#include "lltrans.h" +#include "fscommon.h" + +// This code is based on the fsfloaterimcontainer class, but modified for the purpose of grouping Script Dialog floaters instead of Instant +// Messages. Some methods are left in case in the future bugs are discovered and code needs to be updated to be more similar to the +// fsfloaterimcontainer class. + +// +// FSFloaterScriptDialogContainer +// +FSFloaterScriptDialogContainer::FSFloaterScriptDialogContainer(const LLSD& seed) : + LLMultiFloater(seed), + mIsAddingNewSession(false), + mInitialWidth(0), + mInitialHeight(0) +{ + LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::DOCKED, this); +} + +FSFloaterScriptDialogContainer::~FSFloaterScriptDialogContainer() +{ + mNewMessageConnection.disconnect(); + LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::DOCKED, this); +} + +bool FSFloaterScriptDialogContainer::postBuild() +{ + mInitialWidth = getRect().getWidth(); + mInitialHeight = getRect().getHeight(); + + mNewMessageConnection = + LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&FSFloaterScriptDialogContainer::onNewMessageReceived, this, _1)); + // Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button + // mTabContainer will be initialized in LLMultiFloater::addChild() + + mTabContainer->setAllowRearrange(true); + mTabContainer->setRearrangeCallback(boost::bind(&FSFloaterScriptDialogContainer::onScriptTabRearrange, this, _1, _2)); + //mTabContainer->setDoubleClickCallback(boost::bind(&FSFloaterScriptDialogContainer::onDoubleClick, this)); + setDoubleClickCallback(boost::bind(&FSFloaterScriptDialogContainer::onDoubleClick, this)); + + return true; +} + +void FSFloaterScriptDialogContainer::initTabs() +{ +} + +// [SL:KB] - Patch: UI-TabRearrange | Checked: 2012-05-05 (Catznip-3.3.0) +void FSFloaterScriptDialogContainer::onScriptTabRearrange(S32 tab_index, LLPanel* tab_panel) +{ + LLFloater* pIMFloater = dynamic_cast(tab_panel); + if (!pIMFloater) + return; + + const LLUUID& idSession = pIMFloater->getKey().asUUID(); + if (idSession.isNull()) + return; + + LLChicletPanel* pChicletPanel = LLChicletBar::instance().getChicletPanel(); + LLChiclet* pIMChiclet = pChicletPanel->findChiclet(idSession); + pChicletPanel->setChicletIndex(pIMChiclet, tab_index - mTabContainer->getNumLockedTabs()); +} +// [/SL:KB] + +void FSFloaterScriptDialogContainer::onOpen(const LLSD& key) +{ + LLMultiFloater::onOpen(key); + initTabs(); + LLFloater* active_floater = getActiveFloater(); + if (active_floater && !active_floater->hasFocus()) + { + mTabContainer->setFocus(true); + } +} + +void FSFloaterScriptDialogContainer::onClose(bool app_quitting) +{ + if (app_quitting) + { + for (S32 i = 0; i < mTabContainer->getTabCount(); ++i) + { + LLScriptFloater* floater = dynamic_cast(mTabContainer->getPanelByIndex(i)); + if (floater) + { + floater->onClose(app_quitting); + } + } + } +} + +// Have the double click on a message to move on to the next title +void FSFloaterScriptDialogContainer::onDoubleClick() +{ + + // If we don't do something for the double click, the title will become blank... + //mTabContainer->selectNextTab(); +} + + +void FSFloaterScriptDialogContainer::onClickMinimize() +{ + LLFloater::onClickMinimize(this); +} + +void FSFloaterScriptDialogContainer::addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point) +{ + if (!floaterp) + return; + + floaterp->setCanMinimize(false); + floaterp->setTitle(floaterp->getShortTitle()); + //applyTitle(); + + // already here + if (floaterp->getHost() == this) + { + openFloater(floaterp->getKey()); + setFocus(true); + return; + } + + LLFloater* active_floater = getActiveFloater(); + + // [SL:KB] - Patch: UI-TabRearrange | Checked: 2012-06-22 (Catznip-3.3.0) + // If we're redocking a torn off IM floater, return it back to its previous place + if (!mIsAddingNewSession && floaterp->isTornOff()) + { + LLChicletPanel* pChicletPanel = LLChicletBar::instance().getChicletPanel(); + if (pChicletPanel) + { + LLIMChiclet* pChiclet = pChicletPanel->findChiclet(floaterp->getKey()); + if (pChiclet) + { + S32 idxChiclet = pChicletPanel->getChicletIndex(pChiclet); + //S32 index = 0; + if ((idxChiclet > 0) && (idxChiclet < pChicletPanel->getChicletCount())) + { + while (--idxChiclet >= 0) + { + if ((pChiclet = dynamic_cast(pChicletPanel->getChiclet(idxChiclet)))) + { + if (mSessions.contains(pChiclet->getSessionId())) + { + insertion_point = (LLTabContainer::eInsertionPoint)( + mTabContainer->getIndexForPanel(mSessions[pChiclet->getSessionId()]) + 1); + break; + } + } + } + } + else + { + insertion_point = (0 == idxChiclet) ? LLTabContainer::START : LLTabContainer::END; + } + + if (insertion_point == LLTabContainer::START && idxChiclet > 0) + { + onScriptTabRearrange(static_cast(insertion_point), floaterp); + } + } + } + } + // [/SL:KB] + + + if (!getVisible()) + { + S32 current_height = getRect().getHeight(); + reshape(mInitialWidth, mInitialHeight); + translate(0, current_height - mInitialHeight); + } + + LLMultiFloater::addFloater(floaterp, false, insertion_point); + + if (active_floater && !active_floater->hasFocus()) + { + mTabContainer->selectTabPanel(active_floater); + mTabContainer->setFocus(true); + } + + LLUUID session_id = floaterp->getKey(); + mSessions[session_id] = floaterp; + floaterp->mCloseSignal.connect(boost::bind(&FSFloaterScriptDialogContainer::onCloseFloater, this, session_id)); +} + +void FSFloaterScriptDialogContainer::addNewSession(LLFloater* floaterp) +{ + // Make sure we don't do some strange re-arranging if we add a new IM floater due to a new session + mIsAddingNewSession = true; + LLScriptFloater *floater = dynamic_cast(floaterp); + addFloater(floater, false, LLTabContainer::END); + mIsAddingNewSession = false; +} + +// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-12-11 (Catznip-3.2.0d) | Added: Catznip-3.2.0d +void FSFloaterScriptDialogContainer::removeFloater(LLFloater* floaterp) +{ + LLScriptFloater* script_floaterp = dynamic_cast(floaterp); + if (script_floaterp) + { + mFlashingTabStates.erase(floaterp); + + script_floaterp->setTitle(""); + script_floaterp->setDocked(false, true); + script_floaterp->setCanDock(false); + LL_INFOS() << "removeFloater 1(): Is Dockable: " << floaterp->isDockable() << " Is Docked: " << floaterp->isDocked() << " Is Torn Off" + << floaterp->isTornOff() << " " << LL_ENDL; + if (!script_floaterp->isDocked()) + { + LLMultiFloater::removeFloater(floaterp); + } + + if (mTabContainer->getTabCount() == 0) + { + // RN: could this potentially crash in draw hierarchy? + closeFloater(); + } + } +} +// [/SL:KB] + +void FSFloaterScriptDialogContainer::removeFloater(const LLUUID& id) +{ + LLScriptFloater* script_floaterp = getFloater(id); + + if (script_floaterp) + { + mFlashingTabStates.erase(script_floaterp); + + script_floaterp->setTitle(""); + script_floaterp->setDocked(false, true); + script_floaterp->setCanDock(false); + + LLMultiFloater::removeFloater(script_floaterp); + + if (mTabContainer->getTabCount() == 0) + { + // RN: could this potentially crash in draw hierarchy? + closeFloater(); + } + } +} + +bool FSFloaterScriptDialogContainer::hasFloater(LLFloater* floaterp) const +{ + for (S32 i = 0; i < mTabContainer->getTabCount(); ++i) + { + if (dynamic_cast(mTabContainer->getPanelByIndex(i)) == floaterp) + { + return true; + } + } + return false; +} + +bool FSFloaterScriptDialogContainer::hasFloater(const LLUUID& id) const +{ + for (S32 i = 0; i < mTabContainer->getTabCount(); ++i) + { + LLScriptFloater* current_floater = dynamic_cast(mTabContainer->getPanelByIndex(i)); + if (current_floater && current_floater->getNotificationId() == id) + { + return true; + } + } + return false; +} + +LLScriptFloater* FSFloaterScriptDialogContainer::getFloater(const LLUUID& id) const +{ + for (S32 i = 0; i < mTabContainer->getTabCount(); ++i) + { + LLScriptFloater* current_floater = dynamic_cast(mTabContainer->getPanelByIndex(i)); + if (current_floater && current_floater->getNotificationId() == id) + { + return current_floater; + } + } + + return nullptr; +} + +void FSFloaterScriptDialogContainer::onCloseFloater(LLUUID& id) +{ + mSessions.erase(id); + if (isShown()) + { + setFocus(true); + } + else if (isMinimized()) + { + setMinimized(true); // Make sure console output that needs to be shown is still doing so + } +} + +void FSFloaterScriptDialogContainer::onNewMessageReceived(const LLSD& msg) +{ + LLUUID session_id = msg["session_id"].asUUID(); + LLFloater* floaterp = get_ptr_in_map(mSessions, session_id); + LLFloater* current_floater = LLMultiFloater::getActiveFloater(); + + // KC: Don't flash tab on friend status changes per setting + if (floaterp && current_floater && floaterp != current_floater) + { + startFlashingTab(floaterp, msg["message"].asString()); + } +} + +FSFloaterScriptDialogContainer* FSFloaterScriptDialogContainer::findInstance() +{ + return LLFloaterReg::findTypedInstance("fs_script_dialog_container"); +} + +FSFloaterScriptDialogContainer* FSFloaterScriptDialogContainer::getInstance() +{ + return LLFloaterReg::getTypedInstance("fs_script_dialog_container"); +} + +void FSFloaterScriptDialogContainer::setVisible(bool b) +{ + LLMultiFloater::setVisible(b); +} + +void FSFloaterScriptDialogContainer::setMinimized(bool b) +{ + LLMultiFloater::setMinimized(b); + + if (!b) + { + // We want to restore the MultiFloater title to include the current tab's script floater's short title. + // Otherwise upon the window appearing the title will only have the first part of the Script Dialogs title... + LLFloater *floaterp = getCurrentScriptFloater(); + updateFloaterTitle(floaterp); + + } +} + +void FSFloaterScriptDialogContainer::reloadEmptyFloaters() +{ +} + +// virtual +void FSFloaterScriptDialogContainer::draw() +{ + LLFloater::draw(); +} + +LLFloater* FSFloaterScriptDialogContainer::getCurrentScriptFloater() +{ + LLScriptFloater* script_floater = dynamic_cast(mTabContainer->getPanelByIndex(mTabContainer->getCurrentPanelIndex())); + if (script_floater) + { + return script_floater; + } + + return nullptr; +} + +void FSFloaterScriptDialogContainer::addFlashingSession(const LLUUID& session_id) +{ + uuid_vec_t::iterator found = std::find(mFlashingSessions.begin(), mFlashingSessions.end(), session_id); + if (found == mFlashingSessions.end()) + { + mFlashingSessions.emplace_back(session_id); + } + + checkFlashing(); +} + +void FSFloaterScriptDialogContainer::checkFlashing() +{ + gToolBarView->flashCommand(LLCommandId("script_container"), !mFlashingSessions.empty(), isMinimized()); +} + +void FSFloaterScriptDialogContainer::computeResizeLimits(S32& new_min_width, S32& new_min_height) +{ + static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + // Add the min width of the tab control if the tab is to the left of the floater + S32 tabcntr_min_width = 0; + if (mTabContainer->getTabPosition() == LLTabContainer::TabPosition::LEFT) + { + tabcntr_min_width = mTabContainer->getMinTabWidth(); + } + else + { + tabcntr_min_width = mTabContainer->getTotalTabWidth(); + } + // possibly increase minimum size constraint due to children's minimums. + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx); + if (floaterp) + { + //new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2); + new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2 + tabcntr_min_width); + new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height); + } + } +} + +void FSFloaterScriptDialogContainer::growToFit(S32 content_width, S32 content_height) +{ + static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + // Add the min width of the tab control if the tab is to the left of the floater + S32 tabcntr_min_width = 0; + // There is bug in how the total tab width is calculated as it adds the width, even if the buttons are vertical. + if (mTabContainer->getTabPosition() == LLTabContainer::TabPosition::LEFT) + { + // Use the min as it's set to the actual value of tab_width + tabcntr_min_width = mTabContainer->getMinTabWidth(); + } + // Else, the tabs are side by side, so then use the total tab width + else + { + tabcntr_min_width = mTabContainer->getTotalTabWidth(); + } + S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2 + tabcntr_min_width); + S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height); + + if (isMinimized()) + { + LLRect newrect; + newrect.setLeftTopAndSize(getExpandedRect().mLeft, getExpandedRect().mTop, new_width, new_height); + setExpandedRect(newrect); + } + else + { + S32 old_height = getRect().getHeight(); + reshape(new_width, new_height); + // keep top left corner in same position + translate(0, old_height - new_height); + } +} + +bool FSFloaterScriptDialogContainer::applyRectControl() +{ + bool saved_rect = LLFloater::applyRectControl(); + + //LLRect current_rect; + + //current_rect.setLeftTopAndSize(getRect().mTop, getRect().mLeft, mInitalWidth, mInitalHeight); + + + + + return saved_rect; +} + +void FSFloaterScriptDialogContainer::tabOpen(LLFloater* opened_floater, bool from_click) +{ + LLEmojiHelper::instance().hideHelper(nullptr, true); + + mFlashingTabStates.erase(opened_floater); +} + +void FSFloaterScriptDialogContainer::startFlashingTab(LLFloater* floater, const std::string& message) +{ + if (LLMultiFloater::isFloaterFlashing(floater)) + { + LLMultiFloater::setFloaterFlashing(floater, false); + } + LLMultiFloater::setFloaterFlashing(floater, true, false); +} + +// EOF diff --git a/indra/newview/fsfloaterscriptdialogcontainer.h b/indra/newview/fsfloaterscriptdialogcontainer.h new file mode 100644 index 0000000000..cdd0efcc45 --- /dev/null +++ b/indra/newview/fsfloaterscriptdialogcontainer.h @@ -0,0 +1,113 @@ +/** + * @file fsfloaterscriptdialogcontainer.h + * @brief Multifloater containing active Script Dialog floaters in separate tab container. + * @author minerjr@firestorm + * + * $LicenseInfo:firstyear=2026&license=fsviewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (c) 2026 The Phoenix Firestorm Project, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + * $/LicenseInfo$ + */ + + +#ifndef FS_FLOATERSCRIPTDIALOGCONTAINER_H +#define FS_FLOATERSCRIPTDIALOGCONTAINER_H + +#include "llmultifloater.h" +#include "lltransientdockablefloater.h" +#include "llnotificationptr.h" + +class LLToastPanel; +class LLTabContainer; +class LLScriptFloater; + +///////////////////////////// +// FSFloaterScriptContainer// +/////////////////////////// + +class FSFloaterScriptDialogContainer : public LLMultiFloater +{ +public: + FSFloaterScriptDialogContainer(const LLSD& seed); + virtual ~FSFloaterScriptDialogContainer(); + + /*virtual*/ bool postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); + void onDoubleClick(); + void onCloseFloater(LLUUID& id); + void onClickMinimize(); + + /*virtual*/ void draw(); + /*virtual*/ void growToFit(S32 content_width, S32 content_height) override; + /*virtual*/ bool applyRectControl() override; + /*virtual*/ void addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END) override; + + // [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-12-11 (Catznip-3.2.0d) | Added: Catznip-3.2.0d + /*virtual*/ void removeFloater(LLFloater* floaterp); + void removeFloater(const LLUUID& id); + // [/SL:KB] + bool hasFloater(LLFloater* floaterp) const; + bool hasFloater(const LLUUID& id) const; + LLScriptFloater* getFloater(const LLUUID& id) const; + + void addNewSession(LLFloater* floaterp); + + static FSFloaterScriptDialogContainer* findInstance(); + static FSFloaterScriptDialogContainer* getInstance(); + + virtual void setVisible(bool b); + /*virtual*/ void setMinimized(bool b); + + void onNewMessageReceived(const LLSD& msg); // public so nearbychat can call it directly. TODO: handle via callback. -AO + + static void reloadEmptyFloaters(); + void initTabs(); + + void addFlashingSession(const LLUUID& session_id); + + void tabOpen(LLFloater* opened_floater, bool from_click); + + void startFlashingTab(LLFloater* floater, const std::string& message); + +private: + LLFloater* getCurrentScriptFloater(); + + typedef std::map avatarID_panel_map_t; + avatarID_panel_map_t mSessions; + boost::signals2::connection mNewMessageConnection; + + void checkFlashing(); + uuid_vec_t mFlashingSessions; + + bool mIsAddingNewSession; + S32 mInitialHeight; + S32 mInitialWidth; + + std::map mFlashingTabStates; + + /*virtual*/ void computeResizeLimits(S32& new_min_width, S32& new_min_height) override; + // [SL:KB] - Patch: UI-TabRearrange | Checked: 2012-05-05 (Catznip-3.3.0) +protected: + void onScriptTabRearrange(S32 tab_index, LLPanel* tab_panel); + // [/SL:KB] +}; + +#endif // FS_FLOATERSCRIPTDIALOGCONTAINER_H diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 16cd89b1f9..04f18f746e 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -150,6 +150,7 @@ #include "NACLantispam.h" #include "../llcrashlogger/llcrashlogger.h" #include +#include "llscriptfloater.h" // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window #if LL_WINDOWS #include #endif @@ -829,6 +830,10 @@ bool LLFloaterPreference::postBuild() onAvatarTagSettingsChanged(); // + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + gSavedSettings.getControl("FSSDUseDockFloater")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::updateScriptDialogsPosition, this)); + updateScriptDialogsPosition(); + // [FIRE-35859] // Correct enabled state of Animated Script Dialogs option gSavedSettings.getControl("ScriptDialogsPosition")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::updateAnimatedScriptDialogs, this)); updateAnimatedScriptDialogs(); @@ -3344,6 +3349,34 @@ void LLFloaterPreference::updateAnimatedScriptDialogs() } // +// [FIRE-35859] - Group Script Dialogs into one Multi-Floater window +void LLFloaterPreference::updateScriptDialogsPosition() +{ + // Get the state of the use script dialog dock floater flag + static LLCachedControl use_container_window(gSavedSettings, "FSSDUseDockFloater", false); + + // If the feature is enabled, we need to backup the current Script Dialog Position, + // set it to docked, + if (use_container_window) + { + // Back up the position value + gSavedSettings.setS32("FSSDPositionBackup", gSavedSettings.getS32("ScriptDialogsPosition")); + // Change the position value to DOCKED = 1 + gSavedSettings.setS32("ScriptDialogsPosition", 1); + // Need to enable the ScriptDialogsPosition UI combobox first + childSetEnabled("ScriptDialogsPositionDropdown", !use_container_window); + } + // If disabled, we need to restore the setting from the back up + else + { + // Need to enable the ScriptDialogsPosition UI combobox first + childSetEnabled("ScriptDialogsPositionDropdown", !use_container_window); + // Restore the Script Dialog Position from the backup value + gSavedSettings.setS32("ScriptDialogsPosition", gSavedSettings.getS32("FSSDPositionBackup")); + } +} +// [FIRE-35859] + //------------------------------Updater--------------------------------------- // FIRE-6340, FIRE-6567 - Setting Bandwidth issues diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 1e93e24946..8e5f3b7a88 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -175,6 +175,9 @@ protected: // Correct enabled state of Animated Script Dialogs option void updateAnimatedScriptDialogs(); + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + void updateScriptDialogsPosition(); + // Group Notices and chiclets location setting conversion bool => S32 void onShowGroupNoticesTopRightChanged(); diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp index 24a4112994..67ae3ff5f3 100644 --- a/indra/newview/llscriptfloater.cpp +++ b/indra/newview/llscriptfloater.cpp @@ -52,6 +52,7 @@ #include "llnavigationbar.h" #include "omnifilterengine.h" // Omnifilter support // +#include "fsfloaterscriptdialogcontainer.h" // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -78,6 +79,7 @@ LLScriptFloater::LLScriptFloater(const LLSD& key) : LLDockableFloater(NULL, gSavedSettings.getS32("FSChatWindow") != 1, key) // , mScriptForm(NULL) +, mApplyRect(true) // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window , mSaveFloaterPosition(false) { setMouseDownCallback(boost::bind(&LLScriptFloater::onMouseDown, this)); @@ -85,11 +87,55 @@ LLScriptFloater::LLScriptFloater(const LLSD& key) // FIRE-13459: Script dialogs ignore opening position in mouselook //mIsDockedStateForcedCallback = boost::bind(&LLAgentCamera::cameraMouselook, &gAgentCamera); mNoTransparency = gSavedSettings.getBOOL("FSScriptDialogNoTransparency"); + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + static LLCachedControl use_container_window(gSavedSettings, "FSSDUseDockFloater", false); + + if (use_container_window) + { + // If we are using a multi-floater to store the floater, we need to set it to not allow minimize and can dock. + // Helps hide buttons which cause issues. + setCanMinimize(false); + setCanDock(false); + } + // [FIRE-35859] } bool LLScriptFloater::toggle(const LLUUID& notification_id) { LLScriptFloater* floater = LLFloaterReg::findTypedInstance("script_floater", notification_id); + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + FSFloaterScriptDialogContainer* script_dialog_containerp = FSFloaterScriptDialogContainer::getInstance(); + static LLCachedControl use_container_window(gSavedSettings, "FSSDUseDockFloater", false); + + if (script_dialog_containerp) + { + if (script_dialog_containerp->getFloaterCount() == 0 || !use_container_window) + { + script_dialog_containerp->setVisible(false); + } + else if (use_container_window && floater && !floater->isTornOff()) + { + script_dialog_containerp->setVisible(true); + + if (floater == script_dialog_containerp->getActiveFloater()) + { + script_dialog_containerp->selectNextFloater(); + } + else + { + script_dialog_containerp->selectFloater(floater); + } + + LLChicletPanel* chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel(); + if (NULL != chiclet_panelp) + { + chiclet_panelp->setChicletToggleState(notification_id, true); + } + + return true; + } + } + // [FIRE-35859] // show existing floater if(floater) @@ -256,6 +302,17 @@ void LLScriptFloater::onClose(bool app_quitting) void LLScriptFloater::setDocked(bool docked, bool pop_on_undock /* = true */) { + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + static LLCachedControl use_container_window(gSavedSettings, "FSSDUseDockFloater", false); + // If using the container window, need to disable the CanMinimize UI elements of the float when being docked. + if (use_container_window) + { + if (docked) + { + setCanMinimize(false); + } + } + // [FIRE-35859] LLDockableFloater::setDocked(docked, pop_on_undock); savePosition(); @@ -316,8 +373,17 @@ void LLScriptFloater::savePosition() void LLScriptFloater::restorePosition() { LLScriptFloaterManager::FloaterPositionInfo fpi; + static LLCachedControl use_container_window(gSavedSettings, "FSSDUseDockFloater", false); // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window if(LLScriptFloaterManager::getInstance()->getFloaterPosition(mObjectId, fpi)) { + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + // If using the container window, need to reset the dock to the container window. + if (use_container_window) + { + dockToContainer(fpi.mDockState); + } + else + // [FIRE-35859] dockToChiclet(fpi.mDockState); if(!fpi.mDockState) { @@ -327,6 +393,14 @@ void LLScriptFloater::restorePosition() } else { + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + // Else, we want to dock to the container floater + if (use_container_window) + { + dockToContainer(true); + } + else + // [FIRE-35859] dockToChiclet(true); } } @@ -422,6 +496,36 @@ void LLScriptFloater::dockToChiclet(bool dock, bool scroll_to_chiclet /* = true } } +// [FIRE-35859] - Group Script Dialogs into one Multi-Floater window +// Allow docking to FSFloaterScriptDialogContainer multi-floater. +void LLScriptFloater::dockToContainer(bool dock) +{ + if (getDockControl() == nullptr) + { + FSFloaterScriptDialogContainer* script_dialog_containerp = FSFloaterScriptDialogContainer::getInstance(); + + if (script_dialog_containerp) + { + // Stop saving position while we dock floater + bool save = getSavePosition(); + setSavePosition(false); + + // do not add existed floaters to avoid adding torn off instances + if (!script_dialog_containerp->hasFloater(this) && dock) + { + script_dialog_containerp->addNewSession(this); + } + + openFloater(getKey()); + setFocus(true); + + // Restore saving + setSavePosition(save); + } + } +} +// [FIRE-35859] + void LLScriptFloater::hideToastsIfNeeded() { using namespace LLNotificationsUI; @@ -714,6 +818,20 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id) set_new_message |= !floater->hasFocus(); } + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + static LLCachedControl use_container_window(gSavedSettings, "FSSDUseDockFloater", false); + + if (use_container_window) + { + FSFloaterScriptDialogContainer* script_dialog_containerp = FSFloaterScriptDialogContainer::getInstance(); + + if (script_dialog_containerp) + { + script_dialog_containerp->removeFloater(old_id); + } + } + // [FIRE-35859] + removeNotification(old_id); } } @@ -772,6 +890,13 @@ void LLScriptFloaterManager::onRemoveNotification(const LLUUID& notification_id) return; } + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + static LLCachedControl use_container_window(gSavedSettings, "FSSDUseDockFloater", false); + + static LLCachedControl dialog_position(gSavedSettings, "ScriptDialogsPosition", 1); + + if (!use_container_window && dialog_position > static_cast(LLScriptFloater::eDialogPosition::POS_DOCKED)) + // [FIRE-35859] DialogStack::instance().pop(notification_id); // Dialog Stacking browser // remove related chiclet @@ -1071,6 +1196,37 @@ void LLScriptFloater::draw() } // +// [FIRE-35859] - Group Script Dialogs into one Multi-Floater window +// Borrowed from the fsfloaterim.cpp +bool LLScriptFloater::applyRectControl() +{ + bool res = true; + + // We need to do some sort of hack here to prevent torn-off floaters from + // jumping around if clicking on the IM chiclets: Clicking on a chiclet to + // switch to a particular IM floater will call FSFloaterIM::show() and in + // the process LLFloater::applyControlsAndPosition(). Depending on the result + // of the call to applyRectControl(), applyPositioning() positioning is + // called that will ultimately set the position of the floater depending + // of the position of the last floater in the group. However, this doesn't + // work properly and will cause floaters to jump. To prevent this, we only + // apply the rect if not called by FSFloaterIM::show(). To prevent an + // additionally misplaced floater caused by cascading a group of IM floaters, + // we force LLFloater::applyRectControl() into the path where no cascaded + // position is going to be applied by temporarily clear the instance name + // of the floater. -AH + if (mApplyRect) + { + std::string name = mInstanceName; + mInstanceName.clear(); + res = LLFloater::applyRectControl(); + mInstanceName = name; + } + + return res; +} +// [FIRE-35859] + // script dialogs position LLScriptFloater* LLScriptFloater::show(const LLUUID& notification_id) { @@ -1081,6 +1237,22 @@ LLScriptFloater* LLScriptFloater::show(const LLUUID& notification_id) //LLDialog(LLGiveInventory and LLLoadURL) should no longer steal focus (see EXT-5445) floater->setAutoFocus(false); + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + static LLCachedControl use_container_window(gSavedSettings, "FSSDUseDockFloater", false); + + if (use_container_window) + { + LLNotificationPtr notification = LLNotifications::getInstance()->find(notification_id); + if (notification != NULL) + { + // floater->setTitle(LLScriptFloaterManager::getObjectName(notification_id)); + floater->setShortTitle(LLScriptFloaterManager::getObjectName(notification_id)); + } + // Call self contained dock to container method off of the new floater and return the container docked floater. + floater->dockToContainer(true); + return floater; + } + // [FIRE-35859] eDialogPosition dialog_position = (eDialogPosition)gSavedSettings.getS32("ScriptDialogsPosition"); bool chicletsDisabled = gSavedSettings.getBOOL("FSDisableIMChiclets"); diff --git a/indra/newview/llscriptfloater.h b/indra/newview/llscriptfloater.h index 787125e320..29af7fc5b2 100644 --- a/indra/newview/llscriptfloater.h +++ b/indra/newview/llscriptfloater.h @@ -231,6 +231,11 @@ protected: //void dockToChiclet(bool dock); void dockToChiclet(bool dock, bool scroll_to_chiclet = true); + // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window + void dockToContainer(bool dock); + bool applyRectControl() override; + // [FIRE-35859] + private: bool isScriptTextbox(LLNotificationPtr notification); @@ -240,6 +245,7 @@ private: bool mSaveFloaterPosition; bool mNoTransparency; + bool mApplyRect; // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window // Animated menus public: S32 mCurrentHeight; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index ae3f2cfabc..746c1218e5 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -213,6 +213,7 @@ #include "fsfloaterposestand.h" #include "fsfloaterprotectedfolders.h" #include "fsfloaterradar.h" +#include "fsfloaterscriptdialogcontainer.h" // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window #include "fsfloatersearch.h" #include "fsfloatersplashscreensettings.h" #include "fsfloaterstatistics.h" @@ -652,6 +653,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("fs_poser", "floater_fs_poser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); // [FIRE-30873]: Poser LLFloaterReg::add("fs_protectedfolders", "floater_fs_protectedfolders.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_radar", "floater_fs_radar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("fs_script_dialog_container","floater_fs_script_dialog_container.xml",(LLFloaterBuildFunc)&LLFloaterReg::build); // [FIRE-35859] - Group Script Dialogs into one Multi-Floater window LLFloaterReg::add("fs_splash_screen_settings", "floater_fs_splash_screen_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_streamtitle", "floater_fs_streamtitle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("fs_streamtitlehistory", "floater_fs_streamtitlehistory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/skins/default/xui/en/floater_fs_script_dialog_container.xml b/indra/newview/skins/default/xui/en/floater_fs_script_dialog_container.xml new file mode 100644 index 0000000000..af79629c5a --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_fs_script_dialog_container.xml @@ -0,0 +1,44 @@ + + + + + diff --git a/indra/newview/skins/default/xui/en/panel_preferences_UI.xml b/indra/newview/skins/default/xui/en/panel_preferences_UI.xml index 76626a75f0..bb8a73a4f2 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_UI.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_UI.xml @@ -468,6 +468,20 @@ top_delta="4" width="256" /> + + +