diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index abb853ffd1..79ab398cd0 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -2047,6 +2047,26 @@ S32 LLTabContainer::getTabContainedAtPoint(S32 x, S32 y) const
}
return -1;
}
+
+void LLTabContainer::setTabButtonVisible(const LLPanel* panel, bool visible)
+{
+ for (LLTabTuple* tuple : mTabList)
+ {
+ if (tuple->mTabPanel == panel)
+ {
+ if (tuple->mVisible != visible)
+ {
+ tuple->mVisible = visible;
+ if (tuple->mButton)
+ {
+ tuple->mButton->setVisible(visible);
+ }
+ updateMaxScrollPos();
+ }
+ break;
+ }
+ }
+}
//
void LLTabContainer::onNextBtn( const LLSD& data )
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 8fa2b74d6e..8a0813a977 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -245,6 +245,7 @@ public:
// FIRE-35598: Custom filters in inventory (feature idea: Catznip)
S32 getTabContainedAtPoint(S32 x, S32 y) const;
void setTabPadding(S32 index, S32 padding);
+ void setTabButtonVisible(const LLPanel* panel, bool visible);
//
TabPosition getTabPosition() const { return mTabPosition; }
diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml
index 2546a5063a..c2f83389fb 100644
--- a/indra/newview/app_settings/settings_per_account.xml
+++ b/indra/newview/app_settings/settings_per_account.xml
@@ -1401,7 +1401,7 @@
HideFromEditor
1
Comment
- User-defined custom inventory filter tabs (array of maps with 'name', 'filter' and 'states' keys)
+ User-defined custom inventory filter tabs (array of maps with 'name', 'filter', 'states' and 'tabhidden' keys)
Persist
1
Type
diff --git a/indra/newview/fsinventorycustomtabs.cpp b/indra/newview/fsinventorycustomtabs.cpp
index a3031a78d4..17ada047f0 100644
--- a/indra/newview/fsinventorycustomtabs.cpp
+++ b/indra/newview/fsinventorycustomtabs.cpp
@@ -59,6 +59,10 @@ namespace
constexpr char CLOSE_ICON_NAME[] = "Icon_Close_Foreground";
constexpr char CLOSE_ICON_PRESS[] = "Icon_Close_Press";
constexpr char TAB_BUTTON_PREFIX[] = "htab_";
+ constexpr char SHOW_HIDDEN_PANEL_NAME[] = "FSInventoryCustomTabShowHidden";
+ constexpr char SHOW_HIDDEN_ICON_NAME[] = "Arrow_Down";
+ constexpr char HIDDEN_MENU_NAME[] = "fs_inv_hidden_tabs_menu";
+ constexpr char TABHIDDEN_KEY[] = "tabhidden";
constexpr S32 CLOSE_ICON_SIZE = 16;
constexpr S32 CLOSE_ICON_MARGIN = 4;
constexpr S32 CLOSE_BUTTON_RESERVE = CLOSE_ICON_SIZE + CLOSE_ICON_MARGIN + 2;
@@ -100,6 +104,7 @@ FSInventoryCustomTabs::FSInventoryCustomTabs(LLPanelMainInventory* parent)
}
installAddTab();
+ installShowHiddenTab();
allInstances().insert(this);
}
@@ -124,6 +129,10 @@ FSInventoryCustomTabs::~FSInventoryCustomTabs()
{
mAddClickConnection.disconnect();
}
+ if (mShowHiddenClickConnection.connected())
+ {
+ mShowHiddenClickConnection.disconnect();
+ }
if (mTabs && !mPanels.empty())
{
save();
@@ -133,6 +142,15 @@ FSInventoryCustomTabs::~FSInventoryCustomTabs()
menu->die();
mMenuHandle.markDead();
}
+ if (auto* hidden_menu = mHiddenMenuHandle.get())
+ {
+ if (gMenuHolder)
+ {
+ gMenuHolder->removeChild(hidden_menu);
+ }
+ delete hidden_menu;
+ mHiddenMenuHandle.markDead();
+ }
}
// ---------------------------------------------------------------------------
@@ -149,6 +167,7 @@ void FSInventoryCustomTabs::registerCommitCallbacks(LLPanelMainInventory* parent
reg.add("FSInventoryCustomTab.Rename", [parent](LLUICtrl*, const LLSD&) { if (auto* self = instanceFor(parent)) self->onRenameClicked(); });
reg.add("FSInventoryCustomTab.Clone", [parent](LLUICtrl*, const LLSD&) { if (auto* self = instanceFor(parent)) self->onCloneClicked(); });
reg.add("FSInventoryCustomTab.Close", [parent](LLUICtrl*, const LLSD&) { if (auto* self = instanceFor(parent)) self->onCloseClicked(); });
+ reg.add("FSInventoryCustomTab.Hide", [parent](LLUICtrl*, const LLSD&) { if (auto* self = instanceFor(parent)) self->onHideClicked(); });
}
bool FSInventoryCustomTabs::handleRightMouseDown(S32 x, S32 y)
@@ -256,6 +275,80 @@ void FSInventoryCustomTabs::onAddClicked()
}
}
+void FSInventoryCustomTabs::onShowHiddenClicked()
+{
+ if (!mTabs || mHidden.empty())
+ {
+ return;
+ }
+
+ if (auto* old_menu = mHiddenMenuHandle.get())
+ {
+ if (gMenuHolder)
+ {
+ gMenuHolder->removeChild(old_menu);
+ }
+ delete old_menu;
+ mHiddenMenuHandle.markDead();
+ }
+
+ LLContextMenu::Params menu_params;
+ menu_params.name(HIDDEN_MENU_NAME);
+ menu_params.visible(false);
+ LLContextMenu* menu = LLUICtrlFactory::create(menu_params);
+ if (!menu)
+ {
+ return;
+ }
+
+ auto handle = getHandle();
+ const S32 count = mTabs->getTabCount();
+ for (S32 i = 0; i < count; ++i)
+ {
+ auto* inv_panel = dynamic_cast(mTabs->getPanelByIndex(i));
+ if (!inv_panel || !mHidden.count(inv_panel))
+ {
+ continue;
+ }
+
+ std::string label = computeDisplayLabel(inv_panel);
+ if (label.empty())
+ {
+ label = getDefaultTabName();
+ }
+
+ const std::string panel_name = inv_panel->getName();
+ LLMenuItemCallGL::Params item_params;
+ item_params.name(panel_name);
+ item_params.label(label);
+ item_params.on_click.function([handle, panel_name](LLUICtrl*, const LLSD&)
+ {
+ if (auto* self = handle.get())
+ {
+ if (self->mTabs)
+ {
+ self->showTab(dynamic_cast(self->mTabs->getPanelByName(panel_name)));
+ }
+ }
+ });
+ menu->addChild(LLUICtrlFactory::create(item_params));
+ }
+
+ mHiddenMenuHandle = menu->getHandle();
+ if (gMenuHolder)
+ {
+ gMenuHolder->addChild(menu);
+ }
+
+ if (auto* btn = findTabButton(mShowHiddenTabPanel))
+ {
+ S32 screen_x = 0;
+ S32 screen_y = 0;
+ btn->localPointToScreen(0, 0, &screen_x, &screen_y);
+ menu->show(screen_x, screen_y, btn);
+ }
+}
+
void FSInventoryCustomTabs::noteActivePanel(LLInventoryPanel* panel)
{
if (mLastActivePanel && mLastActivePanel != panel && isCustomTab(mLastActivePanel))
@@ -266,7 +359,7 @@ void FSInventoryCustomTabs::noteActivePanel(LLInventoryPanel* panel)
}
save();
}
- if (panel && panel != mAddTabPanel && mTabs && mTabs->getIndexForPanel(panel) >= 0)
+ if (panel && panel != mAddTabPanel && panel != mShowHiddenTabPanel && mTabs && mTabs->getIndexForPanel(panel) >= 0)
{
mLastActivePanel = panel;
}
@@ -329,7 +422,12 @@ bool FSInventoryCustomTabs::activePanelIsCustom(const LLPanelMainInventory* pare
bool FSInventoryCustomTabs::maybeHandleAddTabSelected(LLPanelMainInventory* parent)
{
auto* self = instanceFor(parent);
- if (!self || !parent->mFilterTabs || !self->isAddTab(parent->mFilterTabs->getCurrentPanel()))
+ if (!self || !parent->mFilterTabs)
+ {
+ return false;
+ }
+ const LLPanel* current = parent->mFilterTabs->getCurrentPanel();
+ if (!self->isAddTab(current) && !self->isShowHiddenTab(current))
{
return false;
}
@@ -453,6 +551,56 @@ void FSInventoryCustomTabs::installAddTab()
}
}
+void FSInventoryCustomTabs::installShowHiddenTab()
+{
+ if (!mTabs || mShowHiddenTabPanel)
+ {
+ return;
+ }
+
+ LLInventoryPanel::Params params;
+ params.name(SHOW_HIDDEN_PANEL_NAME);
+ params.sort_order_setting(LLInventoryPanel::INHERIT_SORT_ORDER);
+ params.preinitialize_views(false);
+ params.follows.flags(FOLLOWS_ALL);
+
+ mShowHiddenTabPanel = LLUICtrlFactory::create(params);
+ if (!mShowHiddenTabPanel)
+ {
+ return;
+ }
+
+ auto where = LLTabContainer::END;
+ if (mAddTabPanel)
+ {
+ const S32 add_idx = mTabs->getIndexForPanel(mAddTabPanel);
+ if (add_idx >= 0)
+ {
+ where = static_cast(add_idx);
+ }
+ }
+
+ mTabs->addTabPanel(LLTabContainer::TabPanelParams().panel(mShowHiddenTabPanel).label(LLStringUtil::null).select_tab(false).insert_at(where));
+
+ if (auto* btn = findTabButton(mShowHiddenTabPanel))
+ {
+ const S32 height = btn->getRect().getHeight();
+ const S32 width = llmax(mTabs->getMinTabWidth() / 2, ADD_TAB_MIN_WIDTH);
+ btn->reshape(width, height);
+ btn->setLeftHPad(2);
+ btn->setRightHPad(2);
+ btn->setImageOverlay(std::string(SHOW_HIDDEN_ICON_NAME));
+ std::string tooltip;
+ if (LLTrans::findString(tooltip, SHOW_HIDDEN_PANEL_NAME) && !tooltip.empty())
+ {
+ btn->setToolTip(tooltip);
+ }
+ mShowHiddenClickConnection = btn->setClickedCallback(boost::bind(&FSInventoryCustomTabs::onShowHiddenClicked, this));
+ }
+
+ mTabs->setTabButtonVisible(mShowHiddenTabPanel, false);
+}
+
LLInventoryPanel* FSInventoryCustomTabs::addTab(const std::string& explicit_name, const std::string& filter, bool select)
{
if (!mTabs || !mParent)
@@ -501,12 +649,13 @@ LLInventoryPanel* FSInventoryCustomTabs::addTab(const std::string& explicit_name
const std::string& display_label = explicit_name.empty() ? auto_label : explicit_name;
auto where = LLTabContainer::END;
- if (mAddTabPanel)
+ LLPanel* anchor = mShowHiddenTabPanel ? static_cast(mShowHiddenTabPanel) : static_cast(mAddTabPanel);
+ if (anchor)
{
- const S32 add_idx = mTabs->getIndexForPanel(mAddTabPanel);
- if (add_idx >= 0)
+ const S32 anchor_idx = mTabs->getIndexForPanel(anchor);
+ if (anchor_idx >= 0)
{
- where = static_cast(add_idx);
+ where = static_cast(anchor_idx);
}
}
@@ -665,25 +814,106 @@ void FSInventoryCustomTabs::onCloseConfirmed(const LLSD& notification, const LLS
if (mTabs->getCurrentPanel() == to_remove)
{
- const S32 idx = mTabs->getIndexForPanel(to_remove);
- if (idx > 0)
- {
- mTabs->selectTab(idx - 1);
- }
- else
- {
- mTabs->selectFirstTab();
- }
+ selectNearestVisibleTab(to_remove);
}
mPanels.erase(to_remove);
mExplicitlyNamed.erase(to_remove);
mPendingRootOpen.erase(to_remove);
+ mHidden.erase(to_remove);
mTabs->removeTabPanel(to_remove);
to_remove->die();
+ updateShowHiddenTabVisibility();
save();
}
+void FSInventoryCustomTabs::onHideClicked()
+{
+ if (isCustomTab(mContextPanel))
+ {
+ hideTab(mContextPanel);
+ }
+}
+
+void FSInventoryCustomTabs::hideTab(LLInventoryPanel* panel)
+{
+ if (!mTabs || !isCustomTab(panel) || mHidden.count(panel))
+ {
+ return;
+ }
+ const bool was_active = (mTabs->getCurrentPanel() == panel);
+ mHidden.insert(panel);
+ mTabs->setTabButtonVisible(panel, false);
+ if (mContextPanel == panel)
+ {
+ mContextPanel = nullptr;
+ }
+ if (mLastActivePanel == panel)
+ {
+ mLastActivePanel = nullptr;
+ }
+ if (was_active)
+ {
+ selectNearestVisibleTab(panel);
+ }
+ updateShowHiddenTabVisibility();
+ save();
+}
+
+void FSInventoryCustomTabs::showTab(LLInventoryPanel* panel)
+{
+ if (!mTabs || !isCustomTab(panel) || !mHidden.count(panel))
+ {
+ return;
+ }
+ mHidden.erase(panel);
+ mTabs->setTabButtonVisible(panel, true);
+ updateShowHiddenTabVisibility();
+ mTabs->selectTabPanel(panel);
+ save();
+}
+
+void FSInventoryCustomTabs::updateShowHiddenTabVisibility()
+{
+ if (mTabs && mShowHiddenTabPanel)
+ {
+ mTabs->setTabButtonVisible(mShowHiddenTabPanel, !mHidden.empty());
+ }
+}
+
+void FSInventoryCustomTabs::selectNearestVisibleTab(LLInventoryPanel* skip_panel)
+{
+ if (!mTabs)
+ {
+ return;
+ }
+
+ const S32 skip_idx = mTabs->getIndexForPanel(skip_panel);
+ const S32 count = mTabs->getTabCount();
+ const auto selectable = [this](LLPanel* p) -> bool
+ {
+ return p && p != static_cast(mAddTabPanel) && p != static_cast(mShowHiddenTabPanel) && mTabs->getTabVisibility(p);
+ };
+
+ for (S32 i = skip_idx - 1; i >= 0; --i)
+ {
+ if (selectable(mTabs->getPanelByIndex(i)))
+ {
+ mTabs->selectTab(i);
+ return;
+ }
+ }
+ for (S32 i = skip_idx + 1; i < count; ++i)
+ {
+ if (selectable(mTabs->getPanelByIndex(i)))
+ {
+ mTabs->selectTab(i);
+ return;
+ }
+ }
+ mTabs->selectFirstTab();
+}
+
void FSInventoryCustomTabs::clearCustomTabs()
{
if (!mTabs)
@@ -696,7 +926,7 @@ void FSInventoryCustomTabs::clearCustomTabs()
mLastActivePanel = nullptr;
auto* current = mTabs->getCurrentPanel();
- if (current && (current == mAddTabPanel || mPanels.count(dynamic_cast(current))))
+ if (current && (current == mAddTabPanel || current == mShowHiddenTabPanel || mPanels.count(dynamic_cast(current))))
{
mTabs->selectFirstTab();
}
@@ -705,6 +935,7 @@ void FSInventoryCustomTabs::clearCustomTabs()
mPanels.clear();
mExplicitlyNamed.clear();
mPendingRootOpen.clear();
+ mHidden.clear();
for (auto* panel : panels_copy)
{
mTabs->removeTabPanel(panel);
@@ -713,6 +944,7 @@ void FSInventoryCustomTabs::clearCustomTabs()
mLastActivePanel = nullptr;
mContextPanel = nullptr;
+ updateShowHiddenTabVisibility();
}
// ---------------------------------------------------------------------------
@@ -747,6 +979,7 @@ void FSInventoryCustomTabs::doLoad()
const LLSD tabs = gSavedPerAccountSettings.getLLSD(SETTING_KEY);
if (!tabs.isArray())
{
+ updateShowHiddenTabVisibility();
mLoadedOk = true;
return;
}
@@ -806,9 +1039,16 @@ void FSInventoryCustomTabs::doLoad()
new_filter.setSearchType(static_cast(states["search_type"].asInteger()));
}
}
+
+ if (entry.has(TABHIDDEN_KEY) && entry[TABHIDDEN_KEY].asBoolean())
+ {
+ mHidden.insert(new_panel);
+ mTabs->setTabButtonVisible(new_panel, false);
+ }
}
}
+ updateShowHiddenTabVisibility();
mLoadedOk = (added == attempted);
if (!mLoadedOk)
{
@@ -887,6 +1127,7 @@ void FSInventoryCustomTabs::save()
entry["states"] = states_sd;
}
+ entry[TABHIDDEN_KEY] = static_cast(mHidden.count(inv_panel));
array.append(entry);
}
@@ -912,7 +1153,7 @@ bool FSInventoryCustomTabs::isUnchangedSetting(const LLSD& array) const
}
for (LLSD::Integer i = 0; i < array.size(); ++i)
{
- if (current[i]["name"].asString() != array[i]["name"].asString() || current[i]["filter"].asString() != array[i]["filter"].asString() || !llsd_equals(current[i]["states"], array[i]["states"]))
+ if (current[i]["name"].asString() != array[i]["name"].asString() || current[i]["filter"].asString() != array[i]["filter"].asString() || current[i][TABHIDDEN_KEY].asBoolean() != array[i][TABHIDDEN_KEY].asBoolean() || !llsd_equals(current[i]["states"], array[i]["states"]))
{
return false;
}
@@ -952,6 +1193,11 @@ bool FSInventoryCustomTabs::isAddTab(const LLPanel* panel) const
return panel && panel == static_cast(mAddTabPanel);
}
+bool FSInventoryCustomTabs::isShowHiddenTab(const LLPanel* panel) const
+{
+ return panel && panel == static_cast(mShowHiddenTabPanel);
+}
+
LLButton* FSInventoryCustomTabs::findTabButton(const LLPanel* panel) const
{
if (!mTabs || !panel)
diff --git a/indra/newview/fsinventorycustomtabs.h b/indra/newview/fsinventorycustomtabs.h
index 580c9f7a1c..5822b82ce3 100644
--- a/indra/newview/fsinventorycustomtabs.h
+++ b/indra/newview/fsinventorycustomtabs.h
@@ -34,6 +34,7 @@
#include
class LLButton;
+class LLContextMenu;
class LLFilterEditor;
class LLInventoryPanel;
class LLPanel;
@@ -61,12 +62,15 @@ public:
bool isCustomTab(const LLPanel* panel) const;
bool isAddTab(const LLPanel* panel) const;
+ bool isShowHiddenTab(const LLPanel* panel) const;
void onAddTabSelected();
void onAddClicked();
+ void onShowHiddenClicked();
void onRenameClicked();
void onCloneClicked();
void onCloseClicked();
+ void onHideClicked();
static void install(LLPanelMainInventory* parent, LLFilterEditor* filter_editor);
static void notifyFilterStateChanged(LLPanelMainInventory* parent);
@@ -89,6 +93,11 @@ private:
bool isUnchangedSetting(const LLSD& array) const;
void clearCustomTabs();
void installAddTab();
+ void installShowHiddenTab();
+ void hideTab(LLInventoryPanel* panel);
+ void showTab(LLInventoryPanel* panel);
+ void updateShowHiddenTabVisibility();
+ void selectNearestVisibleTab(LLInventoryPanel* skip_panel);
void onSettingChangedExternally();
void onRenameConfirmed(const LLSD& notification, const LLSD& response);
void onCloseConfirmed(const LLSD& notification, const LLSD& response);
@@ -110,16 +119,20 @@ private:
LLPanelMainInventory* mParent{ nullptr };
LLTabContainer* mTabs{ nullptr };
LLHandle mMenuHandle;
+ LLHandle mHiddenMenuHandle;
std::set mPanels;
std::unordered_set mExplicitlyNamed;
std::unordered_set mPendingRootOpen;
+ std::unordered_set mHidden;
LLInventoryPanel* mContextPanel{ nullptr };
LLInventoryPanel* mAddTabPanel{ nullptr };
+ LLInventoryPanel* mShowHiddenTabPanel{ nullptr };
LLInventoryPanel* mLastActivePanel{ nullptr };
LLNotificationPtr mRenameNotification;
LLNotificationPtr mCloseNotification;
boost::signals2::connection mSettingConnection;
boost::signals2::connection mAddClickConnection;
+ boost::signals2::connection mShowHiddenClickConnection;
bool mSaving{ false };
bool mLoading{ false };
bool mLoadedOk{ false };
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_custom_tab.xml b/indra/newview/skins/default/xui/en/menu_inventory_custom_tab.xml
index e5a3928951..2b2fab56a4 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory_custom_tab.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory_custom_tab.xml
@@ -17,6 +17,12 @@
+
+
+
Didn't find what you're looking for? Try [secondlife:///app/inventory/filters Show filters].
You haven't marked any items as favorites.
Custom
+ Show hidden tabs
To add a place to your landmarks, click the star to the right of the location name.
Drag a landmark here to add it to your favorites.
You have no listings yet.
diff --git a/indra/newview/skins/default/xui/pl/menu_inventory_custom_tab.xml b/indra/newview/skins/default/xui/pl/menu_inventory_custom_tab.xml
index d96c322bb6..b3df7d6d3b 100644
--- a/indra/newview/skins/default/xui/pl/menu_inventory_custom_tab.xml
+++ b/indra/newview/skins/default/xui/pl/menu_inventory_custom_tab.xml
@@ -2,5 +2,6 @@
diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml
index 472a8251ff..7d49640125 100644
--- a/indra/newview/skins/default/xui/pl/strings.xml
+++ b/indra/newview/skins/default/xui/pl/strings.xml
@@ -1704,6 +1704,9 @@ https://www.firestormviewer.org/support aby uzyskać pomoc.
Własny
+
+ Pokaż ukryte zakładki
+
Aby dodać miejsce do landmarków, kliknij gwiazdkę po prawej stronie nazwy lokalizacji.