stack: Interactive stack menu

Add menu functions for the interactive stack.

Compared to the HP implementation, the interactive stack that was
implemented here adds stack sort and revert features.

Fixes: #1011

Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
This commit is contained in:
Christophe de Dinechin 2024-07-12 02:11:57 +02:00
parent 85d8599ccc
commit 45ef03c4e2
9 changed files with 245 additions and 49 deletions

View file

@ -598,14 +598,24 @@ COMMAND_BODY(Show)
// Show the top-level of the stack graphically, using entire screen
// ----------------------------------------------------------------------------
{
if (object_g obj = rt.top())
object_g obj = rt.top();
return show(obj);
}
object::result show(object_r obj)
// ----------------------------------------------------------------------------
// Draw an obejct
// ----------------------------------------------------------------------------
{
if (obj)
{
grob_g graph = obj->graph();
if (!graph)
{
if (!rt.error())
rt.graph_does_not_fit_error();
return ERROR;
return object::ERROR;
}
ui.draw_graphics();
@ -728,7 +738,7 @@ COMMAND_BODY(Show)
}
redraw_lcd(true);
}
return OK;
return object::OK;
}

View file

@ -115,6 +115,12 @@ inline uint ScreenHeight()
}
object::result show(object_r obj);
// ----------------------------------------------------------------------------
// Show the given object full screen
// ----------------------------------------------------------------------------
COMMAND_DECLARE(Disp,2);
COMMAND_DECLARE(DispXY,3);
COMMAND_DECLARE(Show,1);

View file

@ -807,6 +807,29 @@ HELP_BODY(list)
//
// ============================================================================
object::result to_list(uint depth)
// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------
{
scribble scr;
for (uint i = 0; i < depth; i++)
{
if (object_g obj = rt.stack(depth - 1 - i))
{
size_t objsz = obj->size();
byte_p objp = byte_p(obj);
if (!rt.append(objsz, objp))
return object::ERROR;
}
}
object_g list = list::make(scr.scratch(), scr.growth());
if (rt.drop(depth) && rt.push(list))
return object::OK;
return object::ERROR;
}
COMMAND_BODY(ToList)
// ----------------------------------------------------------------------------
// Convert elements to a list
@ -819,24 +842,7 @@ COMMAND_BODY(ToList)
return ERROR;
if (rt.pop())
{
scribble scr;
for (uint i = 0; i < depth; i++)
{
if (object_g obj = rt.stack(depth - 1 - i))
{
size_t objsz = obj->size();
byte_p objp = byte_p(obj);
if (!rt.append(objsz, objp))
return ERROR;
}
}
object_g list = list::make(scr.scratch(), scr.growth());
if (!rt.drop(depth))
return ERROR;
if (rt.push(list))
return OK;
}
return to_list(depth);
}
return ERROR;
}
@ -1576,7 +1582,7 @@ static int memory_compare(object_p *xp, object_p *yp)
}
static int value_compare(object_p *xp, object_p *yp)
int value_compare(object_p *xp, object_p *yp)
// ----------------------------------------------------------------------------
// Sort items according to value
// ----------------------------------------------------------------------------

View file

@ -355,4 +355,18 @@ inline list_g operator*(list_r x, uint y)
return list_p(+(xt * y));
}
object::result to_list(uint depth);
// ----------------------------------------------------------------------------
// Convert `depth` items to a list
// ----------------------------------------------------------------------------
int value_compare(object_p *xp, object_p *yp);
// ----------------------------------------------------------------------------
// Value comparison for sorting
// ----------------------------------------------------------------------------
#endif // LIST_H

View file

@ -438,7 +438,7 @@ size_t object::render(char *output, size_t length) const
}
cstring object::edit() const
size_t object::edit() const
// ----------------------------------------------------------------------------
// Render an object into the scratchpad, then move it into editor
// ----------------------------------------------------------------------------
@ -453,7 +453,7 @@ cstring object::edit() const
rt.edit();
r.clear();
}
return (cstring) rt.editor();
return size;
}

