Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions indra/llui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ target_sources(llui
llfloaterreglistener.cpp
llflyoutbutton.cpp
llfocusmgr.cpp
llgestureautocompletehelper.cpp
llfolderview.cpp
llfolderviewitem.cpp
llfolderviewmodel.cpp
Expand Down Expand Up @@ -147,6 +148,7 @@ target_sources(llui
llfloaterreglistener.h
llflyoutbutton.h
llfocusmgr.h
llgestureautocompletehelper.h
llfolderview.h
llfolderviewitem.h
llfolderviewmodel.h
Expand Down
152 changes: 152 additions & 0 deletions indra/llui/llgestureautocompletehelper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* @file llgestureautocompletehelper.cpp
*
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2026, Linden Research, 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
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/

#include "linden_common.h"

#include "llgestureautocompletehelper.h"

#include "llfloater.h"
#include "llfloaterreg.h"
#include "lluictrl.h"

constexpr char GESTURE_AUTOCOMPLETE_FLOATER[] = "gesture_autocomplete_picker";

bool LLGestureAutocompleteHelper::isActive(const LLUICtrl* ctrl) const
{
return mHostHandle.get() == ctrl;
}

void LLGestureAutocompleteHelper::showHelper(
LLUICtrl* host_ctrl,
const std::vector<Row>& rows,
size_t total,
const std::string& empty_text,
std::function<void(std::string)> commit_cb)
{
if (mHelperHandle.isDead())
{
LLFloater* helper_floater = LLFloaterReg::getInstance(GESTURE_AUTOCOMPLETE_FLOATER);
mHelperHandle = helper_floater->getHandle();
mHelperCommitConn = helper_floater->setCommitCallback(
[this](LLUICtrl*, const LLSD& param) { onCommitGesture(param.asString()); });
}

setHostCtrl(host_ctrl);
mRows = rows;
mTotal = total;
mEmptyText = empty_text;
mGestureCommitCb = commit_cb;

S32 floater_x, floater_y;
LLRect host_rect = host_ctrl->getRect();
if (!host_ctrl->localPointToOtherView(0, host_rect.getHeight(), &floater_x, &floater_y, gFloaterView))
{
LL_WARNS() << "Cannot show gesture autocomplete helper for non-floater controls." << LL_ENDL;
return;
}

LLFloater* helper_floater = mHelperHandle.get();
LLRect rect = helper_floater->getRect();
rect.setLeftTopAndSize(floater_x, floater_y + rect.getHeight(), rect.getWidth(), rect.getHeight());
helper_floater->setRect(rect);

refreshPicker();
}

void LLGestureAutocompleteHelper::hideHelper(const LLUICtrl* ctrl)
{
if (ctrl && !isActive(ctrl))
{
return;
}

setHostCtrl(nullptr);
}

bool LLGestureAutocompleteHelper::handleKey(const LLUICtrl* ctrl, KEY key, MASK mask)
{
if (mHelperHandle.isDead() || !isActive(ctrl))
{
return false;
}

return mHelperHandle.get()->handleKey(key, mask, true);
}

void LLGestureAutocompleteHelper::onCommitGesture(const std::string& trigger)
{
if (!mHostHandle.isDead() && mGestureCommitCb)
{
mGestureCommitCb(trigger);
}

hideHelper(getHostCtrl());
}

void LLGestureAutocompleteHelper::refreshPicker()
{
if (mHelperHandle.isDead())
{
return;
}

LLFloater* helper_floater = mHelperHandle.get();

if (helper_floater->isShown())
{
helper_floater->onOpen(LLSD());
}
else
{
helper_floater->openFloater(LLSD());
}
}

void LLGestureAutocompleteHelper::setHostCtrl(LLUICtrl* host_ctrl)
{
const LLUICtrl* cur_host_ctrl = mHostHandle.get();

if (cur_host_ctrl != host_ctrl)
{
mHostCtrlFocusLostConn.disconnect();
mHostHandle.markDead();
mGestureCommitCb = {};
mRows.clear();
mEmptyText.clear();
mTotal = 0;

if (!mHelperHandle.isDead())
{
mHelperHandle.get()->closeFloater();
}

if (host_ctrl)
{
mHostHandle = host_ctrl->getHandle();
mHostCtrlFocusLostConn = host_ctrl->setFocusLostCallback(
[this](auto*) { hideHelper(getHostCtrl()); });
}
}
}
83 changes: 83 additions & 0 deletions indra/llui/llgestureautocompletehelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* @file llgestureautocompletehelper.h
*
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2026, Linden Research, 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
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/

#pragma once

#include "llhandle.h"
#include "llsingleton.h"

#include <boost/signals2.hpp>
#include <functional>
#include <string>
#include <vector>

class LLFloater;
class LLUICtrl;

class LLGestureAutocompleteHelper : public LLSingleton<LLGestureAutocompleteHelper>
{
LLSINGLETON(LLGestureAutocompleteHelper) {}
~LLGestureAutocompleteHelper() override {}

public:
struct Row
{
std::string value;
std::string trigger;
std::string name;
};

bool isActive(const LLUICtrl* ctrl) const;
void showHelper(
LLUICtrl* host_ctrl,
const std::vector<Row>& rows,
size_t total,
const std::string& empty_text,
std::function<void(std::string)> commit_cb);
void hideHelper(const LLUICtrl* ctrl = nullptr);
bool handleKey(const LLUICtrl* ctrl, KEY key, MASK mask);
void onCommitGesture(const std::string& trigger);

const std::vector<Row>& rows() const { return mRows; }
size_t total() const { return mTotal; }
const std::string& emptyText() const { return mEmptyText; }

protected:
void setHostCtrl(LLUICtrl* host_ctrl);
LLUICtrl* getHostCtrl() const { return mHostHandle.get(); }

private:
void refreshPicker();

LLHandle<LLUICtrl> mHostHandle;
LLHandle<LLFloater> mHelperHandle;
boost::signals2::connection mHostCtrlFocusLostConn;
boost::signals2::connection mHelperCommitConn;
std::function<void(std::string)> mGestureCommitCb;

std::vector<Row> mRows;
std::string mEmptyText;
size_t mTotal = 0;
};
4 changes: 3 additions & 1 deletion indra/llui/lltexteditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#include "lltooltip.h"
#include "llmenugl.h"
#include "llchatmentionhelper.h"
#include "llgestureautocompletehelper.h"

#include <queue>
#include "llcombobox.h"
Expand Down Expand Up @@ -2107,7 +2108,8 @@ bool LLTextEditor::handleKeyHere(KEY key, MASK mask )
if (!mReadOnly)
{
if ((mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask)) ||
(mShowChatMentionPicker && LLChatMentionHelper::instance().handleKey(this, key, mask)))
(mShowChatMentionPicker && LLChatMentionHelper::instance().handleKey(this, key, mask)) ||
LLGestureAutocompleteHelper::instance().handleKey(this, key, mask))
{
return true;
}
Expand Down
2 changes: 2 additions & 0 deletions indra/newview/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ set(viewer_SOURCE_FILES
llfloaterfonttest.cpp
llfloaterforgetuser.cpp
llfloatergesture.cpp
llfloatergestureautocompletepicker.cpp
llfloatergodtools.cpp
llfloatergotoline.cpp
llfloatergridstatus.cpp
Expand Down Expand Up @@ -1129,6 +1130,7 @@ set(viewer_HEADER_FILES
llfloaterfonttest.h
llfloaterforgetuser.h
llfloatergesture.h
llfloatergestureautocompletepicker.h
llfloatergodtools.h
llfloatergotoline.h
llfloatergridstatus.h
Expand Down
Loading