diff options
17 files changed, 1633 insertions, 584 deletions
diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp index 179c5d25bf..cdcf5a93d6 100644 --- a/indra/llui/llemojidictionary.cpp +++ b/indra/llui/llemojidictionary.cpp @@ -38,7 +38,12 @@  // Constants  // -constexpr char SKINNED_EMOJI_FILENAME[] = "emoji_characters.xml"; +static const std::string SKINNED_EMOJI_FILENAME("emoji_characters.xml"); +static const std::string SKINNED_CATEGORY_FILENAME("emoji_categories.xml"); +static const std::string COMMON_GROUP_FILENAME("emoji_groups.xml"); +static const std::string GROUP_NAME_ALL("all"); +static const std::string GROUP_NAME_OTHERS("others"); +static const std::string GROUP_NAME_SKIP("skip");  // ============================================================================  // Helper functions @@ -50,82 +55,68 @@ std::list<T> llsd_array_to_list(const LLSD& sd, std::function<void(T&)> mutator  template<>  std::list<std::string> llsd_array_to_list(const LLSD& sd, std::function<void(std::string&)> mutator)  { -	std::list<std::string> result; -	for (LLSD::array_const_iterator it = sd.beginArray(), end = sd.endArray(); it != end; ++it) -	{ -		const LLSD& entry = *it; -		if (!entry.isString()) -			continue; - -		result.push_back(entry.asStringRef()); -		if (mutator) -		{ -			mutator(result.back()); -		} -	} -	return result; -} - -LLEmojiDescriptor::LLEmojiDescriptor(const LLSD& descriptor_sd) -{ -	Name = descriptor_sd["Name"].asStringRef(); - -	const LLWString emoji_string = utf8str_to_wstring(descriptor_sd["Character"].asString()); -	Character = (1 == emoji_string.size()) ? emoji_string[0] : L'\0'; // We don't currently support character composition - -	auto toLower = [](std::string& str) { LLStringUtil::toLower(str); }; -	ShortCodes = llsd_array_to_list<std::string>(descriptor_sd["ShortCodes"], toLower); -	Categories = llsd_array_to_list<std::string>(descriptor_sd["Categories"], toLower); - -	if (Name.empty()) -	{ -		Name = ShortCodes.front(); -	} -} - -bool LLEmojiDescriptor::isValid() const -{ -	return -		Character && -		!ShortCodes.empty() && -		!Categories.empty(); +    std::list<std::string> result; +    for (LLSD::array_const_iterator it = sd.beginArray(), end = sd.endArray(); it != end; ++it) +    { +        const LLSD& entry = *it; +        if (!entry.isString()) +            continue; + +        result.push_back(entry.asStringRef()); +        if (mutator) +        { +            mutator(result.back()); +        } +    } +    return result;  }  struct emoji_filter_base  { -	emoji_filter_base(const std::string& needle) -	{ -		// Search without the colon (if present) so the user can type ':food' and see all emojis in the 'Food' category -		mNeedle = (boost::starts_with(needle, ":")) ? needle.substr(1) : needle; -		LLStringUtil::toLower(mNeedle); -	} +    emoji_filter_base(const std::string& needle) +    { +        // Search without the colon (if present) so the user can type ':food' and see all emojis in the 'Food' category +        mNeedle = (boost::starts_with(needle, ":")) ? needle.substr(1) : needle; +        LLStringUtil::toLower(mNeedle); +    }  protected: -	std::string mNeedle; +    std::string mNeedle;  };  struct emoji_filter_shortcode_or_category_contains : public emoji_filter_base  { -	emoji_filter_shortcode_or_category_contains(const std::string& needle) : emoji_filter_base(needle) {} +    emoji_filter_shortcode_or_category_contains(const std::string& needle) : emoji_filter_base(needle) {} -	bool operator()(const LLEmojiDescriptor& descr) const -	{ -		for (const auto& short_code : descr.ShortCodes) -		{ -			if (boost::icontains(short_code, mNeedle)) -				return true; -		} +    bool operator()(const LLEmojiDescriptor& descr) const +    { +        for (const auto& short_code : descr.ShortCodes) +        { +            if (boost::icontains(short_code, mNeedle)) +                return true; +        } -		for (const auto& category : descr.Categories) -		{ -			if (boost::icontains(category, mNeedle)) -				return true; -		} +        if (boost::icontains(descr.Category, mNeedle)) +            return true; -		return false; -	} +        return false; +    }  }; +std::string LLEmojiDescriptor::getShortCodes() const +{ +    std::string result; +    for (const std::string& shortCode : ShortCodes) +    { +        if (!result.empty()) +        { +            result += ", "; +        } +        result += shortCode; +    } +    return result; +} +  // ============================================================================  // LLEmojiDictionary class  // @@ -137,91 +128,278 @@ LLEmojiDictionary::LLEmojiDictionary()  // static  void LLEmojiDictionary::initClass()  { -	LLEmojiDictionary* pThis = &LLEmojiDictionary::initParamSingleton(); - -	LLSD data; - -	auto filenames = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_EMOJI_FILENAME, LLDir::CURRENT_SKIN); -	if (filenames.empty()) -	{ -		LL_WARNS() << "Emoji file characters not found" << LL_ENDL; -		return; -	} -	const std::string filename = filenames.back(); -	llifstream file(filename.c_str()); -	if (file.is_open()) -	{ -		LL_DEBUGS() << "Loading emoji characters file at " << filename << LL_ENDL; -		LLSDSerialize::fromXML(data, file); -	} - -	if (data.isUndefined()) -	{ -		LL_WARNS() << "Emoji file characters missing or ill-formed" << LL_ENDL; -		return; -	} - -	for (LLSD::array_const_iterator descriptor_it = data.beginArray(), descriptor_end = data.endArray(); descriptor_it != descriptor_end; ++descriptor_it) -	{ -		LLEmojiDescriptor descriptor(*descriptor_it); -		if (!descriptor.isValid()) -		{ -			LL_WARNS() << "Skipping invalid emoji descriptor " << descriptor.Character << LL_ENDL; -			continue; -		} -		pThis->addEmoji(std::move(descriptor)); -	} +    LLEmojiDictionary* pThis = &LLEmojiDictionary::initParamSingleton(); + +    pThis->loadTranslations(); +    pThis->loadGroups(); +    pThis->loadEmojis();  }  LLWString LLEmojiDictionary::findMatchingEmojis(const std::string& needle) const  { -	LLWString result; -	boost::transform(mEmojis | boost::adaptors::filtered(emoji_filter_shortcode_or_category_contains(needle)), -		             std::back_inserter(result), [](const auto& descr) { return descr.Character; }); -	return result; +    LLWString result; +    boost::transform(mEmojis | boost::adaptors::filtered(emoji_filter_shortcode_or_category_contains(needle)), +                     std::back_inserter(result), [](const auto& descr) { return descr.Character; }); +    return result;  }  const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromEmoji(llwchar emoji) const  { -	const auto it = mEmoji2Descr.find(emoji); -	return (mEmoji2Descr.end() != it) ? it->second : nullptr; +    const auto it = mEmoji2Descr.find(emoji); +    return (mEmoji2Descr.end() != it) ? it->second : nullptr;  }  const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromShortCode(const std::string& short_code) const  { -	const auto it = mShortCode2Descr.find(short_code); -	return (mShortCode2Descr.end() != it) ? it->second : nullptr; +    const auto it = mShortCode2Descr.find(short_code); +    return (mShortCode2Descr.end() != it) ? it->second : nullptr;  }  std::string LLEmojiDictionary::getNameFromEmoji(llwchar ch) const  { -	const auto it = mEmoji2Descr.find(ch); -	return (mEmoji2Descr.end() != it) ? it->second->Name : LLStringUtil::null; +    const auto it = mEmoji2Descr.find(ch); +    return (mEmoji2Descr.end() != it) ? it->second->ShortCodes.front() : LLStringUtil::null;  }  bool LLEmojiDictionary::isEmoji(llwchar ch) const  { -	// Currently used codes: A9,AE,203C,2049,2122,...,2B55,3030,303D,3297,3299,1F004,...,1FAF6 -	if (ch == 0xA9 || ch == 0xAE || (ch >= 0x2000 && ch < 0x3300) || (ch >= 0x1F000 && ch < 0x20000)) -	{ -		return mEmoji2Descr.find(ch) != mEmoji2Descr.end(); -	} +    // Currently used codes: A9,AE,203C,2049,2122,...,2B55,3030,303D,3297,3299,1F004,...,1FAF6 +    if (ch == 0xA9 || ch == 0xAE || (ch >= 0x2000 && ch < 0x3300) || (ch >= 0x1F000 && ch < 0x20000)) +    { +        return mEmoji2Descr.find(ch) != mEmoji2Descr.end(); +    } -	return false; +    return false; +} + +void LLEmojiDictionary::loadTranslations() +{ +    std::vector<std::string> filenames = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_CATEGORY_FILENAME, LLDir::CURRENT_SKIN); +    if (filenames.empty()) +    { +        LL_WARNS() << "Emoji file categories not found" << LL_ENDL; +        return; +    } + +    const std::string filename = filenames.back(); +    llifstream file(filename.c_str()); +    if (!file.is_open()) +    { +        LL_WARNS() << "Emoji file categories failed to open" << LL_ENDL; +        return; +    } + +    LL_DEBUGS() << "Loading emoji categories file at " << filename << LL_ENDL; + +    LLSD data; +    LLSDSerialize::fromXML(data, file); +    if (data.isUndefined()) +    { +        LL_WARNS() << "Emoji file categories missing or ill-formed" << LL_ENDL; +        return; +    } + +    // Register translations for all categories +    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it) +    { +        const LLSD& sd = *it; +        const std::string& name = sd["Name"].asStringRef(); +        const std::string& category = sd["Category"].asStringRef(); +        if (!name.empty() && !category.empty()) +        { +            mTranslations[name] = category; +        } +        else +        { +            LL_WARNS() << "Skipping invalid emoji category '" << name << "' => '" << category << "'" << LL_ENDL; +        } +    } +} + +void LLEmojiDictionary::loadGroups() +{ +    const std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, COMMON_GROUP_FILENAME); +    llifstream file(filename.c_str()); +    if (!file.is_open()) +    { +        LL_WARNS() << "Emoji file groups failed to open" << LL_ENDL; +        return; +    } + +    LL_DEBUGS() << "Loading emoji groups file at " << filename << LL_ENDL; + +    LLSD data; +    LLSDSerialize::fromXML(data, file); +    if (data.isUndefined()) +    { +        LL_WARNS() << "Emoji file groups missing or ill-formed" << LL_ENDL; +        return; +    } + +    mGroups.clear(); +    // Add group "all" +    mGroups.emplace_back(); +    // https://www.compart.com/en/unicode/U+1F50D +    mGroups.front().Character = 0x1F50D; +    // https://www.compart.com/en/unicode/U+1F302 +    llwchar iconOthers = 0x1F302; + +    // Register all groups +    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it) +    { +        const LLSD& sd = *it; +        const std::string& name = sd["Name"].asStringRef(); +        if (name == GROUP_NAME_ALL) +        { +            mGroups.front().Character = loadIcon(sd); +        } +        else if (name == GROUP_NAME_OTHERS) +        { +            iconOthers = loadIcon(sd); +        } +        else if (name == GROUP_NAME_SKIP) +        { +            mSkipCategories = loadCategories(sd); +            translateCategories(mSkipCategories); +        } +        else +        { +            // Add new group +            mGroups.emplace_back(); +            LLEmojiGroup& group = mGroups.back(); +            group.Character = loadIcon(sd); +            group.Categories = loadCategories(sd); +            translateCategories(group.Categories); + +            for (const std::string& category : group.Categories) +            { +                mCategory2Group.insert(std::make_pair(category, &group)); +            } +        } +    } + +    // Add group "others" +    mGroups.emplace_back(); +    mGroups.back().Character = iconOthers; +} + +void LLEmojiDictionary::loadEmojis() +{ +    std::vector<std::string> filenames = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_EMOJI_FILENAME, LLDir::CURRENT_SKIN); +    if (filenames.empty()) +    { +        LL_WARNS() << "Emoji file characters not found" << LL_ENDL; +        return; +    } + +    const std::string filename = filenames.back(); +    llifstream file(filename.c_str()); +    if (!file.is_open()) +    { +        LL_WARNS() << "Emoji file characters failed to open" << LL_ENDL; +        return; +    } + +    LL_DEBUGS() << "Loading emoji characters file at " << filename << LL_ENDL; + +    LLSD data; +    LLSDSerialize::fromXML(data, file); +    if (data.isUndefined()) +    { +        LL_WARNS() << "Emoji file characters missing or ill-formed" << LL_ENDL; +        return; +    } + +    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it) +    { +        const LLSD& sd = *it; + +        llwchar icon = loadIcon(sd); +        if (!icon) +        { +            LL_WARNS() << "Skipping invalid emoji descriptor (no icon)" << LL_ENDL; +            continue; +        } + +        std::list<std::string> categories = loadCategories(sd); +        if (categories.empty()) +        { +            LL_WARNS() << "Skipping invalid emoji descriptor (no categories)" << LL_ENDL; +            continue; +        } + +        std::string category = categories.front(); + +        if (std::find(mSkipCategories.begin(), mSkipCategories.end(), category) != mSkipCategories.end()) +        { +            // This category is listed for skip +            continue; +        } + +        std::list<std::string> shortCodes = loadShortCodes(sd); +        if (shortCodes.empty()) +        { +            LL_WARNS() << "Skipping invalid emoji descriptor (no shortCodes)" << LL_ENDL; +            continue; +        } + +        if (mCategory2Group.find(category) == mCategory2Group.end()) +        { +            // Add unknown category to "others" group +            mGroups.back().Categories.push_back(category); +            mCategory2Group.insert(std::make_pair(category, &mGroups.back())); +        } + +        mEmojis.emplace_back(); +        LLEmojiDescriptor& emoji = mEmojis.back(); +        emoji.Character = icon; +        emoji.Category = category; +        emoji.ShortCodes = std::move(shortCodes); + +        mEmoji2Descr.insert(std::make_pair(icon, &emoji)); +        mCategory2Descrs[category].push_back(&emoji); +        for (const std::string& shortCode : emoji.ShortCodes) +        { +            mShortCode2Descr.insert(std::make_pair(shortCode, &emoji)); +        } +    } +} + +llwchar LLEmojiDictionary::loadIcon(const LLSD& sd) +{ +    // We don't currently support character composition +    const LLWString icon = utf8str_to_wstring(sd["Character"].asString()); +    return (1 == icon.size()) ? icon[0] : L'\0'; +} + +static inline std::list<std::string> loadStrings(const LLSD& sd, const std::string key) +{ +    auto toLower = [](std::string& str) { LLStringUtil::toLower(str); }; +    return llsd_array_to_list<std::string>(sd[key], toLower); +} + +std::list<std::string> LLEmojiDictionary::loadCategories(const LLSD& sd) +{ +    static const std::string categoriesKey("Categories"); +    return loadStrings(sd, categoriesKey); +} + +std::list<std::string> LLEmojiDictionary::loadShortCodes(const LLSD& sd) +{ +    static const std::string shortCodesKey("ShortCodes"); +    return loadStrings(sd, shortCodesKey);  } -void LLEmojiDictionary::addEmoji(LLEmojiDescriptor&& descr) +void LLEmojiDictionary::translateCategories(std::list<std::string>& categories)  { -	mEmojis.push_back(descr); -	mEmoji2Descr.insert(std::make_pair(descr.Character, &mEmojis.back())); -	for (const std::string& shortCode : descr.ShortCodes) -	{ -		mShortCode2Descr.insert(std::make_pair(shortCode, &mEmojis.back())); -	} -	for (const std::string& category : descr.Categories) -	{ -		mCategory2Descrs[category].push_back(&mEmojis.back()); -	} +    for (std::string& category : categories) +    { +        auto it = mTranslations.find(category); +        if (it != mTranslations.end()) +        { +            category = it->second; +        } +    }  }  // ============================================================================ diff --git a/indra/llui/llemojidictionary.h b/indra/llui/llemojidictionary.h index 1507ebfad3..f6442684a7 100644 --- a/indra/llui/llemojidictionary.h +++ b/indra/llui/llemojidictionary.h @@ -36,14 +36,20 @@  struct LLEmojiDescriptor  { -	LLEmojiDescriptor(const LLSD& descriptor_sd); +    llwchar Character; +    std::string Category; +    std::list<std::string> ShortCodes; +    std::string getShortCodes() const; +}; -	bool isValid() const; +// ============================================================================ +// LLEmojiGroup class +// -	std::string            Name; -	llwchar                Character; -	std::list<std::string> ShortCodes; -	std::list<std::string> Categories; +struct LLEmojiGroup +{ +    llwchar Character; +    std::list<std::string> Categories;  };  // ============================================================================ @@ -52,36 +58,48 @@ struct LLEmojiDescriptor  class LLEmojiDictionary : public LLParamSingleton<LLEmojiDictionary>, public LLInitClass<LLEmojiDictionary>  { -	LLSINGLETON(LLEmojiDictionary); -	~LLEmojiDictionary() override {}; +    LLSINGLETON(LLEmojiDictionary); +    ~LLEmojiDictionary() override {};  public: -	typedef std::map<llwchar, const LLEmojiDescriptor*> emoji2descr_map_t; -	typedef emoji2descr_map_t::value_type emoji2descr_item_t; -	typedef std::map<std::string, const LLEmojiDescriptor*> code2descr_map_t; -	typedef code2descr_map_t::value_type code2descr_item_t; -	typedef std::map<std::string, std::vector<const LLEmojiDescriptor*>> cat2descrs_map_t; -	typedef cat2descrs_map_t::value_type cat2descrs_item_t; - -	static void initClass(); -	LLWString   findMatchingEmojis(const std::string& needle) const; -	const LLEmojiDescriptor* getDescriptorFromEmoji(llwchar emoji) const; -	const LLEmojiDescriptor* getDescriptorFromShortCode(const std::string& short_code) const; -	std::string getNameFromEmoji(llwchar ch) const; -	bool isEmoji(llwchar ch) const; - -	const emoji2descr_map_t& getEmoji2Descr() const { return mEmoji2Descr; } -	const code2descr_map_t& getShortCode2Descr() const { return mShortCode2Descr; } -	const cat2descrs_map_t& getCategory2Descrs() const { return mCategory2Descrs; } +    typedef std::map<std::string, std::string> cat2cat_map_t; +    typedef std::map<std::string, const LLEmojiGroup*> cat2group_map_t; +    typedef std::map<llwchar, const LLEmojiDescriptor*> emoji2descr_map_t; +    typedef std::map<std::string, const LLEmojiDescriptor*> code2descr_map_t; +    typedef std::map<std::string, std::vector<const LLEmojiDescriptor*>> cat2descrs_map_t; + +    static void initClass(); +    LLWString findMatchingEmojis(const std::string& needle) const; +    const LLEmojiDescriptor* getDescriptorFromEmoji(llwchar emoji) const; +    const LLEmojiDescriptor* getDescriptorFromShortCode(const std::string& short_code) const; +    std::string getNameFromEmoji(llwchar ch) const; +    bool isEmoji(llwchar ch) const; + +    const std::vector<LLEmojiGroup>& getGroups() const { return mGroups; } +    const emoji2descr_map_t& getEmoji2Descr() const { return mEmoji2Descr; } +    const cat2descrs_map_t& getCategory2Descrs() const { return mCategory2Descrs; } +    const code2descr_map_t& getShortCode2Descr() const { return mShortCode2Descr; }  private: -	void addEmoji(LLEmojiDescriptor&& descr); +    void loadTranslations(); +    void loadGroups(); +    void loadEmojis(); + +    static llwchar loadIcon(const LLSD& sd); +    static std::list<std::string> loadCategories(const LLSD& sd); +    static std::list<std::string> loadShortCodes(const LLSD& sd); +    void translateCategories(std::list<std::string>& categories);  private: -	std::list<LLEmojiDescriptor> mEmojis; -	emoji2descr_map_t mEmoji2Descr; -	code2descr_map_t mShortCode2Descr; -	cat2descrs_map_t mCategory2Descrs; +    std::vector<LLEmojiGroup> mGroups; +    std::list<LLEmojiDescriptor> mEmojis; +    std::list<std::string> mSkipCategories; + +    cat2cat_map_t mTranslations; +    cat2group_map_t mCategory2Group; +    emoji2descr_map_t mEmoji2Descr; +    cat2descrs_map_t mCategory2Descrs; +    code2descr_map_t mShortCode2Descr;  };  // ============================================================================ diff --git a/indra/newview/app_settings/emoji_groups.xml b/indra/newview/app_settings/emoji_groups.xml new file mode 100644 index 0000000000..b433927f91 --- /dev/null +++ b/indra/newview/app_settings/emoji_groups.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>all</string> +      <key>Character</key> +      <string>🔍</string> +    </map> +    <map> +      <key>Character</key> +      <string>😀</string> +      <key>Categories</key> +      <array> +        <string>smileys and emotion</string> +        <string>people and body</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>🥬</string> +      <key>Categories</key> +      <array> +        <string>animals and nature</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>🍔</string> +      <key>Categories</key> +      <array> +        <string>food and drink</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>🛩</string> +      <key>Categories</key> +      <array> +        <string>travel and places</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>🏈</string> +      <key>Categories</key> +      <array> +        <string>activities</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>💡</string> +      <key>Categories</key> +      <array> +        <string>objects</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>⚠</string> +      <key>Categories</key> +      <array> +        <string>symbols</string> +      </array> +    </map> +    <map> +      <key>Name</key> +      <string>others</string> +      <key>Character</key> +      <string>🌂</string> +    </map> +    <map> +      <key>Name</key> +      <string>skip</string> +      <key>Categories</key> +      <array> +        <string>components</string> +      </array> +    </map> +  </array> +</llsd> diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp index f63062f03a..9194a49c45 100644 --- a/indra/newview/llfloateremojipicker.cpp +++ b/indra/newview/llfloateremojipicker.cpp @@ -27,6 +27,7 @@  #include "llfloateremojipicker.h" +#include "llbutton.h"  #include "llcombobox.h"  #include "llemojidictionary.h"  #include "llfloaterreg.h" @@ -39,179 +40,179 @@  #include "lltextbox.h"   #include "llviewerchat.h"  -std::string LLFloaterEmojiPicker::mSelectedCategory; -std::string LLFloaterEmojiPicker::mSearchPattern; +size_t LLFloaterEmojiPicker::sSelectedGroupIndex; +std::string LLFloaterEmojiPicker::sSearchPattern;  class LLEmojiScrollListItem : public LLScrollListItem  {  public: -	LLEmojiScrollListItem(const llwchar emoji, const LLScrollListItem::Params& params) -		: LLScrollListItem(params) -		, mEmoji(emoji) -	{ -	} - -	llwchar getEmoji() const { return mEmoji; } - -	virtual void draw(const LLRect& rect, -		const LLColor4& fg_color, -		const LLColor4& hover_color, // highlight/hover selection of whole item or cell -		const LLColor4& select_color, // highlight/hover selection of whole item or cell -		const LLColor4& highlight_color, // highlights contents of cells (ex: text) -		S32 column_padding) override -	{ -		LLScrollListItem::draw(rect, fg_color, hover_color, select_color, highlight_color, column_padding); - -		LLWString wstr(1, mEmoji); -		S32 width = getColumn(0)->getWidth(); -		F32 x = rect.mLeft + width / 2; -		F32 y = rect.getCenterY(); -		LLFontGL::getFontEmoji()->render( -			wstr,                       // wstr -			0,                          // begin_offset -			x,                          // x -			y,                          // y -			LLColor4::white,            // color -			LLFontGL::HCENTER,          // halign -			LLFontGL::VCENTER,          // valign -			LLFontGL::NORMAL,           // style -			LLFontGL::DROP_SHADOW_SOFT, // shadow -			1,                          // max_chars -			S32_MAX,                    // max_pixels -			nullptr,                    // right_x -			false,                      // use_ellipses -			true);                      // use_color -	} +    LLEmojiScrollListItem(const llwchar emoji, const LLScrollListItem::Params& params) +        : LLScrollListItem(params) +        , mEmoji(emoji) +    { +    } + +    llwchar getEmoji() const { return mEmoji; } + +    virtual void draw(const LLRect& rect, +        const LLColor4& fg_color, +        const LLColor4& hover_color, // highlight/hover selection of whole item or cell +        const LLColor4& select_color, // highlight/hover selection of whole item or cell +        const LLColor4& highlight_color, // highlights contents of cells (ex: text) +        S32 column_padding) override +    { +        LLScrollListItem::draw(rect, fg_color, hover_color, select_color, highlight_color, column_padding); + +        LLWString wstr(1, mEmoji); +        S32 width = getColumn(0)->getWidth(); +        F32 x = rect.mLeft + width / 2; +        F32 y = rect.getCenterY(); +        LLFontGL::getFontEmoji()->render( +            wstr,                       // wstr +            0,                          // begin_offset +            x,                          // x +            y,                          // y +            LLColor4::white,            // color +            LLFontGL::HCENTER,          // halign +            LLFontGL::VCENTER,          // valign +            LLFontGL::NORMAL,           // style +            LLFontGL::DROP_SHADOW_SOFT, // shadow +            1,                          // max_chars +            S32_MAX,                    // max_pixels +            nullptr,                    // right_x +            false,                      // use_ellipses +            true);                      // use_color +    }  private: -	llwchar mEmoji; +    llwchar mEmoji;  };  class LLEmojiGridRow : public LLScrollingPanel  {  public: -	LLEmojiGridRow(const LLPanel::Params& panel_params, -		const LLScrollingPanelList::Params& list_params) -		: LLScrollingPanel(panel_params) -		, mList(new LLScrollingPanelList(list_params)) -	{ -		addChild(mList); -	} +    LLEmojiGridRow(const LLPanel::Params& panel_params, +        const LLScrollingPanelList::Params& list_params) +        : LLScrollingPanel(panel_params) +        , mList(new LLScrollingPanelList(list_params)) +    { +        addChild(mList); +    } -	virtual void updatePanel(BOOL allow_modify) override {} +    virtual void updatePanel(BOOL allow_modify) override {}  public: -	LLScrollingPanelList* mList; +    LLScrollingPanelList* mList;  };  class LLEmojiGridDivider : public LLScrollingPanel  {  public: -	LLEmojiGridDivider(const LLPanel::Params& panel_params, std::string text) -		: LLScrollingPanel(panel_params) -		, mText(utf8string_to_wstring(text)) -	{ -	} - -	virtual void draw() override -	{ -		LLScrollingPanel::draw(); - -		F32 x = 4; // padding-left -		F32 y = getRect().getHeight() / 2; -		LLFontGL::getFontSansSerifBold()->render( -			mText,                      // wstr -			0,                          // begin_offset -			x,                          // x -			y,                          // y -			LLColor4::white,            // color -			LLFontGL::LEFT,             // halign -			LLFontGL::VCENTER,          // valign -			LLFontGL::NORMAL,           // style -			LLFontGL::DROP_SHADOW_SOFT, // shadow -			mText.size(),               // max_chars -			S32_MAX,                    // max_pixels -			nullptr,                    // right_x -			false,                      // use_ellipses -			true);                      // use_color -	} - -	virtual void updatePanel(BOOL allow_modify) override {} +    LLEmojiGridDivider(const LLPanel::Params& panel_params, std::string text) +        : LLScrollingPanel(panel_params) +        , mText(utf8string_to_wstring(text)) +    { +    } + +    virtual void draw() override +    { +        LLScrollingPanel::draw(); + +        F32 x = 4; // padding-left +        F32 y = getRect().getHeight() / 2; +        LLFontGL::getFontSansSerifBold()->render( +            mText,                      // wstr +            0,                          // begin_offset +            x,                          // x +            y,                          // y +            LLColor4::white,            // color +            LLFontGL::LEFT,             // halign +            LLFontGL::VCENTER,          // valign +            LLFontGL::NORMAL,           // style +            LLFontGL::DROP_SHADOW_SOFT, // shadow +            mText.size(),               // max_chars +            S32_MAX,                    // max_pixels +            nullptr,                    // right_x +            false,                      // use_ellipses +            true);                      // use_color +    } + +    virtual void updatePanel(BOOL allow_modify) override {}  private: -	const LLWString mText; +    const LLWString mText;  };  class LLEmojiGridIcon : public LLScrollingPanel  {  public: -	LLEmojiGridIcon(const LLPanel::Params& panel_params, const LLEmojiDescriptor* descr, std::string category) -		: LLScrollingPanel(panel_params) -		, mEmoji(descr->Character) -		, mText(LLWString(1, mEmoji)) -		, mDescr(descr->Name) -		, mCategory(category) -	{ -	} - -	virtual void draw() override -	{ -		LLScrollingPanel::draw(); - -		F32 x = getRect().getWidth() / 2; -		F32 y = getRect().getHeight() / 2; -		LLFontGL::getFontEmoji()->render( -			mText,                      // wstr -			0,                          // begin_offset -			x,                          // x -			y,                          // y -			LLColor4::white,            // color -			LLFontGL::HCENTER,          // halign -			LLFontGL::VCENTER,          // valign -			LLFontGL::NORMAL,           // style -			LLFontGL::DROP_SHADOW_SOFT, // shadow -			1,                          // max_chars -			S32_MAX,                    // max_pixels -			nullptr,                    // right_x -			false,                      // use_ellipses -			true);                      // use_color -	} - -	virtual void updatePanel(BOOL allow_modify) override {} - -	llwchar getEmoji() const { return mEmoji; } -	LLWString getText() const { return mText; } -	std::string getDescr() const { return mDescr; } -	std::string getCategory() const { return mCategory; } +    LLEmojiGridIcon(const LLPanel::Params& panel_params, const LLEmojiDescriptor* descr, std::string category) +        : LLScrollingPanel(panel_params) +        , mEmoji(descr->Character) +        , mText(LLWString(1, mEmoji)) +        , mDescr(descr->getShortCodes()) +        , mCategory(category) +    { +    } + +    virtual void draw() override +    { +        LLScrollingPanel::draw(); + +        F32 x = getRect().getWidth() / 2; +        F32 y = getRect().getHeight() / 2; +        LLFontGL::getFontEmoji()->render( +            mText,                      // wstr +            0,                          // begin_offset +            x,                          // x +            y,                          // y +            LLColor4::white,            // color +            LLFontGL::HCENTER,          // halign +            LLFontGL::VCENTER,          // valign +            LLFontGL::NORMAL,           // style +            LLFontGL::DROP_SHADOW_SOFT, // shadow +            1,                          // max_chars +            S32_MAX,                    // max_pixels +            nullptr,                    // right_x +            false,                      // use_ellipses +            true);                      // use_color +    } + +    virtual void updatePanel(BOOL allow_modify) override {} + +    llwchar getEmoji() const { return mEmoji; } +    LLWString getText() const { return mText; } +    std::string getDescr() const { return mDescr; } +    std::string getCategory() const { return mCategory; }  private: -	const llwchar mEmoji; -	const LLWString mText; -	const std::string mDescr; -	const std::string mCategory; +    const llwchar mEmoji; +    const LLWString mText; +    const std::string mDescr; +    const std::string mCategory;  };  LLFloaterEmojiPicker* LLFloaterEmojiPicker::getInstance()  { -	LLFloaterEmojiPicker* floater = LLFloaterReg::getTypedInstance<LLFloaterEmojiPicker>("emoji_picker"); -	if (!floater) -		LL_ERRS() << "Cannot instantiate emoji picker" << LL_ENDL; -	return floater; +    LLFloaterEmojiPicker* floater = LLFloaterReg::getTypedInstance<LLFloaterEmojiPicker>("emoji_picker"); +    if (!floater) +        LL_ERRS() << "Cannot instantiate emoji picker" << LL_ENDL; +    return floater;  }  LLFloaterEmojiPicker* LLFloaterEmojiPicker::showInstance(pick_callback_t pick_callback, close_callback_t close_callback)  { -	LLFloaterEmojiPicker* floater = getInstance(); -	floater->show(pick_callback, close_callback); -	return floater; +    LLFloaterEmojiPicker* floater = getInstance(); +    floater->show(pick_callback, close_callback); +    return floater;  }  void LLFloaterEmojiPicker::show(pick_callback_t pick_callback, close_callback_t close_callback)  { -	mEmojiPickCallback = pick_callback; -	mFloaterCloseCallback = close_callback; -	openFloater(mKey); -	setFocus(TRUE); +    mEmojiPickCallback = pick_callback; +    mFloaterCloseCallback = close_callback; +    openFloater(mKey); +    setFocus(TRUE);  }  LLFloaterEmojiPicker::LLFloaterEmojiPicker(const LLSD& key) @@ -221,313 +222,415 @@ LLFloaterEmojiPicker::LLFloaterEmojiPicker(const LLSD& key)  BOOL LLFloaterEmojiPicker::postBuild()  { -	// Should be initialized first -	mPreviewEmoji = getChild<LLButton>("PreviewEmoji"); -	mPreviewEmoji->setClickedCallback([this](LLUICtrl*, const LLSD&) { onPreviewEmojiClick(); }); - -	mDescription = getChild<LLTextBox>("Description"); -	mDescription->setVisible(FALSE); - -	mCategory = getChild<LLComboBox>("Category"); -	mCategory->setCommitCallback([this](LLUICtrl*, const LLSD&) { onCategoryCommit(); }); -	const LLEmojiDictionary::cat2descrs_map_t& cat2Descrs = LLEmojiDictionary::instance().getCategory2Descrs(); -	mCategory->clearRows(); -	for (const LLEmojiDictionary::cat2descrs_item_t& item : cat2Descrs) -	{ -		std::string value = item.first; -		std::string name = value; -		LLStringUtil::capitalize(name); -		mCategory->add(name, value); -	} -	mCategory->setSelectedByValue(mSelectedCategory, true); - -	mSearch = getChild<LLLineEditor>("Search"); -	mSearch->setKeystrokeCallback([this](LLLineEditor*, void*) { onSearchKeystroke(); }, NULL); -	mSearch->setFont(LLViewerChat::getChatFont()); -	mSearch->setText(mSearchPattern); - -	mEmojiScroll = getChild<LLScrollContainer>("EmojiGridContainer"); -	mEmojiScroll->setMouseEnterCallback([this](LLUICtrl*, const LLSD&) { onGridMouseEnter(); }); -	mEmojiScroll->setMouseLeaveCallback([this](LLUICtrl*, const LLSD&) { onGridMouseLeave(); }); - -	mEmojiGrid = getChild<LLScrollingPanelList>("EmojiGrid"); - -	fillEmojiGrid(); - -	return TRUE; +    // Should be initialized first +    mPreviewEmoji = getChild<LLButton>("PreviewEmoji"); +    mPreviewEmoji->setClickedCallback([this](LLUICtrl*, const LLSD&) { onPreviewEmojiClick(); }); + +    mDescription = getChild<LLTextBox>("Description"); +    mDescription->setVisible(FALSE); + +    mGroups = getChild<LLPanel>("Groups"); +    mBadge = getChild<LLPanel>("Badge"); + +    mSearch = getChild<LLLineEditor>("Search"); +    mSearch->setKeystrokeCallback([this](LLLineEditor*, void*) { onSearchKeystroke(); }, NULL); +    mSearch->setFont(LLViewerChat::getChatFont()); +    mSearch->setText(sSearchPattern); + +    mEmojiScroll = getChild<LLScrollContainer>("EmojiGridContainer"); +    mEmojiScroll->setMouseEnterCallback([this](LLUICtrl*, const LLSD&) { onGridMouseEnter(); }); +    mEmojiScroll->setMouseLeaveCallback([this](LLUICtrl*, const LLSD&) { onGridMouseLeave(); }); + +    mEmojiGrid = getChild<LLScrollingPanelList>("EmojiGrid"); + +    fillGroups(); +    fillEmojis(); + +    return TRUE;  }  void LLFloaterEmojiPicker::dirtyRect()  { -	super::dirtyRect(); +    super::dirtyRect(); -	if (mEmojiScroll && mEmojiScroll->getRect().getWidth() != mRecentGridWidth) -	{ -		fillEmojiGrid(); -	} +    if (mEmojiScroll && mEmojiScroll->getRect().getWidth() != mRecentGridWidth) +    { +        moveGroups(); +        fillEmojis(true); +    }  }  LLFloaterEmojiPicker::~LLFloaterEmojiPicker()  { -	gFocusMgr.releaseFocusIfNeeded( this ); +    gFocusMgr.releaseFocusIfNeeded( this ); +} + +void LLFloaterEmojiPicker::fillGroups() +{ +    LLButton::Params params; +    params.font = LLFontGL::getFontEmoji(); +    //params.use_font_color = true; + +    LLRect rect; +    rect.mTop = mGroups->getRect().getHeight(); +    rect.mBottom = mBadge->getRect().getHeight(); + +    const std::vector<LLEmojiGroup>& groups = LLEmojiDictionary::instance().getGroups(); +    for (const LLEmojiGroup& group : groups) +    { +        LLButton* button = LLUICtrlFactory::create<LLButton>(params); +        button->setClickedCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonClick(ctrl); }); +        button->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseEnter(ctrl); }); +        button->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseLeave(ctrl); }); + +        button->setRect(rect); + +        LLUIString text; +        text.insert(0, LLWString(1, group.Character)); +        button->setLabel(text); + +        if (mGroupButtons.size() == sSelectedGroupIndex) +        { +            button->setToggleState(TRUE); +        } + +        mGroupButtons.push_back(button); +        mGroups->addChild(button); +    } + +    moveGroups(); +} + +void LLFloaterEmojiPicker::moveGroups() +{ +    const std::vector<LLEmojiGroup>& groups = LLEmojiDictionary::instance().getGroups(); +    if (groups.empty()) +        return; + +    int badgeWidth = mGroups->getRect().getWidth() / groups.size(); +    if (badgeWidth == mRecentBadgeWidth) +        return; + +    mRecentBadgeWidth = badgeWidth; + +    for (int i = 0; i < mGroupButtons.size(); ++i) +    { +        LLRect rect = mGroupButtons[i]->getRect(); +        rect.mLeft = badgeWidth * i; +        rect.mRight = rect.mLeft + badgeWidth; +        mGroupButtons[i]->setRect(rect); +    } + +    LLRect rect = mBadge->getRect(); +    rect.mLeft = badgeWidth * sSelectedGroupIndex; +    rect.mRight = rect.mLeft + badgeWidth; +    mBadge->setRect(rect);  } -void LLFloaterEmojiPicker::fillEmojiGrid() +void LLFloaterEmojiPicker::fillEmojis(bool fromResize)  { -	mRecentGridWidth = mEmojiScroll->getRect().getWidth(); - -	S32 scrollbarSize = mEmojiScroll->getSize(); -	if (scrollbarSize < 0) -	{ -		static LLUICachedControl<S32> scrollbar_size_control("UIScrollbarSize", 0); -		scrollbarSize = scrollbar_size_control; -	} - -	const S32 clientWidth = mRecentGridWidth - scrollbarSize - mEmojiScroll->getBorderWidth() * 2; -	const S32 gridPadding = mEmojiGrid->getPadding(); -	const S32 iconSpacing = mEmojiGrid->getSpacing(); -	const S32 rowWidth = clientWidth - gridPadding * 2; -	const S32 iconSize = 28; // icon width and height -	const S32 maxIcons = llmax(1, (rowWidth + iconSpacing) / (iconSize + iconSpacing)); - -	// Optimization: don't rearrange for different widths with the same maxIcons -	if (maxIcons == mRecentMaxIcons) -		return; -	mRecentMaxIcons = maxIcons; - -	mHoveredIcon = nullptr; -	mEmojiGrid->clearPanels(); -	mPreviewEmoji->setLabel(LLUIString()); - -	if (mEmojiGrid->getRect().getWidth() != clientWidth) -	{ -		LLRect rect = mEmojiGrid->getRect(); -		rect.mRight = rect.mLeft + clientWidth; -		mEmojiGrid->setRect(rect); -	} - -	LLPanel::Params row_panel_params; -	row_panel_params.rect = LLRect(0, iconSize, rowWidth, 0); - -	LLScrollingPanelList::Params row_list_params; -	row_list_params.rect = row_panel_params.rect; -	row_list_params.is_horizontal = TRUE; -	row_list_params.padding = 0; -	row_list_params.spacing = iconSpacing; - -	LLPanel::Params icon_params; -	LLRect icon_rect(0, iconSize, iconSize, 0); - -	static const LLColor4 bgcolors[] = -	{ -		LLColor4(0.8f, 0.6f, 0.8f, 1.0f), -		LLColor4(0.8f, 0.8f, 0.4f, 1.0f), -		LLColor4(0.6f, 0.6f, 0.8f, 1.0f), -		LLColor4(0.4f, 0.7f, 0.4f, 1.0f), -		LLColor4(0.5f, 0.7f, 0.9f, 1.0f), -		LLColor4(0.7f, 0.8f, 0.2f, 1.0f) -	}; - -	static constexpr U32 bgcolorCount = sizeof(bgcolors) / sizeof(*bgcolors); - -	auto listCategory = [&](std::string category, const std::vector<const LLEmojiDescriptor*>& emojis, bool showDivider) -	{ -		int iconIndex = 0; -		LLEmojiGridRow* row = nullptr; -		LLStringUtil::capitalize(category); -		for (const LLEmojiDescriptor* descr : emojis) -		{ -			if (mSearchPattern.empty() || matchesPattern(descr)) -			{ -				// Place a category title if needed -				if (showDivider) -				{ -					LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, category); -					mEmojiGrid->addPanel(div, true); -					showDivider = false; -				} - -				// Place a new row each (maxIcons) icons -				if (!(iconIndex % maxIcons)) -				{ -					row = new LLEmojiGridRow(row_panel_params, row_list_params); -					mEmojiGrid->addPanel(row, true); -				} - -				// Place a new icon to the current row -				LLEmojiGridIcon* icon = new LLEmojiGridIcon(icon_params, descr, category); -				icon->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseEnter(ctrl); }); -				icon->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseLeave(ctrl); }); -				icon->setMouseUpCallback([this](LLUICtrl* ctrl, S32, S32, MASK mask) { onEmojiMouseClick(ctrl, mask); }); -				icon->setBackgroundColor(bgcolors[iconIndex % bgcolorCount]); -				icon->setBackgroundOpaque(1); -				icon->setRect(icon_rect); -				row->mList->addPanel(icon, true); - -				iconIndex++; -			} -		} -	}; - -	const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs(); -	if (mSelectedCategory.empty()) -	{ -		// List all categories with titles -		for (const LLEmojiDictionary::cat2descrs_item_t& item : category2Descr) -		{ -			listCategory(item.first, item.second, TRUE); -		} -	} -	else -	{ -		// List one category without title -		const LLEmojiDictionary::cat2descrs_map_t::const_iterator& item = category2Descr.find(mSelectedCategory); -		if (item != category2Descr.end()) -		{ -			listCategory(mSelectedCategory, item->second, FALSE); -		} -	} +    mRecentGridWidth = mEmojiScroll->getRect().getWidth(); + +    S32 scrollbarSize = mEmojiScroll->getSize(); +    if (scrollbarSize < 0) +    { +        static LLUICachedControl<S32> scrollbar_size_control("UIScrollbarSize", 0); +        scrollbarSize = scrollbar_size_control; +    } + +    const S32 clientWidth = mRecentGridWidth - scrollbarSize - mEmojiScroll->getBorderWidth() * 2; +    const S32 gridPadding = mEmojiGrid->getPadding(); +    const S32 iconSpacing = mEmojiGrid->getSpacing(); +    const S32 rowWidth = clientWidth - gridPadding * 2; +    const S32 iconSize = 28; // icon width and height +    const S32 maxIcons = llmax(1, (rowWidth + iconSpacing) / (iconSize + iconSpacing)); + +    // Optimization: don't rearrange for different widths with the same maxIcons +    if (fromResize && (maxIcons == mRecentMaxIcons)) +        return; + +    mRecentMaxIcons = maxIcons; + +    mHoveredIcon = nullptr; +    mEmojiGrid->clearPanels(); +    mPreviewEmoji->setLabel(LLUIString()); + +    if (mEmojiGrid->getRect().getWidth() != clientWidth) +    { +        LLRect rect = mEmojiGrid->getRect(); +        rect.mRight = rect.mLeft + clientWidth; +        mEmojiGrid->setRect(rect); +    } + +    LLPanel::Params row_panel_params; +    row_panel_params.rect = LLRect(0, iconSize, rowWidth, 0); + +    LLScrollingPanelList::Params row_list_params; +    row_list_params.rect = row_panel_params.rect; +    row_list_params.is_horizontal = TRUE; +    row_list_params.padding = 0; +    row_list_params.spacing = iconSpacing; + +    LLPanel::Params icon_params; +    LLRect icon_rect(0, iconSize, iconSize, 0); + +    static const LLColor4 bgcolors[] = +    { +        LLColor4(0.8f, 0.6f, 0.8f, 1.0f), +        LLColor4(0.8f, 0.8f, 0.4f, 1.0f), +        LLColor4(0.6f, 0.6f, 0.8f, 1.0f), +        LLColor4(0.4f, 0.7f, 0.4f, 1.0f), +        LLColor4(0.5f, 0.7f, 0.9f, 1.0f), +        LLColor4(0.7f, 0.8f, 0.2f, 1.0f) +    }; + +    static constexpr U32 bgcolorCount = sizeof(bgcolors) / sizeof(*bgcolors); + +    auto listCategory = [&](std::string category, const std::vector<const LLEmojiDescriptor*>& emojis) +    { +        int iconIndex = 0; +        bool showDivider = true; +        LLEmojiGridRow* row = nullptr; +        LLStringUtil::capitalize(category); +        for (const LLEmojiDescriptor* descr : emojis) +        { +            if (sSearchPattern.empty() || matchesPattern(descr)) +            { +                // Place a category title if needed +                if (showDivider) +                { +                    LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, category); +                    mEmojiGrid->addPanel(div, true); +                    showDivider = false; +                } + +                // Place a new row each (maxIcons) icons +                if (!(iconIndex % maxIcons)) +                { +                    row = new LLEmojiGridRow(row_panel_params, row_list_params); +                    mEmojiGrid->addPanel(row, true); +                } + +                // Place a new icon to the current row +                LLEmojiGridIcon* icon = new LLEmojiGridIcon(icon_params, descr, category); +                icon->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseEnter(ctrl); }); +                icon->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseLeave(ctrl); }); +                icon->setMouseUpCallback([this](LLUICtrl* ctrl, S32, S32, MASK mask) { onEmojiMouseClick(ctrl, mask); }); +                icon->setBackgroundColor(bgcolors[iconIndex % bgcolorCount]); +                icon->setBackgroundOpaque(1); +                icon->setRect(icon_rect); +                row->mList->addPanel(icon, true); + +                iconIndex++; +            } +        } +    }; + +    const std::vector<LLEmojiGroup>& groups = LLEmojiDictionary::instance().getGroups(); +    const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs(); +    if (!sSelectedGroupIndex) +    { +        // List all groups +        for (const LLEmojiGroup& group : groups) +        { +            // List all categories in group +            for (const std::string& category : group.Categories) +            { +                // List all emojis in category +                const LLEmojiDictionary::cat2descrs_map_t::const_iterator& item = category2Descr.find(category); +                if (item != category2Descr.end()) +                { +                    listCategory(category, item->second); +                } +            } +        } +    } +    else +    { +        // List all categories in the selected group +        for (const std::string& category : groups[sSelectedGroupIndex].Categories) +        { +            // List all emojis in category +            const LLEmojiDictionary::cat2descrs_map_t::const_iterator& item = category2Descr.find(category); +            if (item != category2Descr.end()) +            { +                listCategory(category, item->second); +            } +        } +    }  }  bool LLFloaterEmojiPicker::matchesPattern(const LLEmojiDescriptor* descr)  { -	if (descr->Name.find(mSearchPattern) != std::string::npos) -		return true; -	for (const std::string& shortCode : descr->ShortCodes) -		if (shortCode.find(mSearchPattern) != std::string::npos) -			return true; -	return false; +    for (const std::string& shortCode : descr->ShortCodes) +        if (shortCode.find(sSearchPattern) != std::string::npos) +            return true; +    return false;  } -void LLFloaterEmojiPicker::onCategoryCommit() +void LLFloaterEmojiPicker::onGroupButtonClick(LLUICtrl* ctrl)  { -	mSelectedCategory = mCategory->getSelectedValue().asString(); -	mRecentMaxIcons = 0; -	fillEmojiGrid(); +    if (LLButton* button = dynamic_cast<LLButton*>(ctrl)) +    { +        if (button == mGroupButtons[sSelectedGroupIndex] || button->getToggleState()) +            return; + +        auto it = std::find(mGroupButtons.begin(), mGroupButtons.end(), button); +        if (it == mGroupButtons.end()) +            return; + +        mGroupButtons[sSelectedGroupIndex]->setToggleState(FALSE); +        sSelectedGroupIndex = it - mGroupButtons.begin(); +        mGroupButtons[sSelectedGroupIndex]->setToggleState(TRUE); + +        LLRect rect = mBadge->getRect(); +        rect.mLeft = button->getRect().mLeft; +        rect.mRight = button->getRect().mRight; +        mBadge->setRect(rect); + +        mSearch->setFocus(TRUE); + +        fillEmojis(); +    }  }  void LLFloaterEmojiPicker::onSearchKeystroke()  { -	mSearchPattern = mSearch->getText(); -	mRecentMaxIcons = 0; -	fillEmojiGrid(); +    sSearchPattern = mSearch->getText(); +    fillEmojis();  }  void LLFloaterEmojiPicker::onPreviewEmojiClick()  { -	if (mEmojiPickCallback) -	{ -		if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(mHoveredIcon)) -		{ -			mEmojiPickCallback(icon->getEmoji()); -		} -	} +    if (mEmojiPickCallback) +    { +        if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(mHoveredIcon)) +        { +            mEmojiPickCallback(icon->getEmoji()); +        } +    }  }  void LLFloaterEmojiPicker::onGridMouseEnter()  { -	mSearch->setVisible(FALSE); -	mDescription->setText(LLStringExplicit(""), LLStyle::Params()); -	mDescription->setVisible(TRUE); +    mSearch->setVisible(FALSE); +    mDescription->setText(LLStringExplicit(""), LLStyle::Params()); +    mDescription->setVisible(TRUE);  }  void LLFloaterEmojiPicker::onGridMouseLeave()  { -	mDescription->setVisible(FALSE); -	mDescription->setText(LLStringExplicit(""), LLStyle::Params()); -	mSearch->setVisible(TRUE); -	mSearch->setFocus(TRUE); +    mDescription->setVisible(FALSE); +    mDescription->setText(LLStringExplicit(""), LLStyle::Params()); +    mSearch->setVisible(TRUE); +    mSearch->setFocus(TRUE); +} + +void LLFloaterEmojiPicker::onGroupButtonMouseEnter(LLUICtrl* ctrl) +{ +    if (LLButton* button = dynamic_cast<LLButton*>(ctrl)) +    { +        button->setUseFontColor(TRUE); +    } +} + +void LLFloaterEmojiPicker::onGroupButtonMouseLeave(LLUICtrl* ctrl) +{ +    if (LLButton* button = dynamic_cast<LLButton*>(ctrl)) +    { +        button->setUseFontColor(FALSE); +    }  }  void LLFloaterEmojiPicker::onEmojiMouseEnter(LLUICtrl* ctrl)  { -	if (ctrl) -	{ -		if (mHoveredIcon && mHoveredIcon != ctrl) -		{ -			unselectGridIcon(mHoveredIcon); -		} +    if (ctrl) +    { +        if (mHoveredIcon && mHoveredIcon != ctrl) +        { +            unselectGridIcon(mHoveredIcon); +        } -		selectGridIcon(ctrl); +        selectGridIcon(ctrl); -		mHoveredIcon = ctrl; -	} +        mHoveredIcon = ctrl; +    }  }  void LLFloaterEmojiPicker::onEmojiMouseLeave(LLUICtrl* ctrl)  { -	if (ctrl) -	{ -		if (ctrl == mHoveredIcon) -		{ -			unselectGridIcon(ctrl); -		} -	} +    if (ctrl) +    { +        if (ctrl == mHoveredIcon) +        { +            unselectGridIcon(ctrl); +        } +    }  }  void LLFloaterEmojiPicker::onEmojiMouseClick(LLUICtrl* ctrl, MASK mask)  { -	if (mEmojiPickCallback) -	{ -		if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) -		{ -			mPreviewEmoji->handleAnyMouseClick(0, 0, 0, EMouseClickType::CLICK_LEFT, TRUE); -			mPreviewEmoji->handleAnyMouseClick(0, 0, 0, EMouseClickType::CLICK_LEFT, FALSE); -			if (!(mask & 4)) -			{ -				closeFloater(); -			} -		} -	} +    if (mEmojiPickCallback) +    { +        if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) +        { +            mPreviewEmoji->handleAnyMouseClick(0, 0, 0, EMouseClickType::CLICK_LEFT, TRUE); +            mPreviewEmoji->handleAnyMouseClick(0, 0, 0, EMouseClickType::CLICK_LEFT, FALSE); +            if (!(mask & 4)) +            { +                closeFloater(); +            } +        } +    }  }  void LLFloaterEmojiPicker::selectGridIcon(LLUICtrl* ctrl)  { -	if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) -	{ -		icon->setBackgroundVisible(TRUE); +    if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) +    { +        icon->setBackgroundVisible(TRUE); -		LLUIString text; -		text.insert(0, icon->getText()); -		mPreviewEmoji->setLabel(text); +        LLUIString text; +        text.insert(0, icon->getText()); +        mPreviewEmoji->setLabel(text); -		std::string descr = icon->getDescr() + "\n" + icon->getCategory(); -		mDescription->setText(LLStringExplicit(descr), LLStyle::Params()); -	} +        std::string descr = icon->getDescr() + "\n" + icon->getCategory(); +        mDescription->setText(LLStringExplicit(descr), LLStyle::Params()); +    }  }  void LLFloaterEmojiPicker::unselectGridIcon(LLUICtrl* ctrl)  { -	if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) -	{ -		icon->setBackgroundVisible(FALSE); -		mPreviewEmoji->setLabel(LLUIString()); -		mDescription->setText(LLStringExplicit(""), LLStyle::Params()); -	} +    if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) +    { +        icon->setBackgroundVisible(FALSE); +        mPreviewEmoji->setLabel(LLUIString()); +        mDescription->setText(LLStringExplicit(""), LLStyle::Params()); +    }  }  // virtual  BOOL LLFloaterEmojiPicker::handleKeyHere(KEY key, MASK mask)  { -	if (mask == MASK_NONE) -	{ -		switch (key) -		{ -		case KEY_ESCAPE: -			closeFloater(); -			return TRUE; -		} -	} - -	return LLFloater::handleKeyHere(key, mask); +    if (mask == MASK_NONE) +    { +        switch (key) +        { +        case KEY_ESCAPE: +            closeFloater(); +            return TRUE; +        } +    } + +    return LLFloater::handleKeyHere(key, mask);  }  // virtual  void LLFloaterEmojiPicker::closeFloater(bool app_quitting)  { -	LLFloater::closeFloater(app_quitting); -	if (mFloaterCloseCallback) -	{ -		mFloaterCloseCallback(); -	} +    LLFloater::closeFloater(app_quitting); +    if (mFloaterCloseCallback) +    { +        mFloaterCloseCallback(); +    }  } diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h index 300d9a4d4a..7fa6ea46b0 100644 --- a/indra/newview/llfloateremojipicker.h +++ b/indra/newview/llfloateremojipicker.h @@ -33,62 +33,70 @@ struct LLEmojiDescriptor;  class LLFloaterEmojiPicker : public LLFloater  { -	using super = LLFloater; +    using super = LLFloater;  public: -	// The callback function will be called with an emoji char. -	typedef boost::function<void (llwchar)> pick_callback_t; -	typedef boost::function<void ()> close_callback_t; +    // The callback function will be called with an emoji char. +    typedef boost::function<void (llwchar)> pick_callback_t; +    typedef boost::function<void ()> close_callback_t; -	// Call this to select an emoji. -	static LLFloaterEmojiPicker* getInstance(); -	static LLFloaterEmojiPicker* showInstance(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr); +    // Call this to select an emoji. +    static LLFloaterEmojiPicker* getInstance(); +    static LLFloaterEmojiPicker* showInstance(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr); -	LLFloaterEmojiPicker(const LLSD& key); -	virtual ~LLFloaterEmojiPicker(); +    LLFloaterEmojiPicker(const LLSD& key); +    virtual ~LLFloaterEmojiPicker(); -	virtual	BOOL postBuild() override; -	virtual void dirtyRect() override; +    virtual	BOOL postBuild() override; +    virtual void dirtyRect() override; -	void show(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr); +    void show(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr); -	virtual void closeFloater(bool app_quitting = false) override; +    virtual void closeFloater(bool app_quitting = false) override;  private: -	void fillEmojiGrid(); - -	bool matchesPattern(const LLEmojiDescriptor* descr); - -	void onCategoryCommit(); -	void onSearchKeystroke(); -	void onPreviewEmojiClick(); -	void onGridMouseEnter(); -	void onGridMouseLeave(); -	void onEmojiMouseEnter(LLUICtrl* ctrl); -	void onEmojiMouseLeave(LLUICtrl* ctrl); -	void onEmojiMouseClick(LLUICtrl* ctrl, MASK mask); - -	void selectGridIcon(LLUICtrl* ctrl); -	void unselectGridIcon(LLUICtrl* ctrl); - -	virtual BOOL handleKeyHere(KEY key, MASK mask) override; - -	class LLComboBox* mCategory { nullptr }; -	class LLLineEditor* mSearch { nullptr }; -	class LLScrollContainer* mEmojiScroll { nullptr }; -	class LLScrollingPanelList* mEmojiGrid { nullptr }; -	class LLButton* mPreviewEmoji { nullptr }; -	class LLTextBox* mDescription { nullptr }; - -	pick_callback_t mEmojiPickCallback; -	close_callback_t mFloaterCloseCallback; - -	S32 mRecentGridWidth { 0 }; -	S32 mRecentMaxIcons { 0 }; -	LLUICtrl* mHoveredIcon { nullptr }; - -	static std::string mSelectedCategory; -	static std::string mSearchPattern; +    void fillGroups(); +    void moveGroups(); +    void fillEmojis(bool fromResize = false); + +    bool matchesPattern(const LLEmojiDescriptor* descr); + +    void onGroupButtonClick(LLUICtrl* ctrl); +    void onSearchKeystroke(); +    void onPreviewEmojiClick(); +    void onGridMouseEnter(); +    void onGridMouseLeave(); +    void onGroupButtonMouseEnter(LLUICtrl* ctrl); +    void onGroupButtonMouseLeave(LLUICtrl* ctrl); +    void onEmojiMouseEnter(LLUICtrl* ctrl); +    void onEmojiMouseLeave(LLUICtrl* ctrl); +    void onEmojiMouseClick(LLUICtrl* ctrl, MASK mask); + +    void selectGridIcon(LLUICtrl* ctrl); +    void unselectGridIcon(LLUICtrl* ctrl); + +    virtual BOOL handleKeyHere(KEY key, MASK mask) override; + +    class LLPanel* mGroups { nullptr }; +    class LLPanel* mBadge { nullptr }; +    class LLLineEditor* mSearch { nullptr }; +    class LLScrollContainer* mEmojiScroll { nullptr }; +    class LLScrollingPanelList* mEmojiGrid { nullptr }; +    class LLButton* mPreviewEmoji { nullptr }; +    class LLTextBox* mDescription { nullptr }; + +    pick_callback_t mEmojiPickCallback; +    close_callback_t mFloaterCloseCallback; + +    std::vector<class LLButton*> mGroupButtons; + +    S32 mRecentBadgeWidth { 0 }; +    S32 mRecentGridWidth { 0 }; +    S32 mRecentMaxIcons { 0 }; +    LLUICtrl* mHoveredIcon { nullptr }; + +    static size_t sSelectedGroupIndex; +    static std::string sSearchPattern;  };  #endif diff --git a/indra/newview/skins/default/xui/da/emoji_categories.xml b/indra/newview/skins/default/xui/da/emoji_categories.xml new file mode 100644 index 0000000000..456b18e4e2 --- /dev/null +++ b/indra/newview/skins/default/xui/da/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>smileys and følelser</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>mennesker and krop</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>komponenter</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>dyr and natur</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>mad and drikke</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>rejser and steder</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>oplevelser</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objekter</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>symboler</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/de/emoji_categories.xml b/indra/newview/skins/default/xui/de/emoji_categories.xml new file mode 100644 index 0000000000..ed63d0bac9 --- /dev/null +++ b/indra/newview/skins/default/xui/de/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>Smileys and Emotionen</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>Menschen and Körper</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>Komponenten</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>Tiere and Natur</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>Essen and Trinken</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>Reisen and Orte</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>Aktivitäten</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>Gegenstände</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>Symbole</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/en/emoji_categories.xml b/indra/newview/skins/default/xui/en/emoji_categories.xml new file mode 100644 index 0000000000..0315d0c43a --- /dev/null +++ b/indra/newview/skins/default/xui/en/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>smileys and emotion</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>people and body</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>components</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animals and nature</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>food and drink</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>travel and places</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>activities</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objects</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>symbols</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml index 2e153933c0..2a534b23b9 100644 --- a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml +++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml @@ -7,8 +7,9 @@      legacy_header_height="0"      can_resize="true"      layout="topleft" +    min_width="250"      height="400" -    width="200"> +    width="250">    <line_editor        name="Search"        label="Type to search" @@ -19,7 +20,7 @@        bottom="14"        left="34"        height="29" -      width="162" /> +      width="212" />    <text        name="Description"        layout="bottomleft" @@ -28,7 +29,7 @@        bottom="14"        left="42"        height="29" -      width="150" /> +      width="200" />    <button        name="PreviewEmoji"        layout="bottomleft" @@ -46,7 +47,7 @@        top="25"        left="0"        height="330" -      width="200"> +      width="250">      <scrolling_panel_list          name="EmojiGrid"          layout="topleft" @@ -55,16 +56,26 @@          spacing="0"          top="0"          left="0" -        width="200"/> +        width="250"/>    </scroll_container> -  <combo_box -      name="Category" -      label="Choose a category" +  <panel +      name="Groups"        layout="topleft"        follows="top|left|right" -      allow_text_entry="true"        top="0" -      left="2" +      left="0"        height="25" -      width="196" /> +      width="250"> +    <panel +      name="Badge" +      layout="bottomleft" +      follows="bottom|left" +      background_visible="true" +      background_opaque="true" +      bg_opaque_color="FrogGreen" +      bottom="0" +      height="2" +      width="20" +      /> +  </panel>  </floater> diff --git a/indra/newview/skins/default/xui/es/emoji_categories.xml b/indra/newview/skins/default/xui/es/emoji_categories.xml new file mode 100644 index 0000000000..b1b73eba5e --- /dev/null +++ b/indra/newview/skins/default/xui/es/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>emoticonos y emoción</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>personas y cuerpo</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>componentes</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animales y la naturaleza</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>comida y bebida</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>viajes y lugares</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>actividades</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objetos</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>símbolos</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/fr/emoji_categories.xml b/indra/newview/skins/default/xui/fr/emoji_categories.xml new file mode 100644 index 0000000000..38dc9cb8f8 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>smileys et émotion</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>les gens et le corps</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>composants</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animaux et la nature</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>nourriture et boissons</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>voyages et lieux</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>activités</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objets</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>symboles</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/it/emoji_categories.xml b/indra/newview/skins/default/xui/it/emoji_categories.xml new file mode 100644 index 0000000000..a4782e60a6 --- /dev/null +++ b/indra/newview/skins/default/xui/it/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>smileys and emozione</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>persone e corpo</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>componenti</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animali and natura</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>cibo e bevande</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>viaggi and luoghi</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>attività</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>oggetti</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>simboli</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/ja/emoji_categories.xml b/indra/newview/skins/default/xui/ja/emoji_categories.xml new file mode 100644 index 0000000000..7750f4ad2e --- /dev/null +++ b/indra/newview/skins/default/xui/ja/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>スマイリーと感情</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>人体</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>コンポーネント</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>動物自然</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>飲み物・食べ物</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>旅行・場所</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>有効化</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>オブジェクト</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>シンボル</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/pl/emoji_categories.xml b/indra/newview/skins/default/xui/pl/emoji_categories.xml new file mode 100644 index 0000000000..9aad7af794 --- /dev/null +++ b/indra/newview/skins/default/xui/pl/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>buźki and emocje</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>ludzie and ciało</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>składniki</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>zwierzęta and przyroda</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>jedzenie i picie</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>podróże and miejsca</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>aktywność</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objekt</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>symbole</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/pt/emoji_categories.xml b/indra/newview/skins/default/xui/pt/emoji_categories.xml new file mode 100644 index 0000000000..887444b957 --- /dev/null +++ b/indra/newview/skins/default/xui/pt/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>sorrisos e emoção</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>pessoas e corpo</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>componentes</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animais e natureza</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>comida e bebida</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>viagens e lugares</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>atividades</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objetos</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>símbolos</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/ru/emoji_categories.xml b/indra/newview/skins/default/xui/ru/emoji_categories.xml new file mode 100644 index 0000000000..b08f0d8117 --- /dev/null +++ b/indra/newview/skins/default/xui/ru/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>смайлики и люди</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>тело людей</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>компонент</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>животные и природа</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>еда и напитки</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>путешествия и местности</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>варианты досуга</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>предметы</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>символы</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/zh/emoji_categories.xml b/indra/newview/skins/default/xui/zh/emoji_categories.xml new file mode 100644 index 0000000000..fbe6165eeb --- /dev/null +++ b/indra/newview/skins/default/xui/zh/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>笑脸</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>人体</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>组件</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>野生动物</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>食物飲料</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>旅遊地點</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>个人活动</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>物件</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>人的符号</string> +    </map> +  </array> +</llsd>  | 