View file

@ -374,7 +374,7 @@ struct object
// ------------------------------------------------------------------------
cstring edit() const;
size_t edit() const;
// ------------------------------------------------------------------------
// Render the object into the scratchpad, then move into the editor
// ------------------------------------------------------------------------

View file

@ -52,7 +52,7 @@ stack::stack()
// ----------------------------------------------------------------------------
// Constructor does nothing at the moment
// ----------------------------------------------------------------------------
: interactive(0)
: interactive(0), interactive_base(0)
#if SIMULATOR
, history(), writer(0), reader(0)
#endif // SIMULATOR
@ -107,6 +107,19 @@ uint stack::draw_stack()
if (!depth)
return bottom;
if (interactive)
{
uint height = rt.editing() ? 3 : 4;
if (interactive < interactive_base + 1)
interactive_base = interactive - 1;
else if (interactive > interactive_base + height)
interactive_base = interactive - height;
}
else
{
interactive_base = 0;
}
rect clip = Screen.clip();
Screen.fill(0, top, hdrx-1, bottom, Settings.StackLevelBackground());
Screen.fill(hdrx, top, hdrx, bottom, Settings.StackLineForeground());
@ -114,7 +127,7 @@ uint stack::draw_stack()
char buf[16];
coord y = bottom;
coord yresult = y;
for (uint level = 0; level < depth; level++)
for (uint level = interactive_base; level < depth; level++)
{
if (coord(y) <= top)
break;

View file

@ -45,6 +45,7 @@ struct stack
uint draw_stack();
uint interactive;
uint interactive_base;
#if SIMULATOR
public:

View file

@ -35,6 +35,7 @@
#include "dmcp.h"
#include "expression.h"
#include "functions.h"
#include "graphics.h"
#include "grob.h"
#include "list.h"
#include "menu.h"
@ -1208,6 +1209,10 @@ uint user_interface::menu_planes()
{
planes = 1;
}
else if (Stack.interactive)
{
planes = 2;
}
else
{
while (planes > 0)
@ -1376,6 +1381,15 @@ bool user_interface::draw_menus()
};
labels = helpMenu;
}
else if (Stack.interactive)
{
static cstring stackMenu[] =
{
"Edit", "Show", "Level", "Roll↓", "Pick", "→List",
"DupN", "DropN", "Keep", "Roll↑", "Sort", "Revert"
};
labels = stackMenu + 6 * plane;
}
if (single)
if (plane != shplane)
@ -2124,8 +2138,10 @@ reposition:
// Draw the area that fits on the screen
int lineHeight = font->height();
int errorHeight = rt.error() ? LCD_H / 3 + 10 : 0;
int top = HeaderFont->height() + errorHeight + 2;
int bottom = LCD_H-1 - menuHeight;
int top = (Stack.interactive
? bottom - HeaderFont->height()
: HeaderFont->height() + errorHeight + 2);
int availableHeight = bottom - top;
int fullRows = availableHeight / lineHeight;
int clippedRows = (availableHeight + lineHeight - 1) / lineHeight;
@ -3639,25 +3655,152 @@ bool user_interface::handle_editing(int key)
bool consumed = false;
size_t editing = rt.editing();
if (Stack.interactive && !shift && !xshift && rt.depth())
if (uint interactive = Stack.interactive)
{
switch (key)
if (shift)
{
case KEY_UP:
if (++Stack.interactive > rt.depth())
Stack.interactive = rt.depth();
dirtyStack = true;
return true;
case KEY_DOWN:
if (--Stack.interactive == 0)
Stack.interactive = 1;
dirtyStack = true;
return true;
case KEY_ENTER:
case KEY_EXIT:
Stack.interactive = 0;
dirtyStack = true;
return true;
switch (key)
{
case KEY_UP:
interactive += 4;
if (interactive > rt.depth())
interactive = rt.depth();
Stack.interactive = interactive;
dirtyStack = true;
return true;
case KEY_DOWN:
if (interactive <= 4)
interactive = 1;
else
interactive -= 4;
Stack.interactive = interactive;
dirtyStack = true;
return true;
case KEY_ENTER:
case KEY_EXIT:
Stack.interactive = 0;
dirtyStack = true;
dirtyMenu = true;
return true;
case KEY_BSP:
rt.roll(interactive);
rt.drop();
if (interactive > rt.depth())
Stack.interactive = rt.depth();
dirtyStack = true;
return true;
case KEY_F1: // DupN
for (uint i = 0; i < interactive; i++)
if (object_p obj = rt.stack(interactive - 1))
if (!rt.push(obj))
break;
dirtyStack = true;
return true;
case KEY_F2: // DropN
rt.drop(interactive);
dirtyStack = true;
return true;
case KEY_F3: // Keep
if (rt.depth() > interactive)
{
size_t depth = rt.depth();
for (uint i = 0; i < interactive; i++)
rt.stack(depth + ~i, rt.stack(interactive + ~i));
rt.drop(depth - interactive);
dirtyStack = true;
}
return true;
case KEY_F4: // Roll
rt.roll(interactive);
dirtyStack = true;
return true;
case KEY_F5: // Sort
typedef int (*qsort_fn)(const void *, const void*);
qsort(rt.stack_base(), interactive,
sizeof(object_p), qsort_fn(value_compare));
dirtyStack = true;
return true;
case KEY_F6: // Revert
for (uint i = 0; i < interactive / 2; i++)
{
object_p a = rt.stack(i);
object_p b = rt.stack(interactive + ~i);
rt.stack(i, b);
rt.stack(interactive + ~i, a);
}
dirtyStack = true;
return true;
default:
break;
}
}
else if (!xshift)
{
switch (key)
{
case KEY_UP:
if (++interactive > rt.depth())
interactive = rt.depth();
Stack.interactive = interactive;
dirtyStack = true;
return true;
case KEY_DOWN:
if (--interactive == 0)
interactive = 1;
Stack.interactive = interactive;
dirtyStack = true;
return true;
case KEY_ENTER:
case KEY_EXIT:
Stack.interactive = 0;
dirtyStack = true;
dirtyMenu = true;
return true;
case KEY_BSP:
rt.roll(interactive);
rt.drop();
if (interactive > rt.depth())
Stack.interactive = rt.depth();
dirtyStack = true;
return true;
case KEY_F1: // Edit
if (object_p obj = rt.stack(interactive - 1))
{
this->editing = obj;
size_t sz = obj->edit();
cursor += sz;
edit(unicode(' '), PROGRAM, false);
edRows = 0;
dirtyEditor = true;
}
return true;
case KEY_F2: // Show
if (object_g obj = rt.stack(interactive - 1))
show(obj);
return true;
case KEY_F3: // Level
if (integer_p depth = integer::make(interactive))
{
rt.push(depth);
dirtyStack = true;
}
return true;
case KEY_F4: // Roll down
rt.rolld(interactive);
dirtyStack = true;
return true;
case KEY_F5: // Pick
if (object_p obj = rt.stack(interactive - 1))
rt.push(obj);
dirtyStack = true;
return true;
case KEY_F6: // To List
to_list(interactive);
dirtyStack = true;
return true;
default:
break;
}
}
}
@ -3904,7 +4047,6 @@ bool user_interface::handle_editing(int key)
case 0:
return false;
}
}
else
{
@ -3949,9 +4091,13 @@ bool user_interface::handle_editing(int key)
}
else if (!shift)
{
if (++Stack.interactive > rt.depth())
Stack.interactive = rt.depth();
dirtyStack = true;
if (rt.args(rt.depth()))
{
if (++Stack.interactive > rt.depth())
Stack.interactive = rt.depth();
dirtyStack = true;
dirtyMenu = true;
}
return true;
}
break;