Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AvailableMetersPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
}

if (update) {
Header_collapseLayout(this->header);
this->header->metersCopied = false;
Settings* settings = this->host->settings;
settings->changed = true;
settings->lastUpdate++;
Expand Down
24 changes: 24 additions & 0 deletions CategoriesPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ static const char* const CategoriesFunctions[] = {" ", " ", " ",

static void CategoriesPanel_delete(Object* object) {
CategoriesPanel* this = (CategoriesPanel*) object;
if (this->header->metersCopied) {
Header_undoMetersCopy(this->header);
this->header->metersCopied = false;
}
Panel_done(&this->super);
free(this);
}
Expand All @@ -45,6 +49,20 @@ static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel*));
Settings* settings = this->host->settings;

size_t hiddenMeterCount = 0;
for (size_t i = columns; i < this->header->maxColumns; i++) {
hiddenMeterCount += Vector_size(this->header->columns[i]);
}

if (hiddenMeterCount > 0) {
this->header->columns[columns - 1]->owner = false;
for (size_t i = columns; i < this->header->maxColumns; i++) {
Vector_splice(this->header->columns[columns - 1], this->header->columns[i]);
}
this->header->columns[columns - 1]->owner = true;
}
this->header->metersCopied = hiddenMeterCount > 0;

for (size_t i = 0; i < columns; i++) {
char titleBuffer[32];
xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1);
Expand Down Expand Up @@ -151,6 +169,12 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
}
if (result == HANDLED) {
int size = ScreenManager_size(this->scr);

if (this->header->metersCopied) {
Header_undoMetersCopy(this->header);
this->header->metersCopied = false;
}

for (int i = 1; i < size; i++)
ScreenManager_remove(this->scr, 1);

Expand Down
161 changes: 106 additions & 55 deletions Header.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ in the source distribution for its full text.
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
Expand All @@ -20,17 +21,20 @@ in the source distribution for its full text.
#include "CRT.h"
#include "CPUMeter.h"
#include "DynamicMeter.h"
#include "HeaderLayout.h"
#include "Macros.h"
#include "Object.h"
#include "Platform.h"
#include "ProvideCurses.h"
#include "Settings.h"
#include "Vector.h"
#include "XUtils.h"


Header* Header_new(Machine* host, HeaderLayout hLayout) {
Header* this = xCalloc(1, sizeof(Header));
this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*));
this->maxColumns = HeaderLayout_getColumns(hLayout);
this->headerLayout = hLayout;
this->host = host;

Expand All @@ -42,7 +46,7 @@ Header* Header_new(Machine* host, HeaderLayout hLayout) {
}

