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" /> + + +