diff options
| author | Isaac Freund <ifreund@ifreund.xyz> | 2020-04-13 17:25:39 +0200 |
|---|---|---|
| committer | Isaac Freund <ifreund@ifreund.xyz> | 2020-04-13 17:25:39 +0200 |
| commit | b822084f39b568d7a1b65b0df7e77ec1dbb3c0bb (patch) | |
| tree | 179bf89821017a35d013822d097eeef0b07cb53e /src/view_stack.zig | |
| parent | 0f52f664f22c81e3971cfbf62d0f2f6da90c98f1 (diff) | |
| download | river-b822084f39b568d7a1b65b0df7e77ec1dbb3c0bb.tar.gz river-b822084f39b568d7a1b65b0df7e77ec1dbb3c0bb.tar.xz | |
Allow storing *View in ViewStack
This is done in preparation for implementing focus stacks.
Diffstat (limited to 'src/view_stack.zig')
| -rw-r--r-- | src/view_stack.zig | 318 |
1 files changed, 161 insertions, 157 deletions
diff --git a/src/view_stack.zig b/src/view_stack.zig index a729837..c1fe4e1 100644 --- a/src/view_stack.zig +++ b/src/view_stack.zig @@ -1,147 +1,151 @@ const View = @import("view.zig").View; /// A specialized doubly-linked stack that allows for filtered iteration -/// over the nodes -pub const ViewStack = struct { - const Self = @This(); +/// over the nodes. T must be View or *View. +pub fn ViewStack(comptime T: type) type { + if (!(T == View or T == *View)) { + @compileError("ViewStack: T must be View or *View"); + } + return struct { + const Self = @This(); - pub const Node = struct { - /// Previous/next nodes in the stack - prev: ?*Node, - next: ?*Node, + pub const Node = struct { + /// Previous/next nodes in the stack + prev: ?*Node, + next: ?*Node, - /// The view stored in this node - view: View, - }; + /// The view stored in this node + view: T, + }; - /// Top/bottom nodes in the stack - first: ?*Node, - last: ?*Node, + /// Top/bottom nodes in the stack + first: ?*Node, + last: ?*Node, - /// Initialize an undefined stack - pub fn init(self: *Self) void { - self.first = null; - self.last = null; - } - - /// Add a node to the top of the stack. - pub fn push(self: *Self, new_node: *Node) void { - // Set the prev/next pointers of the new node - new_node.prev = null; - new_node.next = self.first; - - if (self.first) |first| { - // If the list is not empty, set the prev pointer of the current - // first node to the new node. - first.prev = new_node; - } else { - // If the list is empty set the last pointer to the new node. - self.last = new_node; + /// Initialize an undefined stack + pub fn init(self: *Self) void { + self.first = null; + self.last = null; } - // Set the first pointer to the new node - self.first = new_node; - } + /// Add a node to the top of the stack. + pub fn push(self: *Self, new_node: *Node) void { + // Set the prev/next pointers of the new node + new_node.prev = null; + new_node.next = self.first; + + if (self.first) |first| { + // If the list is not empty, set the prev pointer of the current + // first node to the new node. + first.prev = new_node; + } else { + // If the list is empty set the last pointer to the new node. + self.last = new_node; + } - /// Remove a node from the view stack. This removes it from the stack of - /// all views as well as the stack of visible ones. - pub fn remove(self: *Self, target_node: *Node) void { - // Set the previous node/list head to the next pointer - if (target_node.prev) |prev_node| { - prev_node.next = target_node.next; - } else { - self.first = target_node.next; + // Set the first pointer to the new node + self.first = new_node; } - // Set the next node/list tail to the previous pointer - if (target_node.next) |next_node| { - next_node.prev = target_node.prev; - } else { - self.last = target_node.prev; + /// Remove a node from the view stack. This removes it from the stack of + /// all views as well as the stack of visible ones. + pub fn remove(self: *Self, target_node: *Node) void { + // Set the previous node/list head to the next pointer + if (target_node.prev) |prev_node| { + prev_node.next = target_node.next; + } else { + self.first = target_node.next; + } + + // Set the next node/list tail to the previous pointer + if (target_node.next) |next_node| { + next_node.prev = target_node.prev; + } else { + self.last = target_node.prev; + } } - } - const Iterator = struct { - it: ?*Node, - tags: u32, - reverse: bool, - pending: bool, - - /// Returns the next node in iteration order, or null if done. - /// This function is horribly ugly, but it's well tested below. - pub fn next(self: *Iterator) ?*View { - while (self.it) |node| : (self.it = if (self.reverse) node.prev else node.next) { - if (node.view.mapped and if (self.pending) - if (node.view.pending_tags) |pending_tags| - self.tags & pending_tags != 0 + const Iterator = struct { + it: ?*Node, + tags: u32, + reverse: bool, + pending: bool, + + /// Returns the next node in iteration order, or null if done. + /// This function is horribly ugly, but it's well tested below. + pub fn next(self: *Iterator) ?*Node { + while (self.it) |node| : (self.it = if (self.reverse) node.prev else node.next) { + if (node.view.mapped and if (self.pending) + if (node.view.pending_tags) |pending_tags| + self.tags & pending_tags != 0 + else + self.tags & node.view.current_tags != 0 else - self.tags & node.view.current_tags != 0 - else - self.tags & node.view.current_tags != 0) { - const ret = &node.view; - self.it = if (self.reverse) node.prev else node.next; - return ret; + self.tags & node.view.current_tags != 0) { + self.it = if (self.reverse) node.prev else node.next; + return node; + } } + return null; } - return null; - } - }; - - /// Returns an iterator starting at the passed node and filtered by - /// checking the passed tags against the current tags of each view. - /// Unmapped views are skipped. - pub fn iterator(start: ?*Node, tags: u32) Iterator { - return Iterator{ - .it = start, - .tags = tags, - .reverse = false, - .pending = false, }; - } - /// Returns a reverse iterator starting at the passed node and filtered by - /// checking the passed tags against the current tags of each view. - /// Unmapped views are skipped. - pub fn reverseIterator(start: ?*Node, tags: u32) Iterator { - return Iterator{ - .it = start, - .tags = tags, - .reverse = true, - .pending = false, - }; - } + /// Returns an iterator starting at the passed node and filtered by + /// checking the passed tags against the current tags of each view. + /// Unmapped views are skipped. + pub fn iterator(start: ?*Node, tags: u32) Iterator { + return Iterator{ + .it = start, + .tags = tags, + .reverse = false, + .pending = false, + }; + } - /// Returns an iterator starting at the passed node and filtered by - /// checking the passed tags against the pending tags of each view. - /// If a view has no pending tags, the current tags are used. Unmapped - /// views are skipped. - pub fn pendingIterator(start: ?*Node, tags: u32) Iterator { - return Iterator{ - .it = start, - .tags = tags, - .reverse = false, - .pending = true, - }; - } -}; + /// Returns a reverse iterator starting at the passed node and filtered by + /// checking the passed tags against the current tags of each view. + /// Unmapped views are skipped. + pub fn reverseIterator(start: ?*Node, tags: u32) Iterator { + return Iterator{ + .it = start, + .tags = tags, + .reverse = true, + .pending = false, + }; + } + + /// Returns an iterator starting at the passed node and filtered by + /// checking the passed tags against the pending tags of each view. + /// If a view has no pending tags, the current tags are used. Unmapped + /// views are skipped. + pub fn pendingIterator(start: ?*Node, tags: u32) Iterator { + return Iterator{ + .it = start, + .tags = tags, + .reverse = false, + .pending = true, + }; + } + }; +} const testing = @import("std").testing; -test "push/remove" { +test "push/remove (*View)" { const allocator = testing.allocator; - var views: ViewStack = undefined; + var views: ViewStack(*View) = undefined; views.init(); - const one = try allocator.create(ViewStack.Node); + const one = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(one); - const two = try allocator.create(ViewStack.Node); + const two = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(two); - const three = try allocator.create(ViewStack.Node); + const three = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(three); - const four = try allocator.create(ViewStack.Node); + const four = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(four); - const five = try allocator.create(ViewStack.Node); + const five = try allocator.create(ViewStack(*View).Node); defer allocator.destroy(five); views.push(three); // {3} @@ -255,40 +259,40 @@ test "push/remove" { testing.expect(views.last == null); } -test "iteration" { +test "iteration (View)" { const allocator = testing.allocator; - var views: ViewStack = undefined; + var views: ViewStack(View) = undefined; views.init(); - const one_a_pb = try allocator.create(ViewStack.Node); + const one_a_pb = try allocator.create(ViewStack(View).Node); defer allocator.destroy(one_a_pb); one_a_pb.view.mapped = true; one_a_pb.view.current_tags = 1 << 0; one_a_pb.view.pending_tags = 1 << 1; - const two_a = try allocator.create(ViewStack.Node); + const two_a = try allocator.create(ViewStack(View).Node); defer allocator.destroy(two_a); two_a.view.mapped = true; two_a.view.current_tags = 1 << 0; - const three_b_pa = try allocator.create(ViewStack.Node); + const three_b_pa = try allocator.create(ViewStack(View).Node); defer allocator.destroy(three_b_pa); three_b_pa.view.mapped = true; three_b_pa.view.current_tags = 1 << 1; three_b_pa.view.pending_tags = 1 << 0; - const four_b = try allocator.create(ViewStack.Node); + const four_b = try allocator.create(ViewStack(View).Node); defer allocator.destroy(four_b); four_b.view.mapped = true; four_b.view.current_tags = 1 << 1; - const five_b = try allocator.create(ViewStack.Node); + const five_b = try allocator.create(ViewStack(View).Node); defer allocator.destroy(five_b); five_b.view.mapped = true; five_b.view.current_tags = 1 << 1; - const unmapped_1 = try allocator.create(ViewStack.Node); + const unmapped_1 = try allocator.create(ViewStack(View).Node); defer allocator.destroy(unmapped_1); unmapped_1.view.mapped = false; @@ -301,92 +305,92 @@ test "iteration" { // Iteration over all tags { - var it = ViewStack.iterator(views.first, 0xFFFFFFFF); - testing.expect(it.next() == &two_a.view); - testing.expect(it.next() == &five_b.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &one_a_pb.view); - testing.expect(it.next() == &three_b_pa.view); + var it = ViewStack(View).iterator(views.first, 0xFFFFFFFF); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); testing.expect(it.next() == null); } // Iteration over 'a' tags { - var it = ViewStack.iterator(views.first, 1 << 0); - testing.expect(it.next() == &two_a.view); - testing.expect(it.next() == &one_a_pb.view); + var it = ViewStack(View).iterator(views.first, 1 << 0); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); testing.expect(it.next() == null); } // Iteration over 'b' tags { - var it = ViewStack.iterator(views.first, 1 << 1); - testing.expect(it.next() == &five_b.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &three_b_pa.view); + var it = ViewStack(View).iterator(views.first, 1 << 1); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); testing.expect(it.next() == null); } // Iteration over tags that aren't present { - var it = ViewStack.iterator(views.first, 1 << 2); + var it = ViewStack(View).iterator(views.first, 1 << 2); testing.expect(it.next() == null); } // Reverse iteration over all tags { - var it = ViewStack.reverseIterator(views.last, 0xFFFFFFFF); - testing.expect(it.next() == &three_b_pa.view); - testing.expect(it.next() == &one_a_pb.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &five_b.view); - testing.expect(it.next() == &two_a.view); + var it = ViewStack(View).reverseIterator(views.last, 0xFFFFFFFF); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); testing.expect(it.next() == null); } // Reverse iteration over 'a' tags { - var it = ViewStack.reverseIterator(views.last, 1 << 0); - testing.expect(it.next() == &one_a_pb.view); - testing.expect(it.next() == &two_a.view); + var it = ViewStack(View).reverseIterator(views.last, 1 << 0); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); testing.expect(it.next() == null); } // Reverse iteration over 'b' tags { - var it = ViewStack.reverseIterator(views.last, 1 << 1); - testing.expect(it.next() == &three_b_pa.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &five_b.view); + var it = ViewStack(View).reverseIterator(views.last, 1 << 1); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); testing.expect(it.next() == null); } // Reverse iteration over tags that aren't present { - var it = ViewStack.reverseIterator(views.first, 1 << 2); + var it = ViewStack(View).reverseIterator(views.first, 1 << 2); testing.expect(it.next() == null); } // Iteration over (pending) 'a' tags { - var it = ViewStack.pendingIterator(views.first, 1 << 0); - testing.expect(it.next() == &two_a.view); - testing.expect(it.next() == &three_b_pa.view); + var it = ViewStack(View).pendingIterator(views.first, 1 << 0); + testing.expect((if (it.next()) |node| &node.view else null) == &two_a.view); + testing.expect((if (it.next()) |node| &node.view else null) == &three_b_pa.view); testing.expect(it.next() == null); } // Iteration over (pending) 'b' tags { - var it = ViewStack.pendingIterator(views.first, 1 << 1); - testing.expect(it.next() == &five_b.view); - testing.expect(it.next() == &four_b.view); - testing.expect(it.next() == &one_a_pb.view); + var it = ViewStack(View).pendingIterator(views.first, 1 << 1); + testing.expect((if (it.next()) |node| &node.view else null) == &five_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &four_b.view); + testing.expect((if (it.next()) |node| &node.view else null) == &one_a_pb.view); testing.expect(it.next() == null); } // Iteration over (pending) tags that aren't present { - var it = ViewStack.pendingIterator(views.first, 1 << 2); + var it = ViewStack(View).pendingIterator(views.first, 1 << 2); testing.expect(it.next() == null); } } |
