#include "./contact_list.hpp" #include #include //#include #include static void drawIconDirectLines( const ImVec2 p0, const ImVec2 p1_o, const ImU32 col, const float thickness ) { #define PLINE(x0, y0, x1, y1) \ ImGui::GetWindowDrawList()->AddLine( \ {p0.x + p1_o.x*(x0), p0.y + p1_o.y*(y0)}, \ {p0.x + p1_o.x*(x1), p0.y + p1_o.y*(y1)}, \ col, \ thickness \ ); // arrow 1 // (3,1) -> (9,7) PLINE(0.3f, 0.1f, 0.9f, 0.7f) // (9,7) -> (9,5) PLINE(0.9f, 0.7f, 0.9f, 0.5f) // (9,7) -> (7,7) PLINE(0.9f, 0.7f, 0.7f, 0.7f) // arrow 2 // (7,9) -> (1,3) PLINE(0.7f, 0.9f, 0.1f, 0.3f) // (1,3) -> (3,3) PLINE(0.1f, 0.3f, 0.3f, 0.3f) // (1,3) -> (1,5) PLINE(0.1f, 0.3f, 0.1f, 0.5f) #undef PLINE } static void drawIconDirect( const ImVec2 p0, const ImVec2 p1_o, const ImU32 col_main, const ImU32 col_back ) { // dark background // the circle looks bad in light mode //ImGui::GetWindowDrawList()->AddCircleFilled({p0.x + p1_o.x*0.5f, p0.y + p1_o.y*0.5f}, p1_o.x*0.5f, col_back); drawIconDirectLines(p0, p1_o, col_back, 4.0f); drawIconDirectLines(p0, p1_o, col_main, 1.5f); } static void drawIconCloud( const ImVec2 p0, const ImVec2 p1_o, const ImU32 col_main, const ImU32 col_back ) { std::array points {{ {0.2f, 0.9f}, {0.8f, 0.9f}, {0.9f, 0.8f}, {0.9f, 0.7f}, {0.7f, 0.7f}, {0.9f, 0.5f}, {0.9f, 0.4f}, {0.8f, 0.2f}, {0.6f, 0.2f}, {0.5f, 0.3f}, {0.5f, 0.5f}, {0.4f, 0.4f}, {0.3f, 0.4f}, {0.2f, 0.5f}, {0.2f, 0.6f}, {0.3f, 0.7f}, {0.1f, 0.7f}, {0.1f, 0.8f}, {0.2f, 0.9f}, }}; for (auto& v : points) { v.y -= 0.1f; v = {p0.x + p1_o.x*v.x, p0.y + p1_o.y*v.y}; } // the circle looks bad in light mode //ImGui::GetWindowDrawList()->AddCircleFilled({p0.x + p1_o.x*0.5f, p0.y + p1_o.y*0.5f}, p1_o.x*0.5f, col_back); ImGui::GetWindowDrawList()->AddPolyline(points.data(), points.size(), col_back, ImDrawFlags_None, 4.f); ImGui::GetWindowDrawList()->AddPolyline(points.data(), points.size(), col_main, ImDrawFlags_None, 1.5f); } static void drawIconMailLines( const ImVec2 p0, const ImVec2 p1_o, const ImU32 col, const float thickness ) { #define PLINE(x0, y0, x1, y1) \ ImGui::GetWindowDrawList()->AddLine( \ {p0.x + p1_o.x*(x0), p0.y + p1_o.y*(y0)}, \ {p0.x + p1_o.x*(x1), p0.y + p1_o.y*(y1)}, \ col, \ thickness \ ); // quad // (1,2) -> (1,8) PLINE(0.1f, 0.2f, 0.1f, 0.8f) // (1,8) -> (9,8) PLINE(0.1f, 0.8f, 0.9f, 0.8f) // (9,8) -> (9,2) PLINE(0.9f, 0.8f, 0.9f, 0.2f) // (9,2) -> (1,2) PLINE(0.9f, 0.2f, 0.1f, 0.2f) // lip // (1,2) -> (5,5) PLINE(0.1f, 0.2f, 0.5f, 0.5f) // (5,5) -> (9,2) PLINE(0.5f, 0.5f, 0.9f, 0.2f) #undef PLINE } static void drawIconMail( const ImVec2 p0, const ImVec2 p1_o, const ImU32 col_main, const ImU32 col_back ) { // dark background // the circle looks bad in light mode //ImGui::GetWindowDrawList()->AddCircleFilled({p0.x + p1_o.x*0.5f, p0.y + p1_o.y*0.5f}, p1_o.x*0.5f, col_back); drawIconMailLines(p0, p1_o, col_back, 4.0f); drawIconMailLines(p0, p1_o, col_main, 1.5f); } void renderAvatar( const Theme& th, ContactTextureCache& contact_tc, const Contact3Handle c, ImVec2 box ) { // deploy dummy of same size and check visibility const auto orig_curser_pos = ImGui::GetCursorPos(); ImGui::Dummy(box); if (ImGui::IsItemVisible()) { ImGui::SetCursorPos(orig_curser_pos); // reset for actual img ImVec4 color_current = th.getColor(); if (c.all_of()) { const auto c_state = c.get().state; if (c_state == Contact::Components::ConnectionState::State::direct) { color_current = th.getColor(); } else if (c_state == Contact::Components::ConnectionState::State::cloud) { color_current = th.getColor(); } } // avatar const auto [id, width, height] = contact_tc.get(c); ImGui::Image( id, box, {0, 0}, {1, 1}, {1, 1, 1, 1}, color_current ); } } bool renderContactBig( const Theme& th, ContactTextureCache& contact_tc, const Contact3Handle c, int line_height, const bool unread, const bool selectable, const bool selected ) { ImGui::BeginGroup(); if (line_height < 1) { line_height = 1; } // we dont need ### bc there is no named prefix auto label = "##" + std::to_string(entt::to_integral(c.entity())); const bool request_incoming = c.all_of(); const bool request_outgoing = c.all_of(); ImVec2 orig_curser_pos = ImGui::GetCursorPos(); // HACK: fake selected to make it draw a box for us const bool show_selected = request_incoming || request_outgoing || selected; if (request_incoming) { ImGui::PushStyleColor( ImGuiCol_Header, th.getColor() ); } else if (request_outgoing) { ImGui::PushStyleColor( ImGuiCol_Header, th.getColor() ); } const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); bool got_selected = false; if (selectable) { got_selected = ImGui::Selectable(label.c_str(), show_selected, ImGuiSelectableFlags_None, {0, line_height*TEXT_BASE_HEIGHT}); } else { got_selected = ImGui::InvisibleButton(label.c_str(), {-FLT_MIN, line_height*TEXT_BASE_HEIGHT}); } if (request_incoming || request_outgoing) { ImGui::PopStyleColor(); } const auto* cstate = c.try_get(); if (ImGui::BeginItemTooltip()) { if (cstate != nullptr) { ImGui::Text("Connection state: %s", (cstate->state == Contact::Components::ConnectionState::disconnected) ? "offline" : (cstate->state == Contact::Components::ConnectionState::direct) ? "online (direct)" : "online (cloud)" ); } else { ImGui::TextUnformatted("Connection state: unknown"); } if ( const auto* slt = c.try_get(); slt != nullptr && !slt->text.empty() ) { ImGui::SeparatorText("Status Text"); //ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); ImGui::TextUnformatted(slt->text.c_str()); //ImGui::PopStyleColor(); } // TODO: add a whole bunch more info ImGui::EndTooltip(); } ImVec2 post_curser_pos = ImGui::GetCursorPos(); ImVec2 img_curser { orig_curser_pos.x + ImGui::GetStyle().FramePadding.x, orig_curser_pos.y + ImGui::GetStyle().FramePadding.y }; float img_y {TEXT_BASE_HEIGHT*line_height - ImGui::GetStyle().FramePadding.y*2}; ImGui::SetCursorPos(img_curser); renderAvatar(th, contact_tc, c, {img_y, img_y}); const float same_line_spacing = ImGui::GetStyle().ItemSpacing.x*0.5f; ImGui::SameLine(0.f, same_line_spacing); ImGui::BeginGroup(); { { // line 1 if (line_height == 1 && cstate != nullptr) { // icon pos auto p0 = ImGui::GetCursorScreenPos(); p0.y += ImGui::GetStyle().FramePadding.y; ImVec2 p1_o = {img_y, img_y}; // img_y is 1 line_height in this case const ImU32 col_back = ImGui::GetColorU32(th.getColor()); if (cstate->state == Contact::Components::ConnectionState::direct) { // direct icon drawIconDirect( p0, p1_o, ImGui::GetColorU32(th.getColor()), col_back ); } else if (cstate->state == Contact::Components::ConnectionState::cloud) { // cloud icon drawIconCloud( p0, p1_o, ImGui::GetColorU32(th.getColor()), col_back ); } ImGui::Dummy(p1_o); ImGui::SameLine(0.f, same_line_spacing); } //ImGui::Text("%s%s", unread?"* ":"", (c.all_of() ? c.get().name.c_str() : "")); ImGui::TextUnformatted(c.all_of() ? c.get().name.c_str() : ""); if (unread) { ImGui::SameLine(); const float icon_size { TEXT_BASE_HEIGHT - ImGui::GetStyle().FramePadding.y*2 }; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - icon_size); // icon pos auto p0 = ImGui::GetCursorScreenPos(); //p0.y += ImGui::GetStyle().FramePadding.y; ImVec2 p1_o = {icon_size, icon_size}; drawIconMail( p0, p1_o, ImGui::GetColorU32(th.getColor()), ImGui::GetColorU32(th.getColor()) ); ImGui::Dummy(p1_o); } } // line 2 if (line_height >= 2) { if (request_incoming) { ImGui::TextUnformatted("Incoming request/invite"); } else if (request_outgoing) { ImGui::TextUnformatted("Outgoing request/invite"); } else { if (cstate != nullptr) { // icon pos auto p0 = ImGui::GetCursorScreenPos(); p0.y += ImGui::GetStyle().FramePadding.y; const float box_hight = TEXT_BASE_HEIGHT - ImGui::GetStyle().FramePadding.y*2; ImVec2 p1_o = {box_hight, box_hight}; const ImU32 col_back = ImGui::GetColorU32(th.getColor()); if (cstate->state == Contact::Components::ConnectionState::direct) { // direct icon drawIconDirect( p0, p1_o, ImGui::GetColorU32(th.getColor()), col_back ); } else if (cstate->state == Contact::Components::ConnectionState::cloud) { // cloud icon drawIconCloud( p0, p1_o, ImGui::GetColorU32(th.getColor()), col_back ); } ImGui::Dummy(p1_o); ImGui::SameLine(0.f, same_line_spacing); } if ( const auto* slt = c.try_get(); slt != nullptr && !slt->text.empty() && slt->first_line_length > 0 && slt->first_line_length <= slt->text.size() ) { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); ImGui::TextUnformatted(slt->text.c_str(), slt->text.c_str() + slt->first_line_length); ImGui::PopStyleColor(); } else { ImGui::TextDisabled(" "); // or dummy? } } // line 3 //if (line_height >= 3) { // constexpr std::string_view test_text{"text"}; // ImGui::RenderTextEllipsis(ImGui::GetWindowDrawList(), ImVec2{}, ImVec2{}, 1.f, 1.f, test_text.data(), test_text.data()+test_text.size(), nullptr); //} } } ImGui::EndGroup(); ImGui::SetCursorPos(post_curser_pos); ImGui::EndGroup(); return got_selected; }