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(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, D: Directional + Eq> Splitter<C, D> {
77 pub fn set_direction(&mut self, cx: &mut ConfigCx, direction: D) {
79 if direction == self.direction {
80 return;
81 }
82
83 self.direction = direction;
84 cx.resize();
85 }
86 }
87
88 impl Self {
89 #[inline]
91 pub fn new_dir(widgets: C, direction: D) -> Self {
92 let mut grips = Vec::new();
93 grips.resize_with(widgets.len().saturating_sub(1), GripPart::new);
94 Splitter {
95 core: Default::default(),
96 align_hints: AlignHints::NONE,
97 widgets,
98 grips,
99 data: Default::default(),
100 direction,
101 size_solved: false,
102 next: 0,
103 id_map: Default::default(),
104 }
105 }
106
107 fn make_next_id(&mut self, is_grip: bool, index: usize) -> Id {
109 let child_index = (2 * index) + (is_grip as usize);
110 if !is_grip {
111 if let Some(child) = self.widgets.get_tile(index) {
112 if child.id_ref().is_valid() {
114 if let Some(key) = child.id_ref().next_key_after(self.id_ref()) {
115 if let Entry::Vacant(entry) = self.id_map.entry(key) {
116 entry.insert(child_index);
117 return child.id();
118 }
119 }
120 }
121 }
122 } else {
123 if let Some(child) = self.grips.get_tile(index) {
124 if child.id_ref().is_valid() {
126 if let Some(key) = child.id_ref().next_key_after(self.id_ref()) {
127 if let Entry::Vacant(entry) = self.id_map.entry(key) {
128 entry.insert(child_index);
129 return child.id();
130 }
131 }
132 }
133 }
134 }
135
136 loop {
137 let key = self.next;
138 self.next += 1;
139 if let Entry::Vacant(entry) = self.id_map.entry(key) {
140 entry.insert(child_index);
141 return self.id_ref().make_child(key);
142 }
143 }
144 }
145
146 #[inline]
147 fn dim(&self) -> (D, usize) {
148 (self.direction, self.widgets.len() + self.grips.len())
149 }
150 }
151
152 impl Layout for Self {
153 fn size_rules(&mut self, cx: &mut SizeCx, axis: AxisInfo) -> SizeRules {
154 if self.widgets.is_empty() {
155 return SizeRules::EMPTY;
156 }
157 assert_eq!(self.grips.len() + 1, self.widgets.len());
158
159 let grip_rules = cx.feature(Feature::Separator, axis);
160
161 let mut solver = layout::RowSolver::new(axis, self.dim(), &mut self.data);
162
163 let mut n = 0;
164 loop {
165 assert!(n < self.widgets.len());
166 let widgets = &mut self.widgets;
167 if let Some(w) = widgets.get_mut_tile(n) {
168 solver.for_child(&mut self.data, n << 1, |axis| w.size_rules(cx, axis));
169 }
170
171 if n >= self.grips.len() {
172 break;
173 }
174 let grips = &mut self.grips;
175 solver.for_child(&mut self.data, (n << 1) + 1, |axis| {
176 grips[n].size_rules(cx, axis);
177 grip_rules
178 });
179 n += 1;
180 }
181 solver.finish(&mut self.data)
182 }
183
184 fn set_rect(&mut self, cx: &mut SizeCx, rect: Rect, hints: AlignHints) {
185 self.core.set_rect(rect);
186 self.align_hints = hints;
187 self.size_solved = true;
188 if self.widgets.is_empty() {
189 return;
190 }
191 assert!(self.grips.len() + 1 == self.widgets.len());
192
193 let mut setter =
194 layout::RowSetter::<D, Vec<i32>, _>::new(rect, self.dim(), &mut self.data);
195
196 let mut n = 0;
197 loop {
198 assert!(n < self.widgets.len());
199 if let Some(w) = self.widgets.get_mut_tile(n) {
200 w.set_rect(cx, setter.child_rect(&mut self.data, n << 1), hints);
201 }
202
203 if n >= self.grips.len() {
204 break;
205 }
206
207 let index = (n << 1) + 1;
209 let track = setter.maximal_rect_of(&mut self.data, index);
210 self.grips[n].set_track(track);
211 let rect = setter.child_rect(&mut self.data, index);
212 self.grips[n].set_rect(cx, rect, AlignHints::NONE);
213
214 n += 1;
215 }
216 }
217
218 fn draw(&self, mut draw: DrawCx) {
219 if !self.size_solved {
220 debug_assert!(false);
221 return;
222 }
223 let solver = layout::RowPositionSolver::new(self.direction);
228 solver.for_children(&self.widgets, draw.get_clip_rect(), |w| {
229 w.draw(draw.re());
230 });
231
232 let solver = layout::RowPositionSolver::new(self.direction);
233 solver.for_children(&self.grips, draw.get_clip_rect(), |w| {
234 draw.separator(w.rect())
235 });
236 }
237 }
238
239 impl Tile for Self {
240 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
241 Role::Splitter
242 }
243
244 #[inline]
245 fn child_indices(&self) -> ChildIndices {
246 ChildIndices::range(0..self.widgets.len() + self.grips.len())
247 }
248 fn get_child(&self, index: usize) -> Option<&dyn Tile> {
249 if (index & 1) != 0 {
250 self.grips.get(index >> 1).map(|w| w.as_tile())
251 } else {
252 self.widgets.get_tile(index >> 1)
253 }
254 }
255
256 fn find_child_index(&self, id: &Id) -> Option<usize> {
257 id.next_key_after(self.id_ref())
258 .and_then(|k| self.id_map.get(&k).cloned())
259 }
260 }
261
262 impl Events for Self {
263 fn make_child_id(&mut self, child_index: usize) -> Id {
264 let is_grip = (child_index & 1) != 0;
265 self.make_next_id(is_grip, child_index / 2)
266 }
267
268 fn probe(&self, coord: Coord) -> Id {
269 if !self.size_solved {
270 debug_assert!(false);
271 return self.id();
272 }
273
274 let solver = layout::RowPositionSolver::new(self.direction);
279 if let Some(child) = solver.find_child(&self.widgets, coord) {
280 return child.try_probe(coord).unwrap_or_else(|| self.id());
281 }
282
283 let solver = layout::RowPositionSolver::new(self.direction);
284 if let Some(child) = solver.find_child(&self.grips, coord) {
285 return child.try_probe(coord).unwrap_or_else(|| self.id());
286 }
287
288 self.id()
289 }
290
291 fn configure(&mut self, _: &mut ConfigCx) {
292 self.id_map.clear();
294 }
295
296 fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) {
297 if let Some(index) = cx.last_child() {
298 if (index & 1) == 1 {
299 if let Some(GripMsg::PressMove(mut offset)) = cx.try_pop() {
300 let n = index >> 1;
301 assert!(n < self.grips.len());
302 if let Some(grip) = self.grips.get_mut(n) {
303 if self.direction.is_reversed() {
304 offset = Offset::conv(grip.track().size) - offset;
305 }
306 grip.set_offset(cx, offset);
307 }
308 self.adjust_size(cx, n);
309 }
310 }
311 }
312 }
313 }
314
315 impl Widget for Self {
316 type Data = C::Data;
317
318 fn child_node<'n>(&'n mut self, data: &'n C::Data, index: usize) -> Option<Node<'n>> {
319 if (index & 1) != 0 {
320 self.grips.get_mut(index >> 1).map(|w| w.as_node(&()))
321 } else {
322 self.widgets.child_node(data, index >> 1)
323 }
324 }
325 }
326}
327
328impl<C: Collection, D: Directional> Splitter<C, D> {
329 fn adjust_size(&mut self, cx: &mut ConfigCx, n: usize) {
330 assert!(n < self.grips.len());
331 assert_eq!(self.widgets.len(), self.grips.len() + 1);
332 let index = 2 * n + 1;
333
334 let hrect = self.grips[n].rect();
335 let width1 = (hrect.pos - self.rect().pos).extract(self.direction);
336 let width2 = (self.rect().size - hrect.size).extract(self.direction) - width1;
337
338 let dim = self.dim();
339 let mut setter =
340 layout::RowSetter::<D, Vec<i32>, _>::new_unsolved(self.rect(), dim, &mut self.data);
341 setter.solve_range(&mut self.data, 0..index, width1, true);
342 setter.solve_range(&mut self.data, (index + 1)..dim.1, width2, false);
343 setter.update_offsets(&mut self.data);
344
345 let mut n = 0;
346 loop {
347 assert!(n < self.widgets.len());
348 if let Some(w) = self.widgets.get_mut_tile(n) {
349 let rect = setter.child_rect(&mut self.data, n << 1);
350 w.set_rect(&mut cx.size_cx(), rect, self.align_hints);
351 }
352
353 if n >= self.grips.len() {
354 break;
355 }
356
357 let index = (n << 1) + 1;
358 let track = self.grips[n].track();
359 self.grips[n].set_track(track);
360 let rect = setter.child_rect(&mut self.data, index);
361 self.grips[n].set_rect(&mut cx.size_cx(), rect, AlignHints::NONE);
362
363 n += 1;
364 }
365 }
366
367 pub fn is_empty(&self) -> bool {
369 self.widgets.is_empty()
370 }
371
372 pub fn len(&self) -> usize {
374 self.widgets.len()
375 }
376}
377
378impl<W: Widget, D: Directional> Index<usize> for Splitter<Vec<W>, D> {
379 type Output = W;
380
381 fn index(&self, index: usize) -> &Self::Output {
382 &self.widgets[index]
383 }
384}
385
386impl<W: Widget, D: Directional> IndexMut<usize> for Splitter<Vec<W>, D> {
387 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
388 &mut self.widgets[index]
389 }
390}
391
392impl<W: Widget, D: Directional> Splitter<Vec<W>, D> {
393 pub fn get(&self, index: usize) -> Option<&W> {
395 self.widgets.get(index)
396 }
397
398 pub fn get_mut(&mut self, index: usize) -> Option<&mut W> {
400 self.widgets.get_mut(index)
401 }
402
403 pub fn clear(&mut self) {
405 self.widgets.clear();
406 self.grips.clear();
407 self.size_solved = false;
408 }
409
410 pub fn push(&mut self, cx: &mut ConfigCx, data: &W::Data, mut widget: W) -> usize {
416 let index = self.widgets.len();
417 if index > 0 {
418 let len = self.grips.len();
419 let id = self.make_next_id(true, len);
420 let mut w = GripPart::new();
421 cx.configure(w.as_node(&()), id);
422 self.grips.push(w);
423 }
424
425 let id = self.make_next_id(false, index);
426 cx.configure(widget.as_node(data), id);
427 self.widgets.push(widget);
428
429 self.size_solved = false;
430 cx.resize();
431 index
432 }
433
434 pub fn pop(&mut self, cx: &mut ConfigCx) -> Option<W> {
438 let result = self.widgets.pop();
439 if let Some(w) = result.as_ref() {
440 cx.resize();
441
442 if w.id_ref().is_valid() {
443 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
444 self.id_map.remove(&key);
445 }
446 }
447
448 if let Some(w) = self.grips.pop() {
449 if w.id_ref().is_valid() {
450 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
451 self.id_map.remove(&key);
452 }
453 }
454 }
455 }
456 result
457 }
458
459 pub fn insert(&mut self, cx: &mut ConfigCx, data: &W::Data, index: usize, mut widget: W) {
465 for v in self.id_map.values_mut() {
466 if *v >= index {
467 *v += 2;
468 }
469 }
470
471 if !self.widgets.is_empty() {
472 let index = index.min(self.grips.len());
473 let id = self.make_next_id(true, index);
474 let mut w = GripPart::new();
475 cx.configure(w.as_node(&()), id);
476 self.grips.insert(index, w);
477 }
478
479 let id = self.make_next_id(false, index);
480 cx.configure(widget.as_node(data), id);
481 self.widgets.insert(index, widget);
482
483 self.size_solved = false;
484 cx.resize();
485 }
486
487 pub fn remove(&mut self, cx: &mut ConfigCx, index: usize) -> W {
493 if !self.grips.is_empty() {
494 let index = index.min(self.grips.len());
495 let w = self.grips.remove(index);
496 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
497 self.id_map.remove(&key);
498 }
499 }
500
501 let w = self.widgets.remove(index);
502 if w.id_ref().is_valid() {
503 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
504 self.id_map.remove(&key);
505 }
506 }
507
508 cx.resize();
509
510 for v in self.id_map.values_mut() {
511 if *v > index {
512 *v -= 2;
513 }
514 }
515 w
516 }
517
518 pub fn replace(&mut self, cx: &mut ConfigCx, data: &W::Data, index: usize, mut w: W) -> W {
524 let id = self.make_next_id(false, index);
525 cx.configure(w.as_node(data), id);
526 std::mem::swap(&mut w, &mut self.widgets[index]);
527
528 if w.id_ref().is_valid() {
529 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
530 self.id_map.remove(&key);
531 }
532 }
533
534 self.size_solved = false;
535 cx.resize();
536
537 w
538 }
539
540 pub fn extend<T: IntoIterator<Item = W>>(
544 &mut self,
545 data: &W::Data,
546 cx: &mut ConfigCx,
547 iter: T,
548 ) {
549 let iter = iter.into_iter();
550 if let Some(ub) = iter.size_hint().1 {
551 self.grips.reserve(ub);
552 self.widgets.reserve(ub);
553 }
554
555 for mut widget in iter {
556 let index = self.widgets.len();
557 if index > 0 {
558 let id = self.make_next_id(true, self.grips.len());
559 let mut w = GripPart::new();
560 cx.configure(w.as_node(&()), id);
561 self.grips.push(w);
562 }
563
564 let id = self.make_next_id(false, index);
565 cx.configure(widget.as_node(data), id);
566 self.widgets.push(widget);
567 }
568
569 self.size_solved = false;
570 cx.resize();
571 }
572
573 pub fn resize_with<F: Fn(usize) -> W>(
577 &mut self,
578 data: &W::Data,
579 cx: &mut ConfigCx,
580 len: usize,
581 f: F,
582 ) {
583 let old_len = self.widgets.len();
584
585 if len < old_len {
586 cx.resize();
587 loop {
588 let result = self.widgets.pop();
589 if let Some(w) = result.as_ref() {
590 if w.id_ref().is_valid() {
591 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
592 self.id_map.remove(&key);
593 }
594 }
595
596 if let Some(w) = self.grips.pop() {
597 if w.id_ref().is_valid() {
598 if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
599 self.id_map.remove(&key);
600 }
601 }
602 }
603 }
604
605 if len == self.widgets.len() {
606 return;
607 }
608 }
609 }
610
611 if len > old_len {
612 self.widgets.reserve(len - old_len);
613 for index in old_len..len {
614 if index > 0 {
615 let id = self.make_next_id(true, self.grips.len());
616 let mut w = GripPart::new();
617 cx.configure(w.as_node(&()), id);
618 self.grips.push(w);
619 }
620
621 let id = self.make_next_id(false, index);
622 let mut widget = f(index);
623 cx.configure(widget.as_node(data), id);
624 self.widgets.push(widget);
625 }
626
627 self.size_solved = false;
628 cx.resize();
629 }
630 }
631}