void Header_delete(Header* this) {
Header_forEachColumn(this, i) {
for (size_t i = 0; i < this->maxColumns; i++) {
Comment on lines -45 to +49

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Header_forEachColumn only iterates over visible columns. Since hidden columns now stay allocated in memory, Header_delete needs to free all of them up to maxColumns

Vector_delete(this->columns[i]);
}

Expand All @@ -59,24 +63,47 @@ void Header_setLayout(Header* this, HeaderLayout hLayout) {
if (newColumns == oldColumns)
return;

if (newColumns > oldColumns) {
if (newColumns > this->maxColumns) {
this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
for (size_t i = oldColumns; i < newColumns; i++)
for (size_t i = this->maxColumns; i < newColumns; i++)
this->columns[i] = Vector_new(Class(Meter), true, VECTOR_DEFAULT_SIZE);
} else {
// move meters from to-be-deleted columns into last one
for (size_t i = newColumns; i < oldColumns; i++) {
for (int j = this->columns[i]->items - 1; j >= 0; j--) {
Vector_add(this->columns[newColumns - 1], Vector_take(this->columns[i], j));
}
Vector_delete(this->columns[i]);
}
this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
this->maxColumns = newColumns;
}

Header_calculateHeight(this);
}

void Header_undoMetersCopy(Header* this) {
size_t currentColumns = HeaderLayout_getColumns(this->headerLayout);
size_t maxColumns = this->maxColumns;

size_t duplicateMeters = 0;
for (size_t i = currentColumns; i < maxColumns; i++) {
duplicateMeters += Vector_size(this->columns[i]);
}

Vector* lastColumn = this->columns[currentColumns - 1];
lastColumn->owner = false;

for (size_t j = 0; j < duplicateMeters; j++) {
Vector_remove(lastColumn, Vector_size(lastColumn) - 1);
}
lastColumn->owner = true;
}

void Header_collapseLayout(Header* this) {
size_t currentColumns = HeaderLayout_getColumns(this->headerLayout);
size_t maxColumns = this->maxColumns;

for (size_t i = currentColumns; i < maxColumns; i++) {
Vector* column = this->columns[i];
column->owner = false;
Vector_delete(column);
}
this->columns = xReallocArray(this->columns, currentColumns, sizeof(Vector*));
this->maxColumns = currentColumns;
}

static void Header_addMeterByName(Header* this, const char* name, MeterModeId mode, size_t column) {
assert(column < HeaderLayout_getColumns(this->headerLayout));

Expand Down Expand Up @@ -135,6 +162,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) {
Settings* settings = this->host->settings;
Settings_setHeaderLayout(settings, this->headerLayout);
const size_t numCols = HeaderLayout_getColumns(this->headerLayout);

Header_forEachColumn(this, col) {
MeterColumnSetting* colSettings = &settings->hColumns[col];
Expand All @@ -146,27 +174,38 @@ void Header_writeBackToSettings(const Header* this) {
}
free(colSettings->modes);

const Vector* vec = this->columns[col];
int len = Vector_size(vec);
size_t saveCol = col;
int len = 0;
do {
len += Vector_size(this->columns[saveCol++]);
} while ((col == numCols - 1) && (saveCol < this->maxColumns) && !this->metersCopied);

colSettings->names = len ? xCalloc(len + 1, sizeof(*colSettings->names)) : NULL;
colSettings->modes = len ? xCalloc(len, sizeof(*colSettings->modes)) : NULL;
colSettings->len = len;

for (int i = 0; i < len; i++) {
const Meter* meter = (Meter*) Vector_get(vec, i);
char* name = NULL;
if (meter->param && As_Meter(meter) == &DynamicMeter_class) {
const char* dynamic = DynamicMeter_lookup(settings->dynamicMeters, meter->param);
xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic);
} else if (meter->param && As_Meter(meter) == &CPUMeter_class) {
xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param);
} else {
xAsprintf(&name, "%s", As_Meter(meter)->name);
int idx = 0;
saveCol = col;
if (!len)
continue;
do {
const Vector* vec = this->columns[saveCol++];
for (int i = 0; i < Vector_size(vec); i++) {
const Meter* meter = (Meter*) Vector_get(vec, i);
char* name = NULL;
if (meter->param && As_Meter(meter) == &DynamicMeter_class) {
const char* dynamic = DynamicMeter_lookup(settings->dynamicMeters, meter->param);
xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic);
} else if (meter->param && As_Meter(meter) == &CPUMeter_class) {
xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param);
} else {
xAsprintf(&name, "%s", As_Meter(meter)->name);
}
colSettings->names[idx] = name;
colSettings->modes[idx] = meter->mode;
idx++;
}
colSettings->names[i] = name;
colSettings->modes[i] = meter->mode;
}
} while ((col == numCols - 1) && (saveCol < this->maxColumns) && !this->metersCopied);
}
}

Expand Down Expand Up @@ -204,7 +243,7 @@ void Header_draw(const Header* this) {
float roundingLoss = 0.0F;

Header_forEachColumn(this, col) {
Vector* meters = this->columns[col];
size_t drawCol = col;
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0F;

roundingLoss += colWidth - floorf(colWidth);
Expand All @@ -213,38 +252,46 @@ void Header_draw(const Header* this) {
roundingLoss -= 1.0F;
}

for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
int y = pad / 2;

float actualWidth = colWidth;
do {
Vector* meters = this->columns[drawCol++];
for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);

/* Let meters in text mode expand to the right on empty neighbors;
except for multi column meters. */
if (meter->mode == TEXT_METERMODE && !Meter_isMultiColumn(meter)) {
for (int j = 1; j < meter->columnWidthCount; j++) {
actualWidth++; /* separator column */
actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F;
float actualWidth = colWidth;

/* Let meters in text mode expand to the right on empty neighbors;
except for multi column meters. */
if (meter->mode == TEXT_METERMODE && !Meter_isMultiColumn(meter)) {
for (int j = 1; j < meter->columnWidthCount; j++) {
actualWidth++; /* separator column */
actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F;
}
}
}

assert(meter->draw);
meter->draw(meter, x, y, floorf(actualWidth));
y += meter->h;
}
assert(meter->draw);
meter->draw(meter, x, y, floorf(actualWidth));
y += meter->h;
}
} while ((col == numCols - 1) && (drawCol < this->maxColumns) && !this->metersCopied);

x += floorf(colWidth);
x++; /* separator column */
}
}

void Header_updateData(Header* this) {
const size_t numCols = HeaderLayout_getColumns(this->headerLayout);
Header_forEachColumn(this, col) {
Vector* meters = this->columns[col];
int items = Vector_size(meters);
for (int i = 0; i < items; i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
Meter_updateValues(meter);
}
size_t updCol = col;
do {
Vector* meters = this->columns[updCol++];
for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
Meter_updateValues(meter);
}
} while ((col == numCols - 1) && (updCol < this->maxColumns) && !this->metersCopied);
}
}

