diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 8d902ce618fc0f7d2e6b981332f7bb729e84c1b8..0bb1814322af0b3944987ec99233e7996e30d5e8 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -441,6 +441,7 @@ set(viewer_SOURCE_FILES
     llpaneleditsky.cpp
     llpaneleditwater.cpp
     llpaneleditwearable.cpp
+	llpanelemojicomplete.cpp
     llpanelenvironment.cpp
     llpanelexperiencelisteditor.cpp
     llpanelexperiencelog.cpp
@@ -1072,6 +1073,7 @@ set(viewer_HEADER_FILES
     llpaneleditsky.h
     llpaneleditwater.h
     llpaneleditwearable.h
+	llpanelemojicomplete.h
     llpanelenvironment.h
     llpanelexperiencelisteditor.h
     llpanelexperiencelog.h
diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e1d80b62e2d4d4914040c3373108d4e8b9fcb1e7
--- /dev/null
+++ b/indra/newview/llpanelemojicomplete.cpp
@@ -0,0 +1,210 @@
+/**
+* @file llpanelemojicomplete.h
+* @brief Header file for LLPanelEmojiComplete
+*
+* $LicenseInfo:firstyear=2012&license=lgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2011, 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 "llviewerprecompiledheaders.h"
+
+#include "llemojidictionary.h"
+#include "llpanelemojicomplete.h"
+#include "lluictrlfactory.h"
+
+constexpr U32 MIN_MOUSE_MOVE_DELTA = 4;
+
+// ============================================================================
+// LLPanelEmojiComplete
+//
+
+static LLDefaultChildRegistry::Register<LLPanelEmojiComplete> r("emoji_complete");
+
+LLPanelEmojiComplete::Params::Params()
+	: selected_image("selected_image")
+{
+}
+
+LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p)
+	: LLUICtrl(p)
+	, mSelectedImage(p.selected_image)
+{
+	setFont(p.font);
+}
+
+LLPanelEmojiComplete::~LLPanelEmojiComplete()
+{
+}
+
+void LLPanelEmojiComplete::draw()
+{
+	if (!mEmojis.empty())
+	{
+		const S32 centerY = mRenderRect.getCenterY();
+		const size_t firstVisibleIdx = mScrollPos, lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mEmojis.size()) - 1;
+
+		if (mCurSelected >= firstVisibleIdx && mCurSelected <= lastVisibleIdx)
+		{
+			const S32 emoji_left = mRenderRect.mLeft + (mCurSelected - firstVisibleIdx) * mEmojiWidth;
+			const S32 emoji_height = mFont->getLineHeight() + mPadding;
+			mSelectedImage->draw(emoji_left, centerY - emoji_height / 2, mEmojiWidth, emoji_height);
+		}
+
+		U32 left = mRenderRect.mLeft + mPadding;
+		for (U32 curIdx = firstVisibleIdx; curIdx <= lastVisibleIdx; curIdx++)
+		{
+			mFont->render(
+				mEmojis, curIdx,
+				left, centerY,
+				LLColor4::white, LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW_SOFT,
+				1, S32_MAX, nullptr, false, true);
+			left += mEmojiWidth;
+		}
+	}
+}
+
+BOOL LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask)
+{
+	LLVector2 curHover(x, y);
+	if ((mLastHover - curHover).lengthSquared() > MIN_MOUSE_MOVE_DELTA)
+	{
+		mCurSelected = posToIndex(x, y);
+		mLastHover = curHover;
+	}
+
+	return TRUE;
+}
+
+BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+	if (MASK_NONE == mask)
+	{
+		bool handled = false;
+		switch (key)
+		{
+			case KEY_LEFT:
+			case KEY_UP:
+				selectPrevious();
+				handled = true;
+				break;
+			case KEY_RIGHT:
+			case KEY_DOWN:
+				selectNext();
+				handled = true;
+				break;
+		}
+		return handled;
+	}
+
+	return false;
+}
+
+void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+	LLUICtrl::reshape(width, height, called_from_parent);
+	updateConstraints();
+}
+
+void LLPanelEmojiComplete::setEmojiHint(const std::string& hint)
+{
+	mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint);
+	mScrollPos = llmin(mScrollPos, mEmojis.size());
+	updateConstraints();
+}
+
+size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const
+{
+	if (mRenderRect.pointInRect(x, y))
+	{
+		return llmin((size_t)x / mEmojiWidth, mEmojis.size() - 1);
+	}
+	return npos;
+}
+
+void LLPanelEmojiComplete::select(size_t emoji_idx)
+{
+	mCurSelected = llclamp<size_t>(emoji_idx, 0, mEmojis.size());
+	updateScrollPos();
+}
+
+void LLPanelEmojiComplete::selectNext()
+{
+	select(mCurSelected + 1 < mEmojis.size() ? mCurSelected + 1 : 0);
+}
+
+void LLPanelEmojiComplete::selectPrevious()
+{
+	select(mCurSelected - 1 >= 0 ? mCurSelected - 1 : mEmojis.size() - 1);
+}
+
+void LLPanelEmojiComplete::setFont(const LLFontGL* fontp)
+{
+	mFont = fontp;
+	updateConstraints();
+}
+
+void LLPanelEmojiComplete::updateConstraints()
+{
+	const S32 ctrlWidth = getLocalRect().getWidth();
+
+	mEmojiWidth = mFont->getWidthF32(u8"\U0001F431") + mPadding * 2;
+	mVisibleEmojis = ctrlWidth / mEmojiWidth;
+	mRenderRect = getLocalRect().stretch((ctrlWidth - mVisibleEmojis * mEmojiWidth) / -2, 0);
+
+	updateScrollPos();
+}
+
+void LLPanelEmojiComplete::updateScrollPos()
+{
+	const size_t cntEmoji = mEmojis.size();
+	if (0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected)
+	{
+		mScrollPos = 0;
+	}
+	else if (cntEmoji - 1 == mCurSelected)
+	{
+		mScrollPos = mCurSelected - mVisibleEmojis + 1;
+	}
+	else
+	{
+		mScrollPos = mCurSelected - ((float)mCurSelected / (cntEmoji - 2) * (mVisibleEmojis - 2));
+	}
+}
+
+// ============================================================================
+// LLFloaterEmojiComplete
+//
+
+LLFloaterEmojiComplete::LLFloaterEmojiComplete(const LLSD& sdKey)
+	: LLFloater(sdKey)
+{
+	// This floater should hover on top of our dependent (with the dependent having the focus)
+	setFocusStealsFrontmost(false);
+	setAutoFocus(false);
+	setBackgroundVisible(false);
+}
+
+void LLFloaterEmojiComplete::onOpen(const LLSD& key)
+{
+	findChild<LLPanelEmojiComplete>("emoji_complete_ctrl")->setEmojiHint(key["hint"].asString());
+}
+
+// ============================================================================
diff --git a/indra/newview/llpanelemojicomplete.h b/indra/newview/llpanelemojicomplete.h
new file mode 100644
index 0000000000000000000000000000000000000000..85b0609ae925158bc7e94af5837f6f4b32e616b7
--- /dev/null
+++ b/indra/newview/llpanelemojicomplete.h
@@ -0,0 +1,99 @@
+/**
+* @file llpanelemojicomplete.h
+* @brief Header file for LLPanelEmojiComplete
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, 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 "llfloater.h"
+#include "lluictrl.h"
+
+// ============================================================================
+// LLPanelEmojiComplete
+//
+
+class LLPanelEmojiComplete : public LLUICtrl
+{
+	friend class LLUICtrlFactory;
+public:
+	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+	{
+		Optional<LLUIImage*> selected_image;
+
+		Params();
+	};
+
+protected:
+	LLPanelEmojiComplete(const LLPanelEmojiComplete::Params&);
+public:
+	virtual ~LLPanelEmojiComplete();
+
+	void draw() override;
+	BOOL handleHover(S32 x, S32 y, MASK mask) override;
+	BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent) override;
+	void reshape(S32 width, S32 height, BOOL called_from_parent) override;
+
+public:
+	void setEmojiHint(const std::string& hint);
+protected:
+	size_t posToIndex(S32 x, S32 y) const;
+	void select(size_t emoji_idx);
+	void selectNext();
+	void selectPrevious();
+	void setFont(const LLFontGL* fontp);
+	void updateConstraints();
+	void updateScrollPos();
+
+protected:
+	static constexpr auto npos = std::numeric_limits<size_t>::max();
+
+	const LLFontGL* mFont;
+	U16             mEmojiWidth = 0;
+	LLUIImagePtr	mSelectedImage;
+
+	LLWString       mEmojis;
+	U16             mVisibleEmojis = 0;
+	size_t          mFirstVisible = 0;
+	size_t          mScrollPos = 0;
+	size_t          mCurSelected = 0;
+	LLVector2       mLastHover;
+
+	S32             mPadding = 8;
+	LLRect          mRenderRect;
+};
+
+// ============================================================================
+// LLFloaterEmojiComplete
+//
+
+class LLFloaterEmojiComplete : public LLFloater
+{
+public:
+	LLFloaterEmojiComplete(const LLSD& sdKey);
+
+public:
+	void onOpen(const LLSD& key) override;
+};
+
+// ============================================================================
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 5a05f897588f723e5f975a66eec5be9b33fd59a3..a54b91030e1c1a09baac08d23edef9070acc9df6 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -155,6 +155,7 @@
 #include "llfloaterimnearbychat.h"
 #include "llpanelblockedlist.h"
 #include "llpanelclassified.h"
+#include "llpanelemojicomplete.h"
 #include "llpreviewanim.h"
 #include "llpreviewgesture.h"
 #include "llpreviewnotecard.h"
@@ -229,6 +230,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("delete_pref_preset", "floater_delete_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDeletePrefPreset>);
 	LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>);
 
+	LLFloaterReg::add("emoji_complete", "floater_emoji_complete.xml", &LLFloaterReg::build<LLFloaterEmojiComplete>);
 	LLFloaterReg::add("env_post_process", "floater_post_process.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPostProcess>);
 
     LLFloaterReg::add("env_fixed_environmentent_water", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentWater>);
diff --git a/indra/newview/skins/default/xui/en/floater_emoji_complete.xml b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml
new file mode 100644
index 0000000000000000000000000000000000000000..eb666bc32cd46cace2e2b6537b78abb24d5b1ffa
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ can_close="false"
+ can_dock="false"
+ can_drag_on_left="false"
+ can_minimize="false"
+ can_resize="false"
+ can_tear_off="false"
+ header_height="0"
+ layout="topleft"
+ legacy_header_height="0"
+ height="40"
+ single_instance="true"
+ width="240"
+ >
+    <emoji_complete
+     height="30"
+     follows="top|left"
+     layout="topleft"
+     left="5"
+     top="5"
+     width="230"
+     name="emoji_complete_ctrl"
+     >
+    </emoji_complete>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f35105ff7e1a1f1d0694289d2279dcb8b8f80fd3
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<emoji_complete
+  font="EmojiHuge"
+  hover_image="ListItem_Over"
+  selected_image="ListItem_Select"
+  >
+</emoji_complete>