extract contact list sorting and refine sort
Some checks are pending
ContinuousDelivery / linux-ubuntu (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:armeabi-v7a vcpkg_toolkit:arm-neon-android]) (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousDelivery / windows (push) Waiting to run
ContinuousDelivery / windows-asan (push) Waiting to run
ContinuousDelivery / dumpsyms (push) Blocked by required conditions
ContinuousDelivery / release (push) Blocked by required conditions
ContinuousIntegration / linux (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:armeabi-v7a vcpkg_toolkit:arm-neon-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousIntegration / macos (push) Waiting to run
ContinuousIntegration / windows (push) Waiting to run
Some checks are pending
ContinuousDelivery / linux-ubuntu (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:armeabi-v7a vcpkg_toolkit:arm-neon-android]) (push) Waiting to run
ContinuousDelivery / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousDelivery / windows (push) Waiting to run
ContinuousDelivery / windows-asan (push) Waiting to run
ContinuousDelivery / dumpsyms (push) Blocked by required conditions
ContinuousDelivery / release (push) Blocked by required conditions
ContinuousIntegration / linux (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:arm64-v8a vcpkg_toolkit:arm64-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:armeabi-v7a vcpkg_toolkit:arm-neon-android]) (push) Waiting to run
ContinuousIntegration / android (map[ndk_abi:x86_64 vcpkg_toolkit:x64-android]) (push) Waiting to run
ContinuousIntegration / macos (push) Waiting to run
ContinuousIntegration / windows (push) Waiting to run
This commit is contained in:
parent
0f85bcc128
commit
b87866cb0b
@ -89,6 +89,8 @@ target_sources(tomato PUBLIC
|
|||||||
./chat_gui/icons/group.cpp
|
./chat_gui/icons/group.cpp
|
||||||
./chat_gui/contact_list.hpp
|
./chat_gui/contact_list.hpp
|
||||||
./chat_gui/contact_list.cpp
|
./chat_gui/contact_list.cpp
|
||||||
|
./chat_gui/contact_list_sorter.hpp
|
||||||
|
./chat_gui/contact_list_sorter.cpp
|
||||||
./chat_gui/file_selector.hpp
|
./chat_gui/file_selector.hpp
|
||||||
./chat_gui/file_selector.cpp
|
./chat_gui/file_selector.cpp
|
||||||
./chat_gui/image_viewer_popup.hpp
|
./chat_gui/image_viewer_popup.hpp
|
||||||
|
138
src/chat_gui/contact_list_sorter.cpp
Normal file
138
src/chat_gui/contact_list_sorter.cpp
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#include "./contact_list_sorter.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
|
||||||
|
ContactListSorter::comperator_fn ContactListSorter::getSortGroupsOverPrivates(void) {
|
||||||
|
return [](const ContactRegistry4& cr, const Contact4 lhs, const Contact4 rhs) -> std::optional<bool> {
|
||||||
|
|
||||||
|
// - groups (> privates)
|
||||||
|
if (cr.all_of<Contact::Components::TagGroup>(lhs) && !cr.all_of<Contact::Components::TagGroup>(rhs)) {
|
||||||
|
return true;
|
||||||
|
} else if (!cr.all_of<Contact::Components::TagGroup>(lhs) && cr.all_of<Contact::Components::TagGroup>(rhs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactListSorter::comperator_fn ContactListSorter::getSortAcitivty(void) {
|
||||||
|
return [](const ContactRegistry4& cr, const Contact4 lhs, const Contact4 rhs) -> std::optional<bool> {
|
||||||
|
// - activity (exists)
|
||||||
|
if (cr.all_of<Contact::Components::LastActivity>(lhs) && !cr.all_of<Contact::Components::LastActivity>(rhs)) {
|
||||||
|
return true;
|
||||||
|
} else if (!cr.all_of<Contact::Components::LastActivity>(lhs) && cr.all_of<Contact::Components::LastActivity>(rhs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// else - we can assume both have or dont have LastActivity
|
||||||
|
|
||||||
|
// - activity new > old
|
||||||
|
if (cr.all_of<Contact::Components::LastActivity>(lhs)) {
|
||||||
|
const auto l = cr.get<Contact::Components::LastActivity>(lhs).ts;
|
||||||
|
const auto r = cr.get<Contact::Components::LastActivity>(rhs).ts;
|
||||||
|
if (l > r) {
|
||||||
|
return true;
|
||||||
|
} else if (l < r) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactListSorter::comperator_fn ContactListSorter::getSortFirstSeen(void) {
|
||||||
|
return [](const ContactRegistry4& cr, const Contact4 lhs, const Contact4 rhs) -> std::optional<bool> {
|
||||||
|
// - first seen (exists)
|
||||||
|
if (cr.all_of<Contact::Components::FirstSeen>(lhs) && !cr.all_of<Contact::Components::FirstSeen>(rhs)) {
|
||||||
|
return true;
|
||||||
|
} else if (!cr.all_of<Contact::Components::FirstSeen>(lhs) && cr.all_of<Contact::Components::FirstSeen>(rhs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// else - we can assume both have or dont have FirstSeen
|
||||||
|
|
||||||
|
// - first seen new > old
|
||||||
|
if (cr.all_of<Contact::Components::FirstSeen>(lhs)) {
|
||||||
|
const auto l = cr.get<Contact::Components::FirstSeen>(lhs).ts;
|
||||||
|
const auto r = cr.get<Contact::Components::FirstSeen>(rhs).ts;
|
||||||
|
if (l > r) {
|
||||||
|
return true;
|
||||||
|
} else if (l < r) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContactListSorter::resolveStack(const std::vector<comperator_fn>& stack, const ContactRegistry4& cr, const Contact4 lhs, const Contact4 rhs) {
|
||||||
|
for (const auto& fn : stack) {
|
||||||
|
auto res = fn(cr, lhs, rhs);
|
||||||
|
if (res.has_value()) {
|
||||||
|
return res.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback to numberical ordering, making sure the ordering can be strong
|
||||||
|
return entt::to_integral(lhs) > entt::to_integral(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactListSorter::ContactListSorter(ContactStore4I& cs) :
|
||||||
|
_cs(cs), _cs_sr(_cs.newSubRef(this))
|
||||||
|
{
|
||||||
|
_sort_stack.reserve(3);
|
||||||
|
_sort_stack.emplace_back(getSortGroupsOverPrivates());
|
||||||
|
_sort_stack.emplace_back(getSortAcitivty());
|
||||||
|
_sort_stack.emplace_back(getSortFirstSeen());
|
||||||
|
|
||||||
|
_cs_sr
|
||||||
|
.subscribe(ContactStore4_Event::contact_construct)
|
||||||
|
.subscribe(ContactStore4_Event::contact_update)
|
||||||
|
.subscribe(ContactStore4_Event::contact_destroy)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactListSorter::~ContactListSorter(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContactListSorter::sort(void) {
|
||||||
|
// TODO: timer
|
||||||
|
if (!_dirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& cr = _cs.registry();
|
||||||
|
|
||||||
|
// first: make sure every cantact we want to have in the list has the tag
|
||||||
|
// do we pass exclusion to the list widget, or update sort comp? - the later
|
||||||
|
cr.clear<Contact::Components::ContactSortTag>();
|
||||||
|
for (const auto cv : cr.view<Contact::Components::TagBig>()) {
|
||||||
|
(void)cr.get_or_emplace<Contact::Components::ContactSortTag>(cv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// second: sort
|
||||||
|
cr.sort<Contact::Components::ContactSortTag>(
|
||||||
|
[this, &cr](const Contact4 lhs, const Contact4 rhs) -> bool {
|
||||||
|
return resolveStack(_sort_stack, cr, lhs, rhs);
|
||||||
|
},
|
||||||
|
entt::insertion_sort{} // o(n) in >90% of cases
|
||||||
|
);
|
||||||
|
|
||||||
|
_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContactListSorter::onEvent(const ContactStore::Events::Contact4Construct&) {
|
||||||
|
_dirty = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContactListSorter::onEvent(const ContactStore::Events::Contact4Update&) {
|
||||||
|
_dirty = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContactListSorter::onEvent(const ContactStore::Events::Contact4Destory&) {
|
||||||
|
_dirty = true;
|
||||||
|
return false;
|
||||||
|
}
|
52
src/chat_gui/contact_list_sorter.hpp
Normal file
52
src/chat_gui/contact_list_sorter.hpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_store_events.hpp>
|
||||||
|
#include <solanaceae/contact/contact_store_i.hpp>
|
||||||
|
|
||||||
|
#include "./contact_list.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Contact::Components {
|
||||||
|
|
||||||
|
// empty contact comp that is sorted in the set for displaying
|
||||||
|
struct ContactSortTag {};
|
||||||
|
|
||||||
|
} // Contact::Components
|
||||||
|
|
||||||
|
|
||||||
|
class ContactListSorter : public ContactStore4EventI {
|
||||||
|
public:
|
||||||
|
using comperator_fn = std::function<std::optional<bool>(const ContactRegistry4& cr, const Contact4 lhs, const Contact4 rhs)>;
|
||||||
|
|
||||||
|
static comperator_fn getSortGroupsOverPrivates(void);
|
||||||
|
static comperator_fn getSortAcitivty(void);
|
||||||
|
static comperator_fn getSortFirstSeen(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ContactStore4I& _cs;
|
||||||
|
ContactStore4I::SubscriptionReference _cs_sr;
|
||||||
|
std::vector<comperator_fn> _sort_stack;
|
||||||
|
|
||||||
|
bool _dirty {true};
|
||||||
|
// TODO: timer, to guarantie a sort ever X seconds?
|
||||||
|
// (turns out we dont throw on new messages <.<)
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool resolveStack(const std::vector<comperator_fn>& stack, const ContactRegistry4& cr, const Contact4 lhs, const Contact4 rhs);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// TODO: expose sort stack
|
||||||
|
ContactListSorter(ContactStore4I& cs);
|
||||||
|
~ContactListSorter(void);
|
||||||
|
|
||||||
|
// optionally perform the sort
|
||||||
|
void sort(void);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool onEvent(const ContactStore::Events::Contact4Construct&) override;
|
||||||
|
bool onEvent(const ContactStore::Events::Contact4Update&) override;
|
||||||
|
bool onEvent(const ContactStore::Events::Contact4Destory&) override;
|
||||||
|
};
|
@ -62,9 +62,6 @@ namespace Components {
|
|||||||
int tm_min {0};
|
int tm_min {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
// empty contact comp that is sorted in the set for displaying
|
|
||||||
struct ContactSortTag {};
|
|
||||||
|
|
||||||
} // Components
|
} // Components
|
||||||
|
|
||||||
namespace Context {
|
namespace Context {
|
||||||
@ -207,7 +204,8 @@ ChatGui4::ChatGui4(
|
|||||||
_b_tc(_bil, tu),
|
_b_tc(_bil, tu),
|
||||||
_theme(theme),
|
_theme(theme),
|
||||||
_sip(tu),
|
_sip(tu),
|
||||||
_ivp(_msg_tc)
|
_ivp(_msg_tc),
|
||||||
|
_cls(cs)
|
||||||
{
|
{
|
||||||
_os_sr.subscribe(ObjectStore_Event::object_update);
|
_os_sr.subscribe(ObjectStore_Event::object_update);
|
||||||
}
|
}
|
||||||
@ -258,57 +256,9 @@ float ChatGui4::render(float time_delta, bool window_hidden, bool window_focused
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderContactList();
|
renderContactList();
|
||||||
|
// after vis check
|
||||||
if (_contact_list_sortable) {
|
if (_contact_list_sortable) {
|
||||||
// TODO: extract this; with timer and events to dirty
|
_cls.sort();
|
||||||
// !! events !!
|
|
||||||
auto& cr = _cs.registry();
|
|
||||||
|
|
||||||
// first: make sure every cantact we want to have in the list has the tag
|
|
||||||
// do we pass exclusion to the list widget, or update sort comp? - the later
|
|
||||||
// TODO: re do from sratch every time?
|
|
||||||
//cr.clear<Components::ContactSortTag>();
|
|
||||||
for (const auto cv : cr.view<Contact::Components::TagBig>()) {
|
|
||||||
(void)cr.get_or_emplace<Components::ContactSortTag>(cv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// second: sort
|
|
||||||
cr.sort<Components::ContactSortTag>(
|
|
||||||
[&](const Contact4 lhs, const Contact4 rhs) -> bool {
|
|
||||||
// TODO: custom sort rules, order
|
|
||||||
|
|
||||||
// - groups (> privates)
|
|
||||||
if (cr.all_of<Contact::Components::TagGroup>(lhs) && !cr.all_of<Contact::Components::TagGroup>(rhs)) {
|
|
||||||
return true;
|
|
||||||
} else if (!cr.all_of<Contact::Components::TagGroup>(lhs) && cr.all_of<Contact::Components::TagGroup>(rhs)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - activity (exists)
|
|
||||||
if (cr.all_of<Contact::Components::LastActivity>(lhs) && !cr.all_of<Contact::Components::LastActivity>(rhs)) {
|
|
||||||
return true;
|
|
||||||
} else if (!cr.all_of<Contact::Components::LastActivity>(lhs) && cr.all_of<Contact::Components::LastActivity>(rhs)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// else - we can assume both have or dont have LastActivity
|
|
||||||
|
|
||||||
// - activity new > old
|
|
||||||
if (cr.all_of<Contact::Components::LastActivity>(lhs)) {
|
|
||||||
const auto l = cr.get<Contact::Components::LastActivity>(lhs).ts;
|
|
||||||
const auto r = cr.get<Contact::Components::LastActivity>(rhs).ts;
|
|
||||||
if (l > r) {
|
|
||||||
return true;
|
|
||||||
} else if (l < r) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - first seen new > old
|
|
||||||
// TODO: implement
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
entt::insertion_sort{} // o(n) in 99% of cases
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
@ -1715,7 +1665,7 @@ void ChatGui4::renderContactList(void) {
|
|||||||
_rmm,
|
_rmm,
|
||||||
_theme,
|
_theme,
|
||||||
_contact_tc,
|
_contact_tc,
|
||||||
contact_const_runtime_view{}.iterate(cr.storage<Components::ContactSortTag>()),
|
contact_const_runtime_view{}.iterate(cr.storage<Contact::Components::ContactSortTag>()),
|
||||||
selected_contact
|
selected_contact
|
||||||
)) {
|
)) {
|
||||||
_selected_contact = selected_contact.entity();
|
_selected_contact = selected_contact.entity();
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "./chat_gui/file_selector.hpp"
|
#include "./chat_gui/file_selector.hpp"
|
||||||
#include "./chat_gui/send_image_popup.hpp"
|
#include "./chat_gui/send_image_popup.hpp"
|
||||||
#include "./chat_gui/image_viewer_popup.hpp"
|
#include "./chat_gui/image_viewer_popup.hpp"
|
||||||
|
#include "./chat_gui/contact_list_sorter.hpp"
|
||||||
|
|
||||||
#include <entt/container/dense_map.hpp>
|
#include <entt/container/dense_map.hpp>
|
||||||
|
|
||||||
@ -41,9 +42,9 @@ class ChatGui4 : public ObjectStoreEventI {
|
|||||||
FileSelector _fss;
|
FileSelector _fss;
|
||||||
SendImagePopup _sip;
|
SendImagePopup _sip;
|
||||||
ImageViewerPopup _ivp;
|
ImageViewerPopup _ivp;
|
||||||
|
ContactListSorter _cls;
|
||||||
|
|
||||||
// set to true if not hovered
|
// set to true if not hovered
|
||||||
// TODO: add timer?
|
|
||||||
bool _contact_list_sortable {false};
|
bool _contact_list_sortable {false};
|
||||||
|
|
||||||
// TODO: refactor this to allow multiple open contacts
|
// TODO: refactor this to allow multiple open contacts
|
||||||
|
Loading…
x
Reference in New Issue
Block a user