Expand Down Expand Up @@ -282,14 +329,18 @@ int Header_calculateHeight(Header* this) {
int maxHeight = pad;

Header_forEachColumn(this, col) {
const Vector* meters = this->columns[col];
size_t calcCol = col;
int height = pad;
for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
meter->columnWidthCount = calcColumnWidthCount(this, meter, pad, col, height);
height += meter->h;
}
maxHeight = MAXIMUM(maxHeight, height);

do {
const Vector* meters = this->columns[calcCol++];
for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i);
meter->columnWidthCount = calcColumnWidthCount(this, meter, pad, col, height);
height += meter->h;
}
maxHeight = MAXIMUM(maxHeight, height);
} while ((col == HeaderLayout_getColumns(this->headerLayout) - 1) && (calcCol < this->maxColumns) && !this->metersCopied);
}

if (maxHeight == pad) {
Expand Down
7 changes: 7 additions & 0 deletions Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include <stdbool.h>
#include <stddef.h>

#include "HeaderLayout.h"
Expand All @@ -17,6 +18,8 @@ in the source distribution for its full text.

typedef struct Header_ {
Vector** columns;
size_t maxColumns;
bool metersCopied;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Machine* host;
HeaderLayout headerLayout;
int pad;
Expand All @@ -31,6 +34,10 @@ void Header_delete(Header* this);

void Header_setLayout(Header* this, HeaderLayout hLayout);

void Header_undoMetersCopy(Header* this);

void Header_collapseLayout(Header* this);

void Header_populateFromSettings(Header* this);

void Header_writeBackToSettings(const Header* this);
Expand Down
20 changes: 16 additions & 4 deletions MetersPanel.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
int selected = Panel_getSelectedIndex(super);
HandlerResult result = IGNORED;
bool sideMove = false;
bool modified = false;

switch (ch) {
case 0x0a:
Expand Down Expand Up @@ -136,8 +137,11 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
case KEY_F(7):
case '[':
case '-':
Vector_moveUp(this->meters, selected);
Panel_moveSelectedUp(super);
if (selected > 0) {
Vector_moveUp(this->meters, selected);
Panel_moveSelectedUp(super);
modified = true;
}
result = HANDLED;
break;
case KEY_DOWN:
Expand All @@ -147,8 +151,11 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
case KEY_F(8):
case ']':
case '+':
Vector_moveDown(this->meters, selected);
Panel_moveSelectedDown(super);
if (selected + 1 < Vector_size(this->meters)) {
Vector_moveDown(this->meters, selected);
Panel_moveSelectedDown(super);
modified = true;
}
result = HANDLED;
break;
case KEY_F(6):
Expand Down Expand Up @@ -178,6 +185,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
Panel_remove(super, selected);
}
MetersPanel_setMoving(this, false);
modified = true;
result = HANDLED;
break;
case EVENT_PANEL_LOST_FOCUS:
Expand All @@ -189,6 +197,10 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {

if (result == HANDLED || sideMove) {
Header* header = this->scr->header;
if (modified || sideMove) {
Header_collapseLayout(header);
header->metersCopied = false;
}
Comment on lines +200 to +203

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Treat style changes as a real meter modification.

The new modified || sideMove gate excludes the F4/space style path even though it mutates a meter. In a reduced layout, that leaves metersCopied intact after an edit, so restoring the layout later revives the remembered hidden column assignment instead of ending the temporary-preservation window described in the PR.

Suggested fix
       case ' ':
       case KEY_F(4):
       case 't': {
          if (!Vector_size(this->meters))
             break;
          Meter* meter = (Meter*) Vector_get(this->meters, selected);
          MeterModeId mode = Meter_nextSupportedMode(meter);
          Meter_setMode(meter, mode);
          Panel_set(super, selected, (Object*) Meter_toListItem(meter, this->moving));
+         modified = true;
          result = HANDLED;
          break;
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (modified || sideMove) {
Header_collapseLayout(header);
header->metersCopied = false;
}
case ' ':
case KEY_F(4):
case 't': {
if (!Vector_size(this->meters))
break;
Meter* meter = (Meter*) Vector_get(this->meters, selected);
MeterModeId mode = Meter_nextSupportedMode(meter);
Meter_setMode(meter, mode);
Panel_set(super, selected, (Object*) Meter_toListItem(meter, this->moving));
modified = true;
result = HANDLED;
break;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@MetersPanel.c` around lines 200 - 203, The condition that sets
header->metersCopied to false currently only runs when (modified || sideMove),
which misses style-only edits (the F4/space path) that also mutate a meter;
update the logic so style-only changes are treated as a real modification—either
expand the condition to include the style-change flag used by the F4/space path
(e.g., styleChanged or whatever symbol marks style edits) or call
Header_collapseLayout(header); header->metersCopied = false unconditionally
after any code path that mutates meter style; key symbols to touch: the
conditional around Header_collapseLayout(header) and header->metersCopied
assignment, and the style-change handler invoked by F4/space.

this->settings->changed = true;
this->settings->lastUpdate++;
Header_calculateHeight(header);
Expand Down
Loading