Skip to content

Commit ace5a70

Browse files
authored
Merge pull request #19 from kas-gui/push-povzppxprrkr
Update kas (master branch)
2 parents 771d9a7 + 254bea9 commit ace5a70

File tree

8 files changed

+278
-233
lines changed

8 files changed

+278
-233
lines changed

Cargo.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@ resolver = "2"
77
publish = false
88

99
[dependencies]
10-
kas = { version = "0.14.2" }
10+
kas = "0.15.0"
1111
chrono = "0.4"
12-
env_logger = "0.8"
12+
env_logger = "0.11.8"
1313
pest = "2.1"
1414
pest_derive = "2.1"
15+
16+
[patch.crates-io.kas]
17+
git = "https://github.com/kas-gui/kas.git"
18+
rev = "4bf71367f6a57c727a689097302de40274f5830a"
19+
20+
[patch.crates-io.kas-text]
21+
git = "https://github.com/kas-gui/kas-text.git"
22+
rev = "0bc2629df690ecfd562a7bf7b6ac096ad0d8f1a9"

src/cells.rs

Lines changed: 74 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,28 @@
55

66
//! Cells: a mini spreadsheet
77
8-
use kas::event::{Command, FocusSource};
9-
use kas::prelude::*;
10-
use kas::view::{DataKey, Driver, MatrixData, MatrixView, SharedData};
8+
use kas::view::{
9+
DataChanges, DataClerk, DataKey, DataLen, Driver, GridIndex, GridView, TokenChanges,
10+
};
1111
use kas::widgets::{EditBox, EditField, EditGuard, ScrollBars};
12+
use kas::{prelude::*, TextOrSource};
1213
use std::collections::HashMap;
13-
use std::{fmt, iter, ops};
14+
use std::fmt;
1415

1516
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, Hash)]
1617
pub struct ColKey(u8);
17-
type ColKeyIter = iter::Map<ops::RangeInclusive<u8>, fn(u8) -> ColKey>;
1818
impl ColKey {
1919
const LEN: u8 = 26;
2020
fn try_from_u8(n: u8) -> Option<Self> {
2121
if (b'A'..=b'Z').contains(&n) {
22-
Some(ColKey(n))
22+
Some(ColKey(n - b'A'))
2323
} else {
2424
None
2525
}
2626
}
2727
fn from_u8(n: u8) -> Self {
2828
Self::try_from_u8(n).expect("bad column key")
2929
}
30-
fn iter_keys() -> ColKeyIter {
31-
(b'A'..=b'Z').map(ColKey::from_u8)
32-
}
3330
}
3431

3532
impl fmt::Display for ColKey {
@@ -39,7 +36,7 @@ impl fmt::Display for ColKey {
3936
}
4037
}
4138

42-
const MAX_ROW: u8 = 99;
39+
const ROW_LEN: u32 = 100;
4340

4441
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
4542
pub struct Key(ColKey, u8);
@@ -265,15 +262,6 @@ impl Cell {
265262
self.input = input;
266263
}
267264

