phoenix-firestorm/indra/newview/fsfloaterstreamtitle.cpp

303 lines
8.2 KiB
C++

/**
* @file fsfloaterstreamtitle.cpp
* @brief Class for the stream title and history floater
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (c) 2022 Ansariel Hiller @ Second Life
*
* 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 "fsfloaterstreamtitle.h"
#include "fsscrolllistctrl.h"
#include "llaudioengine.h"
#include "llbutton.h"
#include "llfloaterreg.h"
#include "lltextbox.h"
#include "llviewercontrol.h"
static constexpr size_t MAX_HISTORY_ENTRIES{ 10 };
static constexpr F32 SCROLL_STEP_DELAY{ 0.25f };
static constexpr F32 SCROLL_END_DELAY{ 4.f };
FSStreamTitleManager::~FSStreamTitleManager()
{
if (mMetadataUpdateConnection.connected())
{
mMetadataUpdateConnection.disconnect();
}
}
void FSStreamTitleManager::initSingleton()
{
if (!gAudiop || !gAudiop->getStreamingAudioImpl())
{
return;
}
mMetadataUpdateConnection = gAudiop->getStreamingAudioImpl()->setMetadataUpdateCallback(std::bind(&FSStreamTitleManager::processMetadataUpdate, this, std::placeholders::_1));
processMetadataUpdate(gAudiop->getStreamingAudioImpl()->getCurrentMetadata());
}
void FSStreamTitleManager::processMetadataUpdate(const LLSD& metadata) noexcept
{
std::string chat{};
if (metadata.has("ARTIST"))
{
chat = metadata["ARTIST"].asString();
}
if (metadata.has("TITLE"))
{
if (chat.length() > 0)
{
chat.append(" - ");
}
chat.append(metadata["TITLE"].asString());
}
if (chat != mCurrentStreamTitle)
{
mCurrentStreamTitle = std::move(chat);
if (!mCurrentStreamTitle.empty() && (mStreamTitleHistory.empty() || mStreamTitleHistory.back() != mCurrentStreamTitle))
{
mStreamTitleHistory.emplace_back(mCurrentStreamTitle);
if (mStreamTitleHistory.size() > MAX_HISTORY_ENTRIES)
{
mStreamTitleHistory.erase(mStreamTitleHistory.begin());
}
mHistoryUpdateSignal(mStreamTitleHistory);
}
mUpdateSignal(mCurrentStreamTitle);
}
}
//////////////////////////////////////////////////////////////////////////
/// FSFloaterStreamTitleHistory
//////////////////////////////////////////////////////////////////////////
FSFloaterStreamTitleHistory::FSFloaterStreamTitleHistory(const LLSD& key)
: LLFloater(key)
{
}
FSFloaterStreamTitleHistory::~FSFloaterStreamTitleHistory()
{
if (mUpdateConnection.connected())
{
mUpdateConnection.disconnect();
}
}
bool FSFloaterStreamTitleHistory::postBuild()
{
mHistoryCtrl = findChild<FSScrollListCtrl>("history");
FSStreamTitleManager& instance = FSStreamTitleManager::instance();
mUpdateConnection = instance.setHistoryUpdateCallback([this](const FSStreamTitleManager::history_vec_t& history) { updateHistory(history); });
updateHistory(instance.getStreamTitleHistory());
return true;
}
void FSFloaterStreamTitleHistory::updateHistory(const FSStreamTitleManager::history_vec_t& history)
{
mHistoryCtrl->clearRows();
for (const auto& entry : history)
{
LLSD data;
data["columns"][0]["name"] = "title";
data["columns"][0]["value"] = entry;
mHistoryCtrl->addElement(data, ADD_TOP);
}
}
void FSFloaterStreamTitleHistory::setOwnerOrigin(LLView* owner) noexcept
{
mOwner = owner->getHandle();
}
void FSFloaterStreamTitleHistory::draw()
{
LLFloater::draw();
if (mOwner.get())
{
static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
drawConeToOwner(mContextConeOpacity, max_opacity, mOwner.get());
}
}
//////////////////////////////////////////////////////////////////////////
/// FSFloaterStreamTitle
//////////////////////////////////////////////////////////////////////////
FSFloaterStreamTitle::FSFloaterStreamTitle(const LLSD& key)
: LLFloater(key), LLEventTimer(SCROLL_STEP_DELAY)
{
mEventTimer.stop();
}
FSFloaterStreamTitle::~FSFloaterStreamTitle()
{
if (mUpdateConnection.connected())
{
mUpdateConnection.disconnect();
}
}
bool FSFloaterStreamTitle::postBuild()
{
mTitletext = findChild<LLTextBox>("streamtitle");
mHistoryBtn = findChild<LLButton>("btn_history");
FSStreamTitleManager& instance = FSStreamTitleManager::instance();
mUpdateConnection = instance.setUpdateCallback([this](std::string_view streamtitle) { updateStreamTitle(streamtitle); });
updateStreamTitle(instance.getCurrentStreamTitle());
mHistoryBtn->setCommitCallback(std::bind(&FSFloaterStreamTitle::toggleHistory, this));
mHistoryBtn->setIsToggledCallback([](LLUICtrl*, const LLSD&) { return LLFloaterReg::instanceVisible("fs_streamtitlehistory"); });
setVisibleCallback(boost::bind(&FSFloaterStreamTitle::closeHistory, this));
return true;
}
void FSFloaterStreamTitle::toggleHistory() noexcept
{
LLFloater* root_floater = gFloaterView->getParentFloater(this);
FSFloaterStreamTitleHistory* history_floater = LLFloaterReg::findTypedInstance<FSFloaterStreamTitleHistory>("fs_streamtitlehistory");
if (!history_floater)
{
history_floater = LLFloaterReg::showTypedInstance<FSFloaterStreamTitleHistory>("fs_streamtitlehistory");
if (root_floater && history_floater)
{
root_floater->addDependentFloater(history_floater);
history_floater->setOwnerOrigin(root_floater);
mHistory = history_floater->getHandle();
}
}
else
{
closeHistory();
}
}
void FSFloaterStreamTitle::closeHistory() noexcept
{
if (mHistory.get())
{
mHistory.get()->closeFloater();
}
}
void FSFloaterStreamTitle::updateStreamTitle(std::string_view streamtitle) noexcept
{
if (streamtitle.empty())
{
mTitletext->setText(getString("NoStream"));
}
else
{
mTitletext->setText(static_cast<std::string>(streamtitle));
}
mCurrentTitle = mTitletext->getText();
mCurrentDrawnTitle = mCurrentTitle;
mTitletext->setToolTip(mCurrentTitle);
checkTitleWidth();
}
void FSFloaterStreamTitle::reshape(S32 width, S32 height, bool called_from_parent)
{
LLFloater::reshape(width, height, called_from_parent);
checkTitleWidth();
}
void FSFloaterStreamTitle::checkTitleWidth() noexcept
{
if (!mTitletext)
{
return;
}
S32 text_width = mTitletext->getFont()->getWidth(mCurrentTitle);
S32 textbox_width = mTitletext->getRect().getWidth();
if (text_width > textbox_width)
{
mResetTitle = false;
mEventTimer.start();
}
else
{
mEventTimer.stop();
mTitletext->setText(mCurrentTitle);
mCurrentDrawnTitle = mCurrentTitle;
}
}
bool FSFloaterStreamTitle::tick()
{
if (mResetTitle)
{
mPeriod = SCROLL_END_DELAY;
mEventTimer.reset();
mCurrentDrawnTitle = mCurrentTitle;
mResetTitle = false;
}
else
{
mPeriod = SCROLL_STEP_DELAY;
mEventTimer.reset();
mCurrentDrawnTitle.erase(mCurrentDrawnTitle.begin());
}
mTitletext->setText(mCurrentDrawnTitle);
S32 text_width = mTitletext->getFont()->getWidth(mCurrentDrawnTitle);
S32 textbox_width = mTitletext->getRect().getWidth();
if (textbox_width > text_width)
{
mPeriod = SCROLL_END_DELAY;
mEventTimer.reset();
mResetTitle = true;
}
return false;
}