Implement clipped scrolling of images when in page area mode
This commit is contained in:
parent
480645877a
commit
a9d31541c1
@ -268,12 +268,19 @@ take, and the default value they take when missing. All integers are 32-bit.
|
||||
|===
|
||||
|
||||
|
||||
== Interaction with screen changes
|
||||
== Interaction with other terminal actions
|
||||
|
||||
When resetting the screen, all images that are visible on the screen must be
|
||||
When resetting the terminal, all images that are visible on the screen must be
|
||||
cleared. When switching from the main screen to the alternate screen buffer
|
||||
(1049 private mode) all images in the alternate screen must be cleared, just as
|
||||
all text is cleared.
|
||||
|
||||
The commands to clear the screen and erase text must have no effect on
|
||||
graphics. The dedicated delete graphics commands must be used for those.
|
||||
|
||||
When scrolling the screen (such as when using index cursor movement commands,
|
||||
or scrolling through the history buffer), images must be scrolled along with
|
||||
text. When page margins are defined and the index commands are used, only
|
||||
images that are entirely within the page area (between the margins) must be
|
||||
scrolled. When scrolling them would cause them to extend outside the page area,
|
||||
they must be clipped.
|
||||
|
||||
@ -510,6 +510,14 @@ create_add_response(GraphicsManager UNUSED *self, const GraphicsCommand *g, bool
|
||||
if (base->array == NULL) fatal("Out of memory while ensuring space in array"); \
|
||||
}
|
||||
|
||||
static inline void
|
||||
update_src_rect(ImageRef *ref, Image *img) {
|
||||
// The src rect in OpenGL co-ords [0, 1] with origin at top-left corner of image
|
||||
ref->src_rect.left = (float)ref->src_x / (float)img->width;
|
||||
ref->src_rect.right = (float)(ref->src_x + ref->src_width) / (float)img->width;
|
||||
ref->src_rect.top = (float)ref->src_y / (float)img->height;
|
||||
ref->src_rect.bottom = (float)(ref->src_y + ref->src_height) / (float)img->height;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, bool *is_dirty, Image *img) {
|
||||
@ -535,12 +543,7 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
|
||||
ref->cell_x_offset = MIN(g->cell_x_offset, global_state.cell_width - 1);
|
||||
ref->cell_y_offset = MIN(g->cell_y_offset, global_state.cell_height - 1);
|
||||
ref->num_cols = g->num_cells; ref->num_rows = g->num_lines;
|
||||
|
||||
// The src rect in OpenGL co-ords [0, 1] with origin at top-left corner of image
|
||||
ref->src_rect.left = (float)ref->src_x / (float)img->width;
|
||||
ref->src_rect.right = (float)(ref->src_x + ref->src_width) / (float)img->width;
|
||||
ref->src_rect.top = (float)ref->src_y / (float)img->height;
|
||||
ref->src_rect.bottom = (float)(ref->src_y + ref->src_height) / (float)img->height;
|
||||
update_src_rect(ref, img);
|
||||
|
||||
// Move the cursor, the screen will take care of ensuring it is in bounds
|
||||
uint32_t num_cols = g->num_cells, num_rows = g->num_lines, t;
|
||||
@ -625,7 +628,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
|
||||
// Image lifetime/scrolling {{{
|
||||
|
||||
static inline void
|
||||
filter_refs(GraphicsManager *self, const void* data, bool (*filter_func)(ImageRef*, const void*)) {
|
||||
filter_refs(GraphicsManager *self, const void* data, bool (*filter_func)(ImageRef*, Image*, const void*)) {
|
||||
Image *img; ImageRef *ref;
|
||||
size_t i, j;
|
||||
|
||||
@ -634,7 +637,7 @@ filter_refs(GraphicsManager *self, const void* data, bool (*filter_func)(ImageRe
|
||||
img = self->images + i;
|
||||
for (j = img->refcnt; j-- > 0;) {
|
||||
ref = img->refs + j;
|
||||
if (filter_func(ref, data)) {
|
||||
if (filter_func(ref, img, data)) {
|
||||
remove_from_array(img->refs, sizeof(ImageRef), j, img->refcnt--);
|
||||
}
|
||||
}
|
||||
@ -648,21 +651,60 @@ filter_refs(GraphicsManager *self, const void* data, bool (*filter_func)(ImageRe
|
||||
}
|
||||
|
||||
static inline bool
|
||||
scroll_filter_func(ImageRef *ref, const void* data) {
|
||||
int32_t *d = (int32_t*)data, amt = d[0], limit = d[1];
|
||||
ref->start_row += amt;
|
||||
return ref->start_row + (int32_t)ref->effective_num_rows <= limit;
|
||||
}
|
||||
|
||||
void
|
||||
grman_scroll_images(GraphicsManager *self, int32_t amt, int32_t limit) {
|
||||
int32_t data[2];
|
||||
data[0] = amt; data[1] = limit;
|
||||
filter_refs(self, data, scroll_filter_func);
|
||||
scroll_filter_func(ImageRef *ref, Image UNUSED *img, const void *data) {
|
||||
ScrollData *d = (ScrollData*)data;
|
||||
ref->start_row += d->amt;
|
||||
return ref->start_row + (int32_t)ref->effective_num_rows <= d->limit;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
clear_filter_func(ImageRef *ref, const void UNUSED *data) {
|
||||
ref_within_region(ImageRef *ref, index_type margin_top, index_type margin_bottom) {
|
||||
return ref->start_row >= (int32_t)margin_top && ref->start_row + ref->effective_num_rows <= margin_bottom;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ref_outside_region(ImageRef *ref, index_type margin_top, index_type margin_bottom) {
|
||||
return ref->start_row + ref->effective_num_rows <= margin_top || ref->start_row > (int32_t)margin_bottom;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
scroll_filter_margins_func(ImageRef* ref, Image* img, const void* data) {
|
||||
ScrollData *d = (ScrollData*)data;
|
||||
if (ref_within_region(ref, d->margin_top, d->margin_bottom)) {
|
||||
ref->start_row += d->amt;
|
||||
if (ref_outside_region(ref, d->margin_top, d->margin_bottom)) return true;
|
||||
// Clip the image if scrolling has resulted in part of it being outside the page area
|
||||
uint32_t clip_amt, clipped_rows;
|
||||
if (ref->start_row < (int32_t)d->margin_top) {
|
||||
// image moved up
|
||||
clipped_rows = d->margin_top - ref->start_row;
|
||||
clip_amt = global_state.cell_height * clipped_rows;
|
||||
if (ref->src_height <= clip_amt) return true;
|
||||
ref->src_y += clip_amt; ref->src_height -= clip_amt;
|
||||
ref->effective_num_rows -= clipped_rows;
|
||||
update_src_rect(ref, img);
|
||||
ref->start_row += clipped_rows;
|
||||
} else if (ref->start_row + ref->effective_num_rows > d->margin_bottom) {
|
||||
// image moved down
|
||||
clipped_rows = ref->start_row + ref->effective_num_rows - d->margin_bottom;
|
||||
clip_amt = global_state.cell_height * clipped_rows;
|
||||
if (ref->src_height <= clip_amt) return true;
|
||||
ref->src_height -= clip_amt;
|
||||
ref->effective_num_rows -= clipped_rows;
|
||||
update_src_rect(ref, img);
|
||||
}
|
||||
return ref_outside_region(ref, d->margin_top, d->margin_bottom);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
grman_scroll_images(GraphicsManager *self, const ScrollData *data) {
|
||||
filter_refs(self, data, data->has_margins ? scroll_filter_margins_func : scroll_filter_func);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
clear_filter_func(ImageRef *ref, Image UNUSED *img, const void UNUSED *data) {
|
||||
return ref->start_row + (int32_t)ref->effective_num_rows > 0;
|
||||
}
|
||||
|
||||
|
||||
@ -75,8 +75,14 @@ typedef struct {
|
||||
PyTypeObject GraphicsManager_Type;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int32_t amt, limit;
|
||||
index_type margin_top, margin_bottom;
|
||||
bool has_margins;
|
||||
} ScrollData;
|
||||
|
||||
GraphicsManager* grman_realloc(GraphicsManager *, index_type lines, index_type columns);
|
||||
void grman_clear(GraphicsManager*);
|
||||
const char* grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty);
|
||||
bool grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float screen_left, float screen_top, float dx, float dy, unsigned int num_cols, unsigned int num_rows);
|
||||
void grman_scroll_images(GraphicsManager *self, int32_t amt, int32_t limit);
|
||||
void grman_scroll_images(GraphicsManager *self, const ScrollData*);
|
||||
|
||||
@ -653,9 +653,18 @@ screen_cursor_to_column(Screen *self, unsigned int column) {
|
||||
}
|
||||
}
|
||||
|
||||
#define INDEX_GRAPHICS(amtv) { \
|
||||
bool is_main = self->linebuf == self->main_linebuf; \
|
||||
static ScrollData s; \
|
||||
s.amt = amtv; s.limit = is_main ? -self->historybuf->ynum : 0; \
|
||||
s.has_margins = self->margin_top != 0 || self->margin_bottom != self->lines - 1; \
|
||||
s.margin_top = top; s.margin_bottom = bottom; \
|
||||
grman_scroll_images(self->grman, &s); \
|
||||
}
|
||||
|
||||
#define INDEX_UP \
|
||||
linebuf_index(self->linebuf, top, bottom); \
|
||||
grman_scroll_images(self->grman, -1, self->linebuf == self->main_linebuf ? -self->historybuf->ynum : 0); \
|
||||
INDEX_GRAPHICS(-1) \
|
||||
if (self->linebuf == self->main_linebuf && bottom == self->lines - 1) { \
|
||||
/* Only add to history when no page margins have been set */ \
|
||||
linebuf_init_line(self->linebuf, bottom); \
|
||||
@ -688,6 +697,7 @@ screen_scroll(Screen *self, unsigned int count) {
|
||||
#define INDEX_DOWN \
|
||||
linebuf_reverse_index(self->linebuf, top, bottom); \
|
||||
linebuf_clear_line(self->linebuf, top); \
|
||||
INDEX_GRAPHICS(1) \
|
||||
self->is_dirty = true;
|
||||
|
||||
void
|
||||
|
||||
@ -88,6 +88,7 @@ def put_helpers(self, cw, ch):
|
||||
cmd = 'a=T,f=24,i=%d,s=%d,v=%d,%s' % (iid, w, h, put_cmd(**kw))
|
||||
data = b'x' * w * h * 3
|
||||
send_command(screen, cmd, data)
|
||||
return iid
|
||||
|
||||
def put_ref(screen, **kw):
|
||||
cmd = 'a=p,i=%d,%s' % (iid, put_cmd(**kw))
|
||||
@ -223,14 +224,51 @@ class TestGraphics(BaseTest):
|
||||
s.index()
|
||||
self.ae(s.grman.image_count, 0)
|
||||
|
||||
# Now test with margins
|
||||
s.reset()
|
||||
# Test images outside page area untouched
|
||||
put_image(s, cw, ch) # a one cell image at (0, 0)
|
||||
for i in range(s.lines - 1):
|
||||
s.index()
|
||||
put_image(s, cw, ch) # a one cell image at (0, bottom)
|
||||
s.set_margins(2, 4) # 1-based indexing
|
||||
self.ae(s.grman.image_count, 2)
|
||||
for i in range(s.lines + s.historybuf.ynum):
|
||||
s.index()
|
||||
self.ae(s.grman.image_count, 2)
|
||||
for i in range(s.lines): # ensure cursor is at top margin
|
||||
s.reverse_index()
|
||||
# Test clipped scrolling during index
|
||||
put_image(s, cw, 2*ch, z=-1) # 1x2 cell image
|
||||
self.ae(s.grman.image_count, 3)
|
||||
self.ae(layers(s)[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})
|
||||
s.index(), s.index()
|
||||
l = layers(s)
|
||||
self.ae(len(l), 3)
|
||||
self.ae(layers(s)[0]['src_rect'], {'left': 0.0, 'top': 0.5, 'right': 1.0, 'bottom': 1.0})
|
||||
s.index()
|
||||
self.ae(s.grman.image_count, 2)
|
||||
# Test clipped scrolling during reverse_index
|
||||
for i in range(s.lines):
|
||||
s.reverse_index()
|
||||
put_image(s, cw, 2*ch, z=-1) # 1x2 cell image
|
||||
self.ae(s.grman.image_count, 3)
|
||||
self.ae(layers(s)[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 1.0})
|
||||
while s.cursor.y != 1:
|
||||
s.reverse_index()
|
||||
s.reverse_index()
|
||||
self.ae(layers(s)[0]['src_rect'], {'left': 0.0, 'top': 0.0, 'right': 1.0, 'bottom': 0.5})
|
||||
s.reverse_index()
|
||||
self.ae(s.grman.image_count, 2)
|
||||
|
||||
def test_gr_reset(self):
|
||||
cw, ch = 10, 20
|
||||
s, dx, dy, put_image, put_ref, layers, rect_eq = put_helpers(self, cw, ch)
|
||||
put_image(s, 10, 20) # a one cell image at (0, 0)
|
||||
put_image(s, cw, ch) # a one cell image at (0, 0)
|
||||
self.ae(len(layers(s)), 1)
|
||||
s.reset()
|
||||
self.ae(s.grman.image_count, 0)
|
||||
put_image(s, 10, 20) # a one cell image at (0, 0)
|
||||
put_image(s, cw, ch) # a one cell image at (0, 0)
|
||||
self.ae(s.grman.image_count, 1)
|
||||
for i in range(s.lines):
|
||||
s.index()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user