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/contact_list.hpp
|
||||
./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.cpp
|
||||
./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};
|
||||
};
|
||||
|
||||
// empty contact comp that is sorted in the set for displaying
|
||||
struct ContactSortTag {};
|
||||
|
||||
} // Components
|
||||
|
||||
namespace Context {
|
||||
@ -207,7 +204,8 @@ ChatGui4::ChatGui4(
|
||||
_b_tc(_bil, tu),
|
||||
_theme(theme),
|
||||
_sip(tu),
|
||||
_ivp(_msg_tc)
|
||||
_ivp(_msg_tc),
|
||||
_cls(cs)
|
||||
{
|
||||
_os_sr.subscribe(ObjectStore_Event::object_update);
|
||||
}
|
||||
@ -258,57 +256,9 @@ float ChatGui4::render(float time_delta, bool window_hidden, bool window_focused
|
||||
}
|
||||
|
||||
renderContactList();
|
||||
// after vis check
|
||||
if (_contact_list_sortable) {
|
||||
// TODO: extract this; with timer and events to dirty
|
||||
// !! 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
|
||||
);
|
||||
_cls.sort();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
@ -1715,7 +1665,7 @@ void ChatGui4::renderContactList(void) {
|
||||
_rmm,
|
||||
_theme,
|
||||
_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.entity();
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "./chat_gui/file_selector.hpp"
|
||||
#include "./chat_gui/send_image_popup.hpp"
|
||||
#include "./chat_gui/image_viewer_popup.hpp"
|
||||
#include "./chat_gui/contact_list_sorter.hpp"
|
||||
|
||||
#include <entt/container/dense_map.hpp>
|
||||
|
||||
@ -41,9 +42,9 @@ class ChatGui4 : public ObjectStoreEventI {
|
||||
FileSelector _fss;
|
||||
SendImagePopup _sip;
|
||||
ImageViewerPopup _ivp;
|
||||
ContactListSorter _cls;
|
||||
|
||||
// set to true if not hovered
|
||||
// TODO: add timer?
|
||||
bool _contact_list_sortable {false};
|
||||
|
||||
// TODO: refactor this to allow multiple open contacts
|
||||
|
Loading…
x
Reference in New Issue
Block a user