fix(ui): frost the chat + contacts panes with acrylic blur + padding
The chat and contacts panes drew flat ImGui fills (ChildBg / default WindowBg / NoBackground), so they never sampled the acrylic blur and showed the raw backdrop texture. Route them through DrawGlassPanel -- the pattern the peers_tab sibling and every other tab already use: - chat: frost the conversation-list + thread panes and the composer input box, and add inner padding so content doesn't hug the glass edges - contacts: frost the card/list container AND the table view, padding both Padding needed ImGuiChildFlags_AlwaysUseWindowPadding -- a borderless child silently ignores WindowPadding otherwise (documented gotcha in daemon_download_dialog.h). BeginTable can't take that flag, so the table view is inset within its glass panel instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -557,9 +557,20 @@ void RenderChatTab(App* app)
|
|||||||
const float metaSz = scaledSize(metaFont);
|
const float metaSz = scaledSize(metaFont);
|
||||||
|
|
||||||
// ---- Left: new-conversation button + conversation list ----
|
// ---- Left: new-conversation button + conversation list ----
|
||||||
// Faint sidebar tint so the list reads as distinct from the thread pane (V6).
|
// Frosted-glass pane so the list blurs the backdrop like the rest of the app (the contacts_tab
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(material::WithAlpha(material::OnSurface(), 10)));
|
// pattern): draw the glass behind the child, then give the child a TRANSPARENT bg. A flat ChildBg
|
||||||
ImGui::BeginChild("##ChatList", ImVec2(listW, avail.y), ImGuiChildFlags_Borders,
|
// tint (the old approach) never samples the acrylic blur, so the raw texture showed through.
|
||||||
|
{
|
||||||
|
ImDrawList* paneDL = ImGui::GetWindowDrawList();
|
||||||
|
const ImVec2 pMin = ImGui::GetCursorScreenPos();
|
||||||
|
material::GlassPanelSpec g; g.rounding = 12.0f * Layout::dpiScale(); g.fillAlpha = 20; g.borderAlpha = 34;
|
||||||
|
material::DrawGlassPanel(paneDL, pMin, ImVec2(pMin.x + listW, pMin.y + avail.y), g);
|
||||||
|
}
|
||||||
|
// Inner padding so the list content (buttons, search, conversation cards) doesn't hug the glass
|
||||||
|
// pane's edges now that it's a visible frosted card.
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.0f * Layout::dpiScale(), 10.0f * Layout::dpiScale()));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); // transparent — the frosted glass shows through
|
||||||
|
ImGui::BeginChild("##ChatList", ImVec2(listW, avail.y), ImGuiChildFlags_AlwaysUseWindowPadding,
|
||||||
ImGuiWindowFlags_NoScrollWithMouse);
|
ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
material::ApplySmoothScroll(); // wheel-driven lerp scroll, matching the rest of the app
|
material::ApplySmoothScroll(); // wheel-driven lerp scroll, matching the rest of the app
|
||||||
if (s_show_emoji_picker) {
|
if (s_show_emoji_picker) {
|
||||||
@@ -683,6 +694,7 @@ void RenderChatTab(App* app)
|
|||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::PopStyleColor(); // list ChildBg tint (V6)
|
ImGui::PopStyleColor(); // list ChildBg tint (V6)
|
||||||
|
ImGui::PopStyleVar(); // list inner WindowPadding
|
||||||
|
|
||||||
ImGui::SameLine(0.0f, 10.0f * Layout::dpiScale()); // breathing gutter between the list and thread panes
|
ImGui::SameLine(0.0f, 10.0f * Layout::dpiScale()); // breathing gutter between the list and thread panes
|
||||||
|
|
||||||
@@ -699,7 +711,21 @@ void RenderChatTab(App* app)
|
|||||||
// the box doesn't jump when a reply arrives.
|
// the box doesn't jump when a reply arrives.
|
||||||
const float composerAreaH = sel ? (composerBoxH + metaSz + 12.0f * tdp) : 0.0f;
|
const float composerAreaH = sel ? (composerBoxH + metaSz + 12.0f * tdp) : 0.0f;
|
||||||
|
|
||||||
ImGui::BeginChild("##ChatThread", ImVec2(0, avail.y - composerAreaH), true);
|
// Frosted-glass thread pane (same reasoning as the list): glass behind + transparent child bg so
|
||||||
|
// the message area blurs the backdrop instead of showing the sharp texture. Replaces the old
|
||||||
|
// bordered child (which drew the flat, translucent WindowBg) — the glass supplies the border.
|
||||||
|
{
|
||||||
|
ImDrawList* paneDL = ImGui::GetWindowDrawList();
|
||||||
|
const ImVec2 pMin = ImGui::GetCursorScreenPos();
|
||||||
|
const float pW = ImGui::GetContentRegionAvail().x;
|
||||||
|
material::GlassPanelSpec g; g.rounding = 12.0f * tdp; g.fillAlpha = 12; g.borderAlpha = 34;
|
||||||
|
material::DrawGlassPanel(paneDL, pMin, ImVec2(pMin.x + pW, pMin.y + (avail.y - composerAreaH)), g);
|
||||||
|
}
|
||||||
|
// Inner padding so the header + messages don't hug the glass card's edges (the message child
|
||||||
|
// below trims its own inset to compensate so bubbles aren't double-indented).
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.0f * tdp, 10.0f * tdp));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); // transparent — frosted glass shows through
|
||||||
|
ImGui::BeginChild("##ChatThread", ImVec2(0, avail.y - composerAreaH), ImGuiChildFlags_AlwaysUseWindowPadding);
|
||||||
{
|
{
|
||||||
if (sel) {
|
if (sel) {
|
||||||
app->markChatConversationSeen(sel->cid, sel->lastTs); // viewing the thread clears its unread (Q1)
|
app->markChatConversationSeen(sel->cid, sel->lastTs); // viewing the thread clears its unread (Q1)
|
||||||
@@ -942,14 +968,15 @@ void RenderChatTab(App* app)
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
const float dp = Layout::dpiScale();
|
const float dp = Layout::dpiScale();
|
||||||
// Inset the message list from the pane edges so bubbles don't hug the border/scrollbar (8px).
|
// Small message-list inset (the ##ChatThread WindowPadding above already holds content off
|
||||||
|
// the glass edge; keep a little here so bubbles don't hug the scrollbar).
|
||||||
// Zero the VERTICAL item spacing: the message loop reserves its own gaps via Dummy() (tight
|
// Zero the VERTICAL item spacing: the message loop reserves its own gaps via Dummy() (tight
|
||||||
// within a run so the grouped/merged corners read right), so the theme's 6px would double them.
|
// within a run so the grouped/merged corners read right), so the theme's 6px would double them.
|
||||||
const float origSpacingX = ImGui::GetStyle().ItemSpacing.x;
|
const float origSpacingX = ImGui::GetStyle().ItemSpacing.x;
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0f * dp, 8.0f * dp));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.0f * dp, 8.0f * dp));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(origSpacingX, 0.0f));
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(origSpacingX, 0.0f));
|
||||||
ImGui::BeginChild("##ChatMessages", ImVec2(0, ImGui::GetContentRegionAvail().y),
|
ImGui::BeginChild("##ChatMessages", ImVec2(0, ImGui::GetContentRegionAvail().y),
|
||||||
ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollWithMouse);
|
ImGuiChildFlags_AlwaysUseWindowPadding, ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
material::ApplySmoothScroll(); // smooth wheel scroll; syncs with the auto-scroll-to-bottom below
|
material::ApplySmoothScroll(); // smooth wheel scroll; syncs with the auto-scroll-to-bottom below
|
||||||
bool atBottom = true;
|
bool atBottom = true;
|
||||||
const ImVec2 msgWinMin = ImGui::GetWindowPos();
|
const ImVec2 msgWinMin = ImGui::GetWindowPos();
|
||||||
@@ -1148,6 +1175,8 @@ void RenderChatTab(App* app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
ImGui::PopStyleColor(); // ##ChatThread transparent ChildBg (GetItemRect below still reads the child)
|
||||||
|
ImGui::PopStyleVar(); // ##ChatThread inner WindowPadding
|
||||||
|
|
||||||
// ── Composer — OUTSIDE / below the bordered message box, spanning its width. No divider above it.
|
// ── Composer — OUTSIDE / below the bordered message box, spanning its width. No divider above it.
|
||||||
// Layout (all placed by absolute screen pos so nothing wraps to the window's left edge): a thin
|
// Layout (all placed by absolute screen pos so nothing wraps to the window's left edge): a thin
|
||||||
@@ -1204,7 +1233,17 @@ void RenderChatTab(App* app)
|
|||||||
s_emoji_search[0] = '\0';
|
s_emoji_search[0] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Input.
|
// Input — frosted so it blurs the backdrop like the panes above (glass behind + transparent
|
||||||
|
// FrameBg); a flat FrameBg showed the sharp texture.
|
||||||
|
{
|
||||||
|
ImDrawList* cdl = ImGui::GetWindowDrawList();
|
||||||
|
material::GlassPanelSpec g; g.rounding = 8.0f * tdp; g.fillAlpha = 16; g.borderAlpha = 34;
|
||||||
|
material::DrawGlassPanel(cdl, ImVec2(inputX, rowY),
|
||||||
|
ImVec2(inputX + inputW, rowY + composerBoxH), g);
|
||||||
|
}
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(0, 0, 0, 0));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, IM_COL32(0, 0, 0, 0));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, IM_COL32(0, 0, 0, 0));
|
||||||
ImGui::SetCursorScreenPos(ImVec2(inputX, rowY));
|
ImGui::SetCursorScreenPos(ImVec2(inputX, rowY));
|
||||||
// Enter-to-send (setting): on → Enter sends + Ctrl+Enter newline; off → Enter is a newline and
|
// Enter-to-send (setting): on → Enter sends + Ctrl+Enter newline; off → Enter is a newline and
|
||||||
// the Send button is the only way to send. NOTE: without EnterReturnsTrue, InputTextMultiline
|
// the Send button is the only way to send. NOTE: without EnterReturnsTrue, InputTextMultiline
|
||||||
@@ -1216,6 +1255,7 @@ void RenderChatTab(App* app)
|
|||||||
: ImGuiInputTextFlags_None;
|
: ImGuiInputTextFlags_None;
|
||||||
const bool inputReturned = ImGui::InputTextMultiline("##compose", s_compose, sizeof(s_compose),
|
const bool inputReturned = ImGui::InputTextMultiline("##compose", s_compose, sizeof(s_compose),
|
||||||
ImVec2(inputW, composerBoxH), composeFlags);
|
ImVec2(inputW, composerBoxH), composeFlags);
|
||||||
|
ImGui::PopStyleColor(3); // composer FrameBg (transparent — glass shows through)
|
||||||
bool submit = enterSends && inputReturned; // off-mode sends only via the Send button
|
bool submit = enterSends && inputReturned; // off-mode sends only via the Send button
|
||||||
// Send.
|
// Send.
|
||||||
ImGui::SetCursorScreenPos(ImVec2(sendX, rowY));
|
ImGui::SetCursorScreenPos(ImVec2(sendX, rowY));
|
||||||
|
|||||||
@@ -1005,9 +1005,23 @@ void RenderContactsTab(App* app)
|
|||||||
|
|
||||||
if (viewMode == 2) {
|
if (viewMode == 2) {
|
||||||
// ── TABLE mode (material-ized): no outer/grid borders, row backgrounds, interactive sort. ──
|
// ── TABLE mode (material-ized): no outer/grid borders, row backgrounds, interactive sort. ──
|
||||||
|
// Frosted-glass panel behind the table so it blurs the backdrop like the card/list view (the
|
||||||
|
// table's translucent row backgrounds let the blur show through). A BeginTable can't take
|
||||||
|
// AlwaysUseWindowPadding, so inset the table itself inside the glass so its headers/cells
|
||||||
|
// don't hug the container edges.
|
||||||
|
ImDrawList* tpdl = ImGui::GetWindowDrawList();
|
||||||
|
const ImVec2 tpMin = ImGui::GetCursorScreenPos();
|
||||||
|
const float tpW = ImGui::GetContentRegionAvail().x;
|
||||||
|
const float tIpad = 12.0f * dp, tVpad = 10.0f * dp;
|
||||||
|
{
|
||||||
|
material::GlassPanelSpec g; g.rounding = 12.0f * dp; g.fillAlpha = 14; g.borderAlpha = 34;
|
||||||
|
material::DrawGlassPanel(tpdl, tpMin, ImVec2(tpMin.x + tpW, tpMin.y + listH), g);
|
||||||
|
}
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(tpMin.x + tIpad, tpMin.y + tVpad));
|
||||||
if (ImGui::BeginTable("AddressBookTable", 3,
|
if (ImGui::BeginTable("AddressBookTable", 3,
|
||||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable |
|
ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable |
|
||||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY, ImVec2(0, listH)))
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY,
|
||||||
|
ImVec2(tpW - 2.0f * tIpad, listH - 2.0f * tVpad)))
|
||||||
{
|
{
|
||||||
ImGui::TableSetupColumn(TR("label"), ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultSort, 1.5f);
|
ImGui::TableSetupColumn(TR("label"), ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultSort, 1.5f);
|
||||||
ImGui::TableSetupColumn(TR("address_label"), ImGuiTableColumnFlags_WidthStretch, 2.6f);
|
ImGui::TableSetupColumn(TR("address_label"), ImGuiTableColumnFlags_WidthStretch, 2.6f);
|
||||||
@@ -1093,8 +1107,20 @@ void RenderContactsTab(App* app)
|
|||||||
return toLower(entries[a].label).compare(toLower(entries[b].label)) < 0;
|
return toLower(entries[a].label).compare(toLower(entries[b].label)) < 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Frosted-glass list container so the contacts area blurs the backdrop (peers_tab pattern:
|
||||||
|
// glass behind a NoBackground list child) instead of showing the raw texture through the pane.
|
||||||
|
{
|
||||||
|
ImDrawList* pdl = ImGui::GetWindowDrawList();
|
||||||
|
const ImVec2 pMin = ImGui::GetCursorScreenPos();
|
||||||
|
const float pW = ImGui::GetContentRegionAvail().x;
|
||||||
|
material::GlassPanelSpec g; g.rounding = 12.0f * dp; g.fillAlpha = 14; g.borderAlpha = 34;
|
||||||
|
material::DrawGlassPanel(pdl, pMin, ImVec2(pMin.x + pW, pMin.y + listH), g);
|
||||||
|
}
|
||||||
|
// AlwaysUseWindowPadding: a borderless child ignores WindowPadding without it, so the rows
|
||||||
|
// would hug the frosted container's edges.
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.0f * dp, 10.0f * dp));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0,0,0,0));
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0,0,0,0));
|
||||||
ImGui::BeginChild("##contactList", ImVec2(0, listH), false,
|
ImGui::BeginChild("##contactList", ImVec2(0, listH), ImGuiChildFlags_AlwaysUseWindowPadding,
|
||||||
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollWithMouse);
|
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
material::ApplySmoothScroll(); // wheel-lerp scroll, matching the rest of the app
|
material::ApplySmoothScroll(); // wheel-lerp scroll, matching the rest of the app
|
||||||
if (visibleRows.empty()) {
|
if (visibleRows.empty()) {
|
||||||
@@ -1243,6 +1269,7 @@ void RenderContactsTab(App* app)
|
|||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopStyleVar(); // ##contactList inner WindowPadding
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared right-click context menu (opened by either view's row right-click; acts on the selection).
|
// Shared right-click context menu (opened by either view's row right-click; acts on the selection).
|
||||||
|
|||||||
Reference in New Issue
Block a user