a modern tui library written in zig

Screen: move unicode field to Window and widgets.View

Unicode is not actually used by Screen anyway,
and Vaxis.init cannot return with Vaxis.screen.unicode assigned
as the pointer to Vaxis.unicode could be dangling.

The initial Vaxis without .screen.unicode
makes {Window,widgets.View}.gwidth a footgun.

authored by Nguyễn Gia Phong and committed by rockorager.dev 6572093e f1542bc5

verified
Changed files
+26 -22
src
+1 -5
src/Screen.zig
··· 5 5 const Shape = @import("Mouse.zig").Shape; 6 6 const Image = @import("Image.zig"); 7 7 const Winsize = @import("main.zig").Winsize; 8 - const Unicode = @import("Unicode.zig"); 9 8 const Method = @import("gwidth.zig").Method; 10 9 11 10 const Screen = @This(); ··· 21 20 cursor_row: u16 = 0, 22 21 cursor_col: u16 = 0, 23 22 cursor_vis: bool = false, 24 - 25 - unicode: *const Unicode = undefined, 26 23 27 24 width_method: Method = .wcwidth, 28 25 29 26 mouse_shape: Shape = .default, 30 27 cursor_shape: Cell.CursorShape = .default, 31 28 32 - pub fn init(alloc: std.mem.Allocator, winsize: Winsize, unicode: *const Unicode) std.mem.Allocator.Error!Screen { 29 + pub fn init(alloc: std.mem.Allocator, winsize: Winsize) std.mem.Allocator.Error!Screen { 33 30 const w = winsize.cols; 34 31 const h = winsize.rows; 35 32 const self = Screen{ ··· 38 35 .height = h, 39 36 .width_pix = winsize.x_pixel, 40 37 .height_pix = winsize.y_pixel, 41 - .unicode = unicode, 42 38 }; 43 39 const base_cell: Cell = .{}; 44 40 @memset(self.buf, base_cell);
+2 -1
src/Vaxis.zig
··· 195 195 ) !void { 196 196 log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows }); 197 197 self.screen.deinit(alloc); 198 - self.screen = try Screen.init(alloc, winsize, &self.unicode); 198 + self.screen = try Screen.init(alloc, winsize); 199 199 self.screen.width_method = self.caps.unicode; 200 200 // try self.screen.int(alloc, winsize.cols, winsize.rows); 201 201 // we only init our current screen. This has the effect of redrawing ··· 226 226 .width = self.screen.width, 227 227 .height = self.screen.height, 228 228 .screen = &self.screen, 229 + .unicode = &self.unicode, 229 230 }; 230 231 } 231 232
+14 -6
src/Window.zig
··· 25 25 height: u16, 26 26 27 27 screen: *Screen, 28 + unicode: *const Unicode, 28 29 29 30 /// Creates a new window with offset relative to parent and size clamped to the 30 31 /// parent's size. Windows do not retain a reference to their parent and are ··· 49 50 .width = @min(width, max_width), 50 51 .height = @min(height, max_height), 51 52 .screen = self.screen, 53 + .unicode = self.unicode, 52 54 }; 53 55 } 54 56 ··· 205 207 206 208 /// returns the width of the grapheme. This depends on the terminal capabilities 207 209 pub fn gwidth(self: Window, str: []const u8) u16 { 208 - return gw.gwidth(str, self.screen.width_method, &self.screen.unicode.width_data); 210 + return gw.gwidth(str, self.screen.width_method, &self.unicode.width_data); 209 211 } 210 212 211 213 /// fills the window with the provided cell ··· 293 295 .grapheme => { 294 296 var col: u16 = opts.col_offset; 295 297 const overflow: bool = blk: for (segments) |segment| { 296 - var iter = self.screen.unicode.graphemeIterator(segment.text); 298 + var iter = self.unicode.graphemeIterator(segment.text); 297 299 while (iter.next()) |grapheme| { 298 300 if (col >= self.width) { 299 301 row += 1; ··· 376 378 col = 0; 377 379 } 378 380 379 - var grapheme_iterator = self.screen.unicode.graphemeIterator(word); 381 + var grapheme_iterator = self.unicode.graphemeIterator(word); 380 382 while (grapheme_iterator.next()) |grapheme| { 381 383 soft_wrapped = false; 382 384 if (row >= self.height) { ··· 415 417 .none => { 416 418 var col: u16 = opts.col_offset; 417 419 const overflow: bool = blk: for (segments) |segment| { 418 - var iter = self.screen.unicode.graphemeIterator(segment.text); 420 + var iter = self.unicode.graphemeIterator(segment.text); 419 421 while (iter.next()) |grapheme| { 420 422 if (col >= self.width) break :blk true; 421 423 const s = grapheme.bytes(segment.text); ··· 487 489 .width = 20, 488 490 .height = 20, 489 491 .screen = undefined, 492 + .unicode = undefined, 490 493 }; 491 494 492 495 const ch = parent.initChild(1, 1, null, null); ··· 503 506 .width = 20, 504 507 .height = 20, 505 508 .screen = undefined, 509 + .unicode = undefined, 506 510 }; 507 511 508 512 const ch = parent.initChild(0, 0, 21, 21); ··· 519 523 .width = 20, 520 524 .height = 20, 521 525 .screen = undefined, 526 + .unicode = undefined, 522 527 }; 523 528 524 529 const ch = parent.initChild(10, 10, 21, 21); ··· 535 540 .width = 20, 536 541 .height = 20, 537 542 .screen = undefined, 543 + .unicode = undefined, 538 544 }; 539 545 540 546 const ch = parent.initChild(10, 10, 21, 21); ··· 551 557 .width = 20, 552 558 .height = 20, 553 559 .screen = undefined, 560 + .unicode = undefined, 554 561 }; 555 562 556 563 const ch = parent.initChild(10, 10, 21, 21); ··· 565 572 const alloc = std.testing.allocator_instance.allocator(); 566 573 const unicode = try Unicode.init(alloc); 567 574 defer unicode.deinit(alloc); 568 - var screen: Screen = .{ .width_method = .unicode, .unicode = &unicode }; 575 + var screen: Screen = .{ .width_method = .unicode }; 569 576 const win: Window = .{ 570 577 .x_off = 0, 571 578 .y_off = 0, ··· 574 581 .width = 4, 575 582 .height = 2, 576 583 .screen = &screen, 584 + .unicode = &unicode, 577 585 }; 578 586 const opts: PrintOptions = .{ 579 587 .commit = false, ··· 633 641 defer unicode.deinit(alloc); 634 642 var screen: Screen = .{ 635 643 .width_method = .unicode, 636 - .unicode = &unicode, 637 644 }; 638 645 const win: Window = .{ 639 646 .x_off = 0, ··· 643 650 .width = 4, 644 651 .height = 2, 645 652 .screen = &screen, 653 + .unicode = &unicode, 646 654 }; 647 655 const opts: PrintOptions = .{ 648 656 .commit = false,
+9 -10
src/widgets/View.zig
··· 18 18 /// Underlying Screen 19 19 screen: Screen, 20 20 21 + unicode: *const Unicode, 22 + 21 23 /// View Initialization Config 22 24 pub const Config = struct { 23 25 width: u16, ··· 26 28 27 29 /// Initialize a new View 28 30 pub fn init(alloc: mem.Allocator, unicode: *const Unicode, config: Config) mem.Allocator.Error!View { 29 - const screen = try Screen.init( 30 - alloc, 31 - .{ 31 + return .{ 32 + .alloc = alloc, 33 + .screen = try Screen.init(alloc, .{ 32 34 .cols = config.width, 33 35 .rows = config.height, 34 36 .x_pixel = 0, 35 37 .y_pixel = 0, 36 - }, 37 - unicode, 38 - ); 39 - return .{ 40 - .alloc = alloc, 41 - .screen = screen, 38 + }), 39 + .unicode = unicode, 42 40 }; 43 41 } 44 42 ··· 51 49 .width = self.screen.width, 52 50 .height = self.screen.height, 53 51 .screen = &self.screen, 52 + .unicode = self.unicode, 54 53 }; 55 54 } 56 55 ··· 142 141 143 142 /// Returns the width of the grapheme. This depends on the terminal capabilities 144 143 pub fn gwidth(self: View, str: []const u8) u16 { 145 - return gw.gwidth(str, self.screen.width_method, &self.screen.unicode.width_data); 144 + return gw.gwidth(str, self.screen.width_method, &self.unicode.width_data); 146 145 } 147 146 148 147 /// Fills the View with the provided cell