diff options
| -rw-r--r-- | docs/design/008-module-format.md | 5 | ||||
| -rw-r--r-- | prototypes/connections/module.c | 20 | ||||
| -rw-r--r-- | prototypes/connections/module.h | 7 | ||||
| -rw-r--r-- | prototypes/connections/test-module.c | 32 |
4 files changed, 63 insertions, 1 deletions
diff --git a/docs/design/008-module-format.md b/docs/design/008-module-format.md index 2ee4bec..6d1b32e 100644 --- a/docs/design/008-module-format.md +++ b/docs/design/008-module-format.md @@ -151,6 +151,11 @@ text·리스트가 남을 때만 위치를 유지한다. 응답은 `pure_app_reply`로 순서대로 빌려준다. 페이로드와 문서의 익스텐트를 복사하지 않는다. 문서를 고친 이벤트의 문서 응답과 text 상태의 응답은 확정 전에 복사한다. text 보기는 `pure_app_view_text`로 빌려준다. +호스트는 인덱스 상태를 한 항목으로 옮길 수 있다(`pure_app_point`, 사용자가 행을 가리키는 것). 핸들러 없는 이벤트이며 +키 이벤트처럼 저장되고, 마지막 항목을 넘으면 `not_found`다. 어느 인덱스를 옮길지는 보기가 알려 준다. +`pure_module_position`은 수 보기가 인덱스 상태의 위치 그대로(`view tasks_focus = at.position`)이면 그 상태를 돌려준다. +그래서 인터페이스(Vega)는 앱 코드 없이 목록의 클릭을 포커스로 옮긴다. + 표 보기는 리스트 항목마다 행 하나를 가진다(`pure_app_rows`). 호스트는 보여 줄 칸만 `pure_app_cell`·`pure_app_cell_text`로 행 번호와 함께 계산한다. 화면 모델을 따로 만들거나 목록을 복사하지 않으므로, 10만 항목 목록에서 20줄을 그리는 표면은 20행만 계산한다(가상 스크롤이 런타임의 성질이 된다). 보기가 실패해도 diff --git a/prototypes/connections/module.c b/prototypes/connections/module.c index ebb746f..5e2b98f 100644 --- a/prototypes/connections/module.c +++ b/prototypes/connections/module.c @@ -522,6 +522,13 @@ int pure_module_view(const PureModule *m, const char *name) { return -1; } +int pure_module_position(const PureModule *m, uint32_t view) { + if (view >= m->views || m->view_type[view] != PURE_VIEW_NUM || m->view_table[view]) return -1; + const PureBlock *block = &m->view_code[view]; + const PureOp *code = &m->code[block->start]; + return block->length == 2 && code[0].op == PURE_OP_INDEX_LOAD && code[1].op == PURE_OP_INDEX_NUM ? (int)code[0].arg : -1; +} + int pure_module_column(const PureModule *m, uint32_t table, const char *name) { for (uint32_t i = 0; i < m->views; ++i) if (m->view_table[i] == table + 1 && !strcmp(m->view_names[i], name)) return (int)i; @@ -1121,6 +1128,19 @@ PureResult pure_app_input(PureApp *app, uint32_t input, PureEdge edge) { return dispatch(app, 1 + 2 * input + (edge == PURE_EDGE_UP), &ev); } +PureResult pure_app_point(PureApp *app, uint32_t state, size_t position) { + const PureModule *m = app->module; + pure_app_finish(app); + if (state >= m->states || m->state_type[state] != PURE_STATE_INDEX) return PURE_INVALID; + if (position >= pure_list_count(app->lists[m->state_ref[state]])) return PURE_NOT_FOUND; + open_burst(app); /* what a failed save returns to */ + app->fault = PURE_FAULT_NONE; + app->fault_pc = UINT32_MAX; + app->values[state] = (double)position; + ++app->events; + return app->deferred ? PURE_OK : pure_app_save(app); +} + PureResult pure_app_text(PureApp *app, uint32_t input, PureSpan text) { pure_app_finish(app); if (input >= app->module->inputs || app->module->input[input].kind != PURE_KIND_TEXT || diff --git a/prototypes/connections/module.h b/prototypes/connections/module.h index c9fb606..16b755c 100644 --- a/prototypes/connections/module.h +++ b/prototypes/connections/module.h @@ -106,6 +106,9 @@ int pure_module_state(const PureModule *, const char *name); int pure_module_input(const PureModule *, const char *name); int pure_module_view(const PureModule *, const char *name); int pure_module_column(const PureModule *, uint32_t table, const char *name); +/* The index state a number view shows as its position (a view that is just + * `at.position`), or -1: what a host moves with pure_app_point. */ +int pure_module_position(const PureModule *, uint32_t view); /* Keyed collections live in a data host (for example the tree store). One * event opens at most one document, the message's key, at its current @@ -189,6 +192,10 @@ bool pure_app_defer(PureApp *, bool deferred); void pure_app_stop(PureApp *); PureResult pure_app_frame(PureApp *); PureResult pure_app_input(PureApp *, uint32_t input, PureEdge); +/* Point an index state at the item at position (0 first), as a user pointing + * at a row: an event with no handler, saved like a key event. NOT_FOUND past + * the last item, INVALID for a state that is not an index. */ +PureResult pure_app_point(PureApp *, uint32_t state, size_t position); /* Deliver committed text (for example from an input method). Not UTF-8: invalid. */ PureResult pure_app_text(PureApp *, uint32_t input, PureSpan text); /* Deliver a message. base is the revision the sender edited, or -1 for none; diff --git a/prototypes/connections/test-module.c b/prototypes/connections/test-module.c index 6f48bc8..b0666fe 100644 --- a/prototypes/connections/test-module.c +++ b/prototypes/connections/test-module.c @@ -1714,6 +1714,35 @@ static void deferred(void) { deferred_messages(); } +/* A host points at an item: the index a view shows as its position moves + * there, as an event with no handler, saved like a key event; deferred, it + * joins the burst and a failed save undoes it with the rest. */ +static void pointing(void) { + CHECK(load(list_spec, &module_a) == PURE_OK); + CHECK(pure_module_position(&module_a, 1) == 2); /* position = at.position */ + CHECK(pure_module_position(&module_a, 0) == -1 && pure_module_position(&module_a, 2) == -1 && + pure_module_position(&module_a, 3) == -1 && pure_module_position(&module_a, 99) == -1); + char path[] = "/tmp/pure-point-XXXXXX"; + CHECK(mkdtemp(path) && pure_store_init(path) == PURE_OK); + PureApp app; + CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK); + CHECK(pure_app_point(&app, 2, 0) == PURE_NOT_FOUND); /* nothing to point at */ + for (int i = 0; i < 3; ++i) CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK); + uint64_t events = app.events; + CHECK(app.values[2] == 2 && pure_app_point(&app, 2, 0) == PURE_OK && app.values[2] == 0 && app.events == events + 1); + CHECK(pure_app_point(&app, 2, 3) == PURE_NOT_FOUND && app.values[2] == 0); + CHECK(pure_app_point(&app, 0, 1) == PURE_INVALID && pure_app_point(&app, 1, 1) == PURE_INVALID); /* a number, a list */ + CHECK(pure_app_point(&app, 2, 1) == PURE_OK); + pure_app_stop(&app); + CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[2] == 1); + CHECK(pure_app_defer(&app, true) && pure_app_point(&app, 2, 2) == PURE_OK && app.open && app.values[2] == 2); + CHECK(pure_app_save(&app) == PURE_OK); + pure_app_stop(&app); + CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[2] == 2); + pure_app_stop(&app); + remove_store(path); +} + int main(void) { behaviour(); faults(); @@ -1724,12 +1753,13 @@ int main(void) { texts(); text_mutations(); lists(); + pointing(); list_mutations(); tables(); table_mutations(); stored_states(); deferred(); printf("{\"sizeof_module\":%zu,\"sizeof_app\":%zu}\n", sizeof(PureModule), sizeof(PureApp)); - puts("{\"module_tests\":\"verified-load-atomic-events-faults-signed-zero-if-else-replace-by-name-rejections-mutations-messages-documents-typed-verification-text-cursors-utf8-views-rollback-replace-lists-indices-undo-migration-joins-tables-columns-pick-slide-stored-equivalence-crashes-frames-messages-migration-deferred-bursts\"}"); + puts("{\"module_tests\":\"verified-load-atomic-events-faults-signed-zero-if-else-replace-by-name-rejections-mutations-messages-documents-typed-verification-text-cursors-utf8-views-rollback-replace-lists-indices-undo-migration-joins-tables-columns-pick-slide-stored-equivalence-crashes-frames-messages-migration-deferred-bursts-pointing\"}"); return 0; } |
