refactor(market): extract pfProjectSeries + pfRightAlignX helpers
Dedup the identical min/max→rect projection shared by pfDrawSparkline and pfDrawSparklineFilled into pfProjectSeries, and the repeated right-align cursor math (footer Close, detail Revert/Save, summary Manage button) into pfRightAlignX. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -260,38 +260,47 @@ static std::vector<std::pair<std::time_t, double>> pfChartSeries(const MarketInf
|
||||
|
||||
// A group's value trend tracks the DRGX price, so a group sparkline plots the price history
|
||||
// (normalized) across a rect. Colored by overall direction. Only meaningful for market bases.
|
||||
static void pfDrawSparkline(ImDrawList* dl, ImVec2 mn, ImVec2 mx,
|
||||
const std::vector<double>& hist, ImU32 col)
|
||||
// Normalize a numeric series into evenly-spaced screen points within [mn, mx], y scaled to the
|
||||
// series min..max. Returns false (leaving `out` untouched) when there are fewer than 2 points.
|
||||
static bool pfProjectSeries(const std::vector<double>& hist, ImVec2 mn, ImVec2 mx,
|
||||
std::vector<ImVec2>& out)
|
||||
{
|
||||
int n = (int)hist.size();
|
||||
if (n < 2) return;
|
||||
if (n < 2) return false;
|
||||
double lo = *std::min_element(hist.begin(), hist.end());
|
||||
double hi = *std::max_element(hist.begin(), hist.end());
|
||||
if (hi <= lo) hi = lo + 1e-9;
|
||||
float w = mx.x - mn.x, h = mx.y - mn.y;
|
||||
std::vector<ImVec2> pts(n);
|
||||
out.resize(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
float t = (n > 1) ? (float)i / (float)(n - 1) : 0.0f;
|
||||
pts[i] = ImVec2(mn.x + t * w, mx.y - (float)((hist[i] - lo) / (hi - lo)) * h);
|
||||
float t = (float)i / (float)(n - 1);
|
||||
out[i] = ImVec2(mn.x + t * w, mx.y - (float)((hist[i] - lo) / (hi - lo)) * h);
|
||||
}
|
||||
dl->AddPolyline(pts.data(), n, col, ImDrawFlags_None, 1.2f);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Right-align the next item(s) of total width `w` within the remaining content region (cursor form).
|
||||
static void pfRightAlignX(float w)
|
||||
{
|
||||
float avail = ImGui::GetContentRegionAvail().x;
|
||||
if (avail > w) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + avail - w);
|
||||
}
|
||||
|
||||
static void pfDrawSparkline(ImDrawList* dl, ImVec2 mn, ImVec2 mx,
|
||||
const std::vector<double>& hist, ImU32 col)
|
||||
{
|
||||
std::vector<ImVec2> pts;
|
||||
if (!pfProjectSeries(hist, mn, mx, pts)) return;
|
||||
dl->AddPolyline(pts.data(), (int)pts.size(), col, ImDrawFlags_None, 1.2f);
|
||||
}
|
||||
|
||||
// Sparkline with a soft fill under the line — for the card's dedicated bottom strip.
|
||||
static void pfDrawSparklineFilled(ImDrawList* dl, ImVec2 mn, ImVec2 mx,
|
||||
const std::vector<double>& hist, ImU32 lineCol)
|
||||
{
|
||||
int n = (int)hist.size();
|
||||
if (n < 2) return;
|
||||
double lo = *std::min_element(hist.begin(), hist.end());
|
||||
double hi = *std::max_element(hist.begin(), hist.end());
|
||||
if (hi <= lo) hi = lo + 1e-9;
|
||||
float w = mx.x - mn.x, h = mx.y - mn.y;
|
||||
std::vector<ImVec2> pts(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
float t = (n > 1) ? (float)i / (float)(n - 1) : 0.0f;
|
||||
pts[i] = ImVec2(mn.x + t * w, mx.y - (float)((hist[i] - lo) / (hi - lo)) * h);
|
||||
}
|
||||
std::vector<ImVec2> pts;
|
||||
if (!pfProjectSeries(hist, mn, mx, pts)) return;
|
||||
int n = (int)pts.size();
|
||||
for (int i = 0; i < n; i++) dl->PathLineTo(pts[i]);
|
||||
dl->PathLineTo(ImVec2(pts[n - 1].x, mx.y));
|
||||
dl->PathLineTo(ImVec2(pts[0].x, mx.y));
|
||||
@@ -1138,8 +1147,7 @@ static void RenderPortfolioEditor(App* app)
|
||||
{
|
||||
float bw = 104.0f * dp, sp = Layout::spacingSm();
|
||||
float grp = bw * 2 + sp;
|
||||
float availD = ImGui::GetContentRegionAvail().x;
|
||||
if (availD > grp) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + availD - grp);
|
||||
pfRightAlignX(grp);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + Layout::spacingSm());
|
||||
ImGui::BeginDisabled(!selDirty);
|
||||
if (ImGui::Button(TR("portfolio_revert"), ImVec2(bw, addH)))
|
||||
@@ -1156,8 +1164,7 @@ static void RenderPortfolioEditor(App* app)
|
||||
// ================= FOOTER: close (modal-level action, larger → higher hierarchy) =================
|
||||
{
|
||||
float bh = 40.0f * dp, bw = 120.0f * dp;
|
||||
float availF = ImGui::GetContentRegionAvail().x;
|
||||
if (availF > bw) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + availF - bw);
|
||||
pfRightAlignX(bw);
|
||||
if (ImGui::Button(TR("portfolio_close"), ImVec2(bw, bh))) { commitIfNeeded(); s_portfolio_editor_open = false; }
|
||||
}
|
||||
|
||||
@@ -1804,8 +1811,7 @@ void RenderMarketTab(App* app)
|
||||
const char* ml = TR("portfolio_manage");
|
||||
float mBtnW = body2->CalcTextSizeA(body2->LegacySize, FLT_MAX, 0, ml).x + Layout::spacingMd() * 2;
|
||||
ImGui::SameLine();
|
||||
float availX = ImGui::GetContentRegionAvail().x;
|
||||
if (availX > mBtnW) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + availX - mBtnW);
|
||||
pfRightAlignX(mBtnW);
|
||||
if (ImGui::Button(ml, ImVec2(mBtnW, 0))) {
|
||||
// Open the combined editor selecting the first group (or the empty state if none).
|
||||
PortfolioBeginEdit(app, app->settings()->getPortfolioEntries().empty() ? -1 : 0);
|
||||
|
||||
Reference in New Issue
Block a user