268-
/// Get display string
269-
fn display(&self) -> String {
270-
if !self.display.is_empty() {
271-
self.display.clone()
272-
} else {
273-
self.input.clone()
274-
}
275-
}
276-
277265
fn try_eval(&mut self, values: &HashMap<Key, f64>) -> Result<Option<f64>, EvalError> {
278266
if self.parse_error {
279267
// Display the error locally; propegate NaN
@@ -345,60 +333,50 @@ impl CellData {
345333
}
346334
}
347335

348-
#[derive(Clone, Debug, Default)]
349-
struct Item {
350-
input: String,
351-
display: String,
352-
error: bool,
336+
struct Clerk {
337+
empty_cell: Cell,
353338
}
354339

355-
impl SharedData for CellData {
340+
impl DataClerk<GridIndex> for Clerk {
341+
type Data = CellData;
356342
type Key = Key;
357-
type Item = Item;
358-
type ItemRef<'b> = Self::Item;
343+
type Item = Cell;
344+
type Token = Key;
359345

360-
fn contains_key(&self, _: &Self::Key) -> bool {
361-
// we know both sub-keys are valid and that the length is fixed
362-
true
346+
fn update(&mut self, _: &mut ConfigCx<'_>, _: Id, _: &Self::Data) -> DataChanges {
347+
DataChanges::Any
363348
}
364349

365-
fn borrow(&self, key: &Self::Key) -> Option<Self::Item> {
366-
self.cells
367-
.get(key)
368-
.map(|cell| Item {
369-
input: cell.input.clone(),
370-
display: cell.display(),
371-
error: cell.parse_error,
372-
})
373-
.or_else(|| Some(Item::default()))
374-
}
375-
}
376-
377-
impl MatrixData for CellData {
378-
type ColKey = ColKey;
379-
type RowKey = u8;
380-
type ColKeyIter<'b> = iter::Take<iter::Skip<ColKeyIter>>;
381-
type RowKeyIter<'b> = iter::Take<iter::Skip<ops::RangeInclusive<u8>>>;
382-
383-
fn is_empty(&self) -> bool {
384-
false
385-
}
386-
fn len(&self) -> (usize, usize) {
387-
(ColKey::LEN.cast(), 99)
350+
fn len(&self, _: &CellData, _: GridIndex) -> DataLen<GridIndex> {
351+
DataLen::Known(GridIndex {
352+
col: ColKey::LEN.cast(),
353+
row: ROW_LEN,
354+
})
388355
}
389356

390-
fn col_iter_from(&self, start: usize, limit: usize) -> Self::ColKeyIter<'_> {
391-
ColKey::iter_keys().skip(start).take(limit)
392-
}
357+
fn update_token(
358+
&self,
359+
_: &CellData,
360+
index: GridIndex,
361+
_: bool,
362+
token: &mut Option<Key>,
363+
) -> TokenChanges {
364+
if index.col >= ColKey::LEN as u32 || index.row >= ROW_LEN {
365+
*token = None;
366+
return TokenChanges::Any;
367+
}
393368

394-
fn row_iter_from(&self, start: usize, limit: usize) -> Self::RowKeyIter<'_> {
395-
// NOTE: for strict compliance with the 7GUIs challenge the rows should
396-
// start from 0, but any other spreadsheet I've seen starts from 1!
397-
(1..=MAX_ROW).skip(start).take(limit)
369+
let key = Key(ColKey(index.col as u8), index.row as u8);
370+
if *token == Some(key) {
371+
TokenChanges::None
372+
} else {
373+
*token = Some(key);
374+
TokenChanges::Any
375+
}
398376
}
399377

400-
fn make_key(&self, col: &Self::ColKey, row: &Self::RowKey) -> Self::Key {
401-
Key(*col, *row)
378+
fn item<'r>(&'r self, data: &'r CellData, key: &'r Key) -> &'r Cell {
379+
data.cells.get(key).unwrap_or(&self.empty_cell)
402380
}
403381
}
404382

@@ -411,39 +389,42 @@ struct CellGuard {
411389
is_input: bool,
412390
}
413391
impl EditGuard for CellGuard {
414-
type Data = Item;
392+
type Data = Cell;
415393

416-
fn update(edit: &mut EditField<Self>, cx: &mut ConfigCx, item: &Item) {
417-
let mut action = edit.set_error_state(item.error);
394+
fn update(edit: &mut EditField<Self>, cx: &mut ConfigCx, item: &Cell) {
395+
edit.set_error_state(cx, item.parse_error);
418396
if !edit.has_edit_focus() {
419-
action |= edit.set_str(&item.display);
397+
let text = if !item.display.is_empty() {
398+
&item.display
399+
} else {
400+
&item.input
401+
};
402+
edit.set_str(cx, text);
420403
edit.guard.is_input = false;
421404
}
422-
cx.action(edit, action);
423405
}
424406

425-
fn activate(edit: &mut EditField<Self>, cx: &mut EventCx, item: &Item) -> IsUsed {
407+
fn activate(edit: &mut EditField<Self>, cx: &mut EventCx, item: &Cell) -> IsUsed {
426408
Self::focus_lost(edit, cx, item);
427409
IsUsed::Used
428410
}
429411

430-
fn focus_gained(edit: &mut EditField<Self>, cx: &mut EventCx, item: &Item) {
431-
cx.action(edit.id(), edit.set_str(&item.input));
412+
fn focus_gained(edit: &mut EditField<Self>, cx: &mut EventCx, item: &Cell) {
413+
edit.set_str(cx, &item.input);
432414
edit.guard.is_input = true;
433415
}
434416

435-
fn focus_lost(edit: &mut EditField<Self>, cx: &mut EventCx, item: &Item) {
436-
let s = edit.get_string();
437-
if edit.guard.is_input && s != item.input {
438-
cx.push(UpdateInput(edit.guard.key, s));
417+
fn focus_lost(edit: &mut EditField<Self>, cx: &mut EventCx, item: &Cell) {
418+
if edit.guard.is_input && edit.as_str() != item.input {
419+
cx.push(UpdateInput(edit.guard.key, edit.clone_string()));
439420
}
440421
}
441422
}
442423

443424
#[derive(Debug)]
444425
struct CellDriver;
445426

446-
impl Driver<Item, CellData> for CellDriver {
427+
impl Driver<Key, Cell> for CellDriver {
447428
// TODO: we should use EditField instead of EditBox but:
448429
// (a) there is currently no code to draw separators between cells
449430
// (b) EditField relies on a parent (EditBox) to draw background highlight on error state
@@ -454,6 +435,15 @@ impl Driver<Item, CellData> for CellDriver {
454435
key: *key,
455436
is_input: false,
456437
})
438+
.with_width_em(6.0, 6.0)
439+
}
440+
441+
fn navigable(_: &Self::Widget) -> bool {
442+
false
443+
}
444+
445+
fn label(widget: &Self::Widget) -> Option<TextOrSource<'_>> {
446+
Some(widget.as_str().into())
457447
}
458448
}
459449

@@ -470,46 +460,29 @@ pub fn window() -> Window<()> {
470460
cells.insert(make_key("C2"), Cell::new("= A2 * A3 * A4"));
471461
data.update_values();
472462

473-
let cells = MatrixView::new(CellDriver).with_num_visible(5, 20);
463+
let clerk = Clerk {
464+
empty_cell: Cell::default(),
465+
};
466+
467+
let cells = GridView::new(clerk, CellDriver).with_num_visible(5, 20);
474468

475469
let ui = impl_anon! {
476-
#[widget {
477-
layout = self.cells;
478-
}]
470+
#[widget]
471+
#[layout(self.cells)]
479472
struct {
480473
core: widget_core!(),
481474
data: CellData = data,
482-
#[widget(&self.data)] cells: ScrollBars<MatrixView<CellData, CellDriver>> =
475+
#[widget(&self.data)] cells: ScrollBars<GridView<Clerk, CellDriver>> =
483476
ScrollBars::new(cells),
484477
}
485478
impl Events for Self {
486479
type Data = ();
487480

488-
fn steal_event(&mut self, cx: &mut EventCx, _: &(), _: &Id, event: &Event) -> IsUsed {
489-
match event {
490-
Event::Command(Command::Enter, _) => {
491-
if let Some(Key(col, row)) = cx.nav_focus().and_then(|id| {
492-
Key::reconstruct_key(self.cells.inner().id_ref(), id)
493-
})
494-
{
495-
let row = if cx.modifiers().shift_key() {
496-
(row - 1).max(1)
497-
} else {
498-
(row + 1).min(MAX_ROW)
499-
};
500-
let id = Key(col, row).make_id(self.cells.inner().id_ref());
501-
cx.next_nav_focus(Some(id), false, FocusSource::Synthetic);
502-
}
503-
IsUsed::Used
504-
},
505-
_ => IsUsed::Unused
506-
}
507-
}
508-
509481
fn handle_messages(&mut self, cx: &mut EventCx, _: &()) {
510482
if let Some(UpdateInput(key, input)) = cx.try_pop() {
511483
self.data.cells.entry(key).or_default().update(input);
512484
self.data.update_values();
485+
cx.update(self.cells.as_node(&self.data));
513486
}
514487
}
515488
}

src/counter.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,18 @@
66
//! Counter
77
88
use kas::prelude::*;
9-
use kas::widgets::{Adapt, Button, EditBox};
9+
use kas::widgets::{row, Button, EditBox};
1010

1111
#[derive(Clone, Debug)]
1212
struct Incr;
1313

1414
pub fn window() -> Window<()> {
15-
let ui = kas::row![
16-
align!(
17-
right,
18-
EditBox::string(|count| format!("{count}")).with_width_em(3.0, 3.0)
19-
),
20-
Button::label_msg("Count", Incr).map_any(),
15+
let ui = row![
16+
EditBox::string(|count| format!("{count}"))
17+
.with_width_em(3.0, 3.0)
18+
.align(AlignHints::RIGHT),
19+
Button::label_msg("&Count", Incr).map_any(),
2120
];
22-
let ui = Adapt::new(ui, 0).on_message(|_, count, Incr| *count += 1);
21+
let ui = ui.with_state(0).on_message(|_, count, Incr| *count += 1);
2322
Window::new(ui, "Counter")
2423
}

0 commit comments

Comments
 (0)