1use std::collections::hash_map::{Entry, HashMap};
9use std::ops::{Index, IndexMut};
10
11use super::{GripMsg, GripPart};
12use kas::Collection;
13use kas::layout::{self, RulesSetter, RulesSolver};
14use kas::prelude::*;
15use kas::theme::Feature;
16
17#[impl_self]
18mod Splitter {
19 #[derive(Clone, Default, Debug)]
24 #[widget]
25 pub struct Splitter<C: Collection, D: Directional = Direction> {
26 core: widget_core!(),
27 align_hints: AlignHints,
28 widgets: C,
29 grips: Vec<GripPart>,
30 data: layout::DynRowStorage,
31 direction: D,
32 size_solved: bool,
33 next: usize,
34 id_map: HashMap<usize, usize>, }
36
37 impl Self
38 where
39 D: Default,
40 {
41 #[inline]
43 pub fn new(widgets: C) -> Self {
44 Self::new_dir(widgets, Default::default())
45 }
46 }
47 impl<C: Collection> Splitter<C, kas::dir::Left> {
48 #[inline]
50 pub fn left(widgets: C) -> Self {
51 Self::new(widgets)
52 }
53 }
54 impl<C: Collection> Splitter<C, kas::dir::Right> {
55 #[inline]
57 pub fn right(widgets: C) -> Self {
58 Self::new(widgets)
59 }
60 }
61 impl<C: Collection> Splitter<C, kas::dir::Up> {
62 #[inline]
64 pub fn up(widgets: C) -> Self {
65 Self::new(widgets)
66 }
67 }
68 impl<C: Collection> Splitter<C, kas::dir::Down> {
69 #[inline]
71 pub fn down(widgets: C) -> Self {
72 Self::new(widgets)
73 }
74 }
75
76 impl<C: Collection> Splitter<C, Direction> {
77 pub fn set_direction(&mut self, cx: &mut EventState, direction: Direction) {
79 if direction == self.direction {
80 return;
81 }
82
83 self.direction = direction;
84 cx.resize(self);
86 }
87 }
88
89 impl Self {
90 #[inline]
92 pub fn new_dir(widgets: C, direction: D) -> Self {
93 let mut grips = Vec::new();
94 grips.resize_with(widgets.len().saturating_sub(1), GripPart::new);
95 Splitter {
96 core: Default::default(),
97 align_hints: AlignHints::NONE,
98 widgets,
99 grips,
100 data: Default::default(),
101 direction,
102 size_solved: false,
103 next: 0,
104 id_map: Default::default(),
105 }
106 }
107
108 fn make_next_id(&mut self, is_grip: bool, index: usize) -> Id {
110 let child_index = (2 * index) + (is_grip as usize);
111 if !is_grip {
112 if let Some(child) = self.widgets.get_tile(index) {
113 if child.id_ref().is_valid() && self.id_ref().is_ancestor_of(child.id_ref()) {
115 if let Some(key) = child.id_ref().next_key_after(self.id_ref()) {
116 if let Entry::Vacant(entry) = self.id_map.entry(key) {
117 entry.insert(child_index);
118 return child.id();
119 }
120 }
121 }
122 }
123 } else {
124 if let Some(child) = self.grips.get_tile(index) {
125 if child.id_ref().is_valid() && self.id_ref().is_ancestor_of(child.id_ref()) {
127 if let Some(key) = child.id_ref().next_key_after(self.id_ref()) {
128 if let Entry::Vacant(entry) = self.id_map.entry(key) {
129 entry.insert(child_index);
130 return child.id();
131 }
132 }
133 }
134 }
135 }
136
137 loop {
138 let key = self.next;
139 self.next += 1;
140 if let Entry::Vacant(entry) = self.id_map.entry(key) {
141 entry.insert(child_index);
142 return self.id_ref().make_child(key);
143 }
144 }
145 }
146
147 #[inline]
148 fn dim(&self) -> (D, usize) {
149 (self.direction, self.widgets.len() + self.grips.len())
150 }
151 }
152
153 impl Layout for Self {
154 fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
155 if self.widgets.is_empty() {
156 return SizeRules::EMPTY;
157 }
158 assert_eq!(self.grips.len() + 1, self.widgets.len());
159
160 let grip_rules = sizer.feature(Feature::Separator, axis);
161
162 let mut solver = layout::RowSolver::new(axis, self.dim(), &mut self.data);
163
164 let mut n = 0;
165 loop {
166 assert!(n < self.widgets.len());
167 let widgets = &mut self.widgets;
168 if let Some(w) = widgets.get_mut_tile(n) {
169 solver.for_child(&mut self.data, n << 1, |axis| {
170 w.size_rules(sizer.re(), axis)
171 });
172 }
173
174 if n >= self.grips.len() {
175 break;
176 }
177 let grips = &mut self.grips;
178 solver.for_child(&mut self.data, (n << 1) + 1, |axis| {
179 grips[n].size_rules(sizer.re(), axis);
180 grip_rules
181 });
182 n += 1;
183 }
184 solver.finish(&mut self.data)
185 }
186
187 fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
188 widget_set_rect!(rect);
189 self.align_hints = hints;
190 self.size_solved = true;
191 if self.widgets.is_empty() {
192 return;
193 }
194 assert!(self.grips.len() + 1 == self.widgets.len());
195
196 let mut setter =
197 layout::RowSetter::<D, Vec<i32>, _>::new(rect, self.dim(), &mut self.data);
198
199 let mut n = 0;
200 loop {
201 assert!(n < self.widgets.len());
202 if let Some(w) = self.widgets.get_mut_tile(n) {
203 w.set_rect(cx, setter.child_rect(&mut self.data, n << 1), hints);
204 }
205
206 if n >= self.grips.len() {
207 break;
208 }
209
210 let index = (n << 1) + 1;
212 let track = setter.maximal_rect_of(&mut self.data, index);
213 self.grips[n].set_track(track);
214 let rect = setter.child_rect(&mut self.data, index);
215 self.grips[n].set_rect(cx, rect, AlignHints::NONE);
216
217 n += 1;
218 }
219 }
220
221 fn draw(&self, mut draw: DrawCx) {
222 if !self.size_solved {
223 debug_assert!(false);
224 return;
225 }
226 let solver = layout::RowPositionSolver::new(self.direction);
231 solver.for_children(&self.widgets, draw.get_clip_rect(), |w| {
232 w.draw(draw.re());
233 });
234
235 let solver = layout::RowPositionSolver::new(self.direction);
236 solver.for_children(&self.grips, draw.get_clip_rect(), |w| {
237 draw.separator(w.rect())
238 });
239 }
240 }
241
242 impl Tile for Self {
243 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
244 Role::Splitter
245 }
246
247 #[inline]
248 fn child_indices(&self) -> ChildIndices {
249 (0..self.widgets.len() + self.grips.len()).into()
250 }
251 fn get_child(&self, index: usize) -> Option<&dyn Tile> {
252 if (index & 1) != 0 {
253 self.grips.get(index >> 1).map(|w| w.as_tile())
254 } else {
255 self.widgets.get_tile(index >> 1)
256 }
257 }
258
259 fn find_child_index(&self, id: &Id) -> Option<usize> {
260 id.next_key_after(self.id_ref())
261 .and_then(|k| self.id_map.get(&k).cloned())
262 }
263
264 fn probe(&self, coord: Coord) -> Id {
265 if !self.size_solved {
266 debug_assert!(false);
267 return self.id();
268 }
269
270 let solver = layout::RowPositionSolver::new(self.direction);
275 if let Some(child) = solver.find_child(&self.widgets, coord) {
276 return child.try_probe(coord).unwrap_or_else(|| self.id());
277 }
278
279 let solver = layout::RowPositionSolver::new(self.direction);
280 if let Some(child) = solver.find_child(&self.grips, coord) {
281 return child.try_probe(coord).unwrap_or_else(|| self.id());
282 }
283
284 self.id()
285 }
286 }
287
288 impl Events for Self {
289 fn make_child_id(&mut self, child_index: usize) -> Id {
290 let is_grip = (child_index & 1) != 0;
291 self.make_next_id(is_grip, child_index / 2)
292 }
293
294 fn configure(&mut self, _: &mut ConfigCx) {
295 self.id_map.clear();
297 }
298
299 fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) {
300 if let Some(index) = cx.last_child() {
301 if (index & 1) == 1 {
302 if let Some(GripMsg::PressMove(mut offset)) = cx.try_pop() {
303 let n = index >> 1;
304 assert!(n < self.grips.len());
305 if let Some(grip) = self.grips.get_mut(n) {
306 if self.direction.is_reversed() {
307 offset = Offset::conv(grip.track().size) - offset;
308 }
309 grip.set_offset(cx, offset);
310 }
311 self.adjust_size(&mut cx.config_cx(), n);
312 }
313 }
314 }
315 }
316 }
317
318 impl Widget for Self {
319 type Data = C::Data;
320
321 fn child_node<'n>(&'n mut self, data: &'n C::Data, index: usize) -> Option<Node<'n>> {
322 if (index & 1) != 0 {
323 self.grips.get_mut(index >> 1).map(|w| w.as_node(&()))
324 } else {
325 self.widgets.child_node(data, index >> 1)
326 }
327 }
328 }
329}
330
331impl<C: Collection, D: Directional> Splitter<C, D> {
332 fn adjust_size(&mut self, cx: &mut ConfigCx, n: usize) {
333 assert!(n < self.grips.len());
334 assert_eq!(self.widgets.len(), self.grips.len() + 1);
335 let index = 2 * n + 1;
336
337 let hrect = self.grips[n].rect();
338 let width1 = (hrect.pos - self.rect().pos).extract(self.direction);
339 let width2 = (self.rect().size - hrect.size).extract(self.direction) - width1;
340
341 let dim = self.dim();
342 let mut setter =
343 layout::RowSetter::<D, Vec<i32>, _>::new_unsolved(self.rect(), dim, &mut self.data);
344 setter.solve_range(&mut self.data, 0..index, width1);
345 setter.solve_range(&mut self.data, (index + 1)..dim.1, width2);
346 setter.update_offsets(&mut self.data);
347
348 let mut n = 0;
349 loop {
350 assert!(n < self.widgets.len());
351 if let Some(w) = self.widgets.get_mut_tile(n) {
352 let rect = setter.child_rect(&mut self.data, n << 1);
353 w.set_rect(cx, rect, self.align_hints);
354 }
355
356 if n >= self.grips.len() {
357 break;
358 }
359
360 let index = (n << 1) + 1;
361 let track = self.grips[n].track();
362 self.grips[n].set_track(track);
363 let rect = setter.child_rect(&mut self.data, index);
364 self.grips[n].set_rect(cx, rect, AlignHints::NONE);
365
366 n += 1;
367 }
368 }
369
370 pub fn is_empty(&self) -> bool {
372 self.widgets.is_empty()
373 }
374
375 pub fn len(&self) -> usize {
377 self.widgets.len()
378 }
379}
380
381impl<W: Widget, D: Directional> Index<usize> for Splitter<Vec<W>, D> {
382 type Output = W;
383
384 fn index(&self, index: usize) -> &Self::Output {
385 &self.widgets[index]
386 }
387}
388
389impl<W: Widget, D: Directional> IndexMut<usize> for Splitter<Vec<W>, D> {
390 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
391 &mut self.widgets[index]
392 }
393}
394
395impl<W: Widget, D: Directional> Splitter<Vec<W>, D> {
396 pub fn get(&self, index: usize) -> Option<&W> {
398 self.widgets.get(index)
399 }
400
401 pub fn get_mut(&mut self, index: usize) -> Option<&mut W> {
403 self.widgets.get_mut(index)
404 }
405
406 pub fn clear(&mut self) {
408 self.widgets.clear();
409 self.grips.clear();
410 self.size_solved = false;
411 }
412
413 pub fn push(&mut self, cx: &mut ConfigCx, data: &W::Data, mut widget: W) -> usize {
420 let index = self.widgets.len();
421 if index > 0 {
422 let len = self.grips.len();
423 let id = self.make_next_id(true, len);
424 let mut w = GripPart::new();
425 cx.configure(w.as_node(&()), id);
426 self.grips.push(w);
427 }
428
429 let id = self.make_next_id(false, index);
430 cx.configure(widget.as_node(data), id);
431 self.widgets.push(widget);
432
433 self.size_solved = false;
434 cx.resize(self);
435 index
436 }
437
438 pub fn pop(&mut self, cx: &mut EventState) -> Option<W> {
442 let result = self.widgets.pop();
443 if let Some(w) = result.as_ref() {
444 cx.resize(&self);
445
446 if w.id_ref().is_valid() {
447 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
448 self.id_map.remove(&key);
449 }
450 }
451
452 if let Some(w) = self.grips.pop() {
453 if w.id_ref().is_valid() {
454 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
455 self.id_map.remove(&key);
456 }
457 }
458 }
459 }
460 result
461 }
462
463 pub fn insert(&mut self, cx: &mut ConfigCx, data: &W::Data, index: usize, mut widget: W) {
469 for v in self.id_map.values_mut() {
470 if *v >= index {
471 *v += 2;
472 }
473 }
474
475 if !self.widgets.is_empty() {
476 let index = index.min(self.grips.len());
477 let id = self.make_next_id(true, index);
478 let mut w = GripPart::new();
479 cx.configure(w.as_node(&()), id);
480 self.grips.insert(index, w);
481 }
482
483 let id = self.make_next_id(false, index);
484 cx.configure(widget.as_node(data), id);
485 self.widgets.insert(index, widget);
486
487 self.size_solved = false;
488 cx.resize(self);
489 }
490
491 pub fn remove(&mut self, cx: &mut EventState, index: usize) -> W {
497 if !self.grips.is_empty() {
498 let index = index.min(self.grips.len());
499 let w = self.grips.remove(index);
500 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
501 self.id_map.remove(&key);
502 }
503 }
504
505 let w = self.widgets.remove(index);
506 if w.id_ref().is_valid() {
507 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
508 self.id_map.remove(&key);
509 }
510 }
511
512 cx.resize(&self);
513
514 for v in self.id_map.values_mut() {
515 if *v > index {
516 *v -= 2;
517 }
518 }
519 w
520 }
521
522 pub fn replace(&mut self, cx: &mut ConfigCx, data: &W::Data, index: usize, mut w: W) -> W {
528 let id = self.make_next_id(false, index);
529 cx.configure(w.as_node(data), id);
530 std::mem::swap(&mut w, &mut self.widgets[index]);
531
532 if w.id_ref().is_valid() {
533 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
534 self.id_map.remove(&key);
535 }
536 }
537
538 self.size_solved = false;
539 cx.resize(self);
540
541 w
542 }
543
544 pub fn extend<T: IntoIterator<Item = W>>(
548 &mut self,
549 data: &W::Data,
550 cx: &mut ConfigCx,
551 iter: T,
552 ) {
553 let iter = iter.into_iter();
554 if let Some(ub) = iter.size_hint().1 {
555 self.grips.reserve(ub);
556 self.widgets.reserve(ub);
557 }
558
559 for mut widget in iter {
560 let index = self.widgets.len();
561 if index > 0 {
562 let id = self.make_next_id(true, self.grips.len());
563 let mut w = GripPart::new();
564 cx.configure(w.as_node(&()), id);
565 self.grips.push(w);
566 }
567
568 let id = self.make_next_id(false, index);
569 cx.configure(widget.as_node(data), id);
570 self.widgets.push(widget);
571 }
572
573 self.size_solved = false;
574 cx.resize(self);
575 }
576
577 pub fn resize_with<F: Fn(usize) -> W>(
581 &mut self,
582 data: &W::Data,
583 cx: &mut ConfigCx,
584 len: usize,
585 f: F,
586 ) {
587 let old_len = self.widgets.len();
588
589 if len < old_len {
590 cx.resize(&self);
591 loop {
592 let result = self.widgets.pop();
593 if let Some(w) = result.as_ref() {
594 if w.id_ref().is_valid() {
595 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
596 self.id_map.remove(&key);
597 }
598 }
599
600 if let Some(w) = self.grips.pop() {
601 if w.id_ref().is_valid() {
602 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
603 self.id_map.remove(&key);
604 }
605 }
606 }
607 }
608
609 if len == self.widgets.len() {
610 return;
611 }
612 }
613 }
614
615 if len > old_len {
616 self.widgets.reserve(len - old_len);
617 for index in old_len..len {
618 if index > 0 {
619 let id = self.make_next_id(true, self.grips.len());
620 let mut w = GripPart::new();
621 cx.configure(w.as_node(&()), id);
622 self.grips.push(w);
623 }
624
625 let id = self.make_next_id(false, index);
626 let mut widget = f(index);
627 cx.configure(widget.as_node(data), id);
628 self.widgets.push(widget);
629 }
630
631 self.size_solved = false;
632 cx.resize(self);
633 }
634 }
635}