Clay – ไลบรารีเลย์เอาต์ UI
(nicbarker.com)- Clay เป็นไลบรารีเลย์เอาต์ UI อัตโนมัติสไตล์ Flexbox ที่สร้างด้วยภาษา C โดยมุ่งให้สามารถจัดวาง UI แบบประกาศได้ แม้ในเอนจินแบบกำหนดเองหรือสภาพแวดล้อมระดับต่ำ
- แจกจ่ายในรูปแบบ ไฟล์เฮดเดอร์ เดียว ใช้งานได้ใน C/C++ และเน้นว่ามีโค้ด C99 ประมาณ 2,000 บรรทัด พร้อมผลลัพธ์ที่คอมไพล์เป็น
.wasmขนาด 15KB - ไม่มี dependency แม้รวมถึง C standard library และสามารถใช้งานแยกจากวิธีเรนเดอร์อย่าง Raylib, WebGL Canvas, HTML ได้
- เร็วพอที่จะคำนวณ UI ทั้งหมดใหม่ทุกเฟรม ใช้ หน่วยความจำพื้นฐาน 3.5MB และทำงานด้วยการจัดสรรแบบ static กับการนำกลับมาใช้ซ้ำ โดยไม่ต้องใช้
malloc/free - สามารถผสมลูป เงื่อนไข และฟังก์ชันของโค้ด C เข้ากับลำดับชั้น UI ได้ และตรวจสอบสถานะเลย์เอาต์แบบเรียลไทม์ด้วยเครื่องมือดีบักในตัว
เฮดเดอร์เดี่ยว C99 และโมเดลหน่วยความจำ
- Clay เป็นไลบรารีเลย์เอาต์อัตโนมัติสไตล์ Flexbox บนพื้นฐานภาษา C
- ใช้ไวยากรณ์แบบ declarative
- ชูจุดเด่นด้านประสิทธิภาพระดับไมโครวินาที
- เว็บเพจปัจจุบันเองก็จัดเลย์เอาต์ด้วย Clay
- แจกจ่ายในรูปแบบ ไฟล์เฮดเดอร์ เดียว
- ใช้ได้ใน C/C++
- include ด้วยรูปแบบ
#include clay.h - ประกอบด้วยโค้ด C99 ประมาณ 2,000 บรรทัด
- ไม่มี dependency รวมถึง C standard library
- เมื่อคอมไพล์เป็น
.wasmจะมีขนาด 15KB
- แนวทางด้านประสิทธิภาพและการใช้หน่วยความจำออกแบบมาโดยคำนึงถึง การอัปเดต UI แบบเรียลไทม์
- เร็วพอที่จะคำนวณ UI ทั้งหมดใหม่ได้ทุกเฟรม
- การใช้หน่วยความจำพื้นฐานคือ 3.5MB
- ใช้การจัดสรรแบบ static และการนำกลับมาใช้ซ้ำ
- ไม่ใช้
mallocและfree - สามารถทำให้การออกแบบ animation และ UI แบบ responsive ง่ายขึ้นได้ โดยไม่ต้องใช้เทคนิคหลบเลี่ยงด้านประสิทธิภาพแบบมาตรฐาน
UI แบบ declarative และการแยก renderer
- ไวยากรณ์แบบ declarative เน้นการจัดลำดับชั้นขององค์ประกอบ UI ที่ซ้อนกันให้อ่านง่าย
- สามารถผสมองค์ประกอบ UI เข้ากับลูป เงื่อนไข และฟังก์ชันของภาษา C ได้
- สามารถสร้างไลบรารีคอมโพเนนต์ที่นำกลับมาใช้ซ้ำได้จากองค์ประกอบพื้นฐานของ UI เช่น ข้อความ รูปภาพ และสี่เหลี่ยม
- Clay ไม่ผูกติด กับ renderer และแพลตฟอร์ม
- Clay จัดการเลย์เอาต์ ส่วนการเรนเดอร์ทำได้ด้วย Raylib, WebGL Canvas, HTML ฯลฯ
- เอาต์พุตเป็นอาร์เรย์ที่เรียงลำดับของคำสั่งเรนเดอร์พื้นฐาน เช่น
RECTANGLE,TEXT,IMAGE - สามารถเขียน renderer เองด้วยโค้ดไม่กี่ร้อยบรรทัด หรือใช้ตัวอย่างที่มีให้
- มี HTML renderer ให้ด้วย และหน้าปัจจุบันนี้เป็นตัวอย่างดังกล่าว
- รวม เครื่องมือดีบัก ในตัว
- มีเครื่องมือดีบักสไตล์ Chrome Inspector
- สามารถดู hierarchy ของเลย์เอาต์และการตั้งค่าได้แบบเรียลไทม์
- สามารถกดปุ่ม
dบนหน้าเว็บเพื่อทดลองใช้เครื่องมือดีบักได้
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ดูดี และวิดีโอ YouTube ที่นักพัฒนาอธิบายเองก็ยอดเยี่ยม: https://www.youtube.com/watch?v=DYWTw19_8r4
รูปแบบสมัยใหม่คือ GUI แบบใช้เมาส์ที่ Xerox สร้างขึ้นในทศวรรษ 1970 ต่อมาถูกทำเป็นเชิงพาณิชย์ใน Xerox Star และเป็นสายธารที่ Jobs ได้รับอิทธิพลไปสู่ Apple Lisa ส่วน Gates ก็ได้รับอิทธิพลไปสู่ MS Windows
GUI framework เป็นตัวกำหนดหน้าตาและความรู้สึกของแพลตฟอร์ม และชี้ขาดว่านักพัฒนาจะสร้างแอปได้ง่ายแค่ไหน จึงกลายเป็นแกนหลักของการแข่งขันระหว่างแพลตฟอร์มหลายแขนง เช่น ระบบปฏิบัติการ เบราว์เซอร์ และกราฟิกเอนจิน
ค่อนข้างเจ๋งที่ทำได้ด้วยโค้ดไม่กี่พันบรรทัด
แต่ผมชอบ CSS Grid มากกว่า Flex เลยทำไลบรารีเลย์เอาต์ CSS Grid ด้วย Nim ล้วน ๆ และค่อนข้างภูมิใจกับมัน: https://github.com/elcritch/cssgrid
อยากลองดู Clay และเปรียบเทียบอัลกอริทึมเลย์เอาต์ด้วย การได้เห็นกล่องปรับขนาดตัวเองด้วยอัลกอริทึมที่เราเขียนเองมันสนุกดี
และก็สงสัยว่าจะเปิดเผย C interface ได้ไหม
รองรับทั้ง Flexbox และ Block layout ด้วย และไลเซนส์ทั้งคู่ก็ดูเหมือน MIT ดังนั้นจะหยิบส่วนที่ต้องใช้ไปก็ได้ แต่อาจเพิ่มไฟล์ LICENSE ไว้จะได้หาเจอง่ายขึ้น
เป้าหมายคือให้เข้ากันได้กับเว็บอย่างสมบูรณ์ แต่ยังไปไม่ถึงเต็มที่ และส่วนที่น่าจะน่าสนใจเป็นพิเศษคือชุดทดสอบที่ค่อนข้างใหญ่
การทดสอบนิยามเป็นชิ้นส่วน HTML แล้วรัน Chrome ผ่าน webdriver เพื่อดึง assertion ที่ถูกต้องออกมา จากนั้นแปลงเป็น unit test แบบโค้ดล้วน หากต้องการก็เขียนตัวสร้างการทดสอบเองแล้วนำชิ้นส่วนของเราไปใช้ซ้ำได้
โครงสร้างพื้นฐานการทดสอบนี้ยังแชร์บางส่วนกับ Yoga ซึ่งเป็นอิมพลีเมนต์ Flexbox ภาษา C++ ที่ขับเคลื่อน React Native: https://github.com/facebook/yoga
ในกรณีง่าย ๆ ที่เซลล์ไม่ได้คร่อมหลายช่องเหมือน Flexbox จะทำได้ค่อนข้างง่าย แต่ถ้าไม่ใช่ก็ไม่ธรรมดา จึงต้องใช้แนวทางอย่างตัวแก้แบบซิมเพล็กซ์หรือ Cassowary: https://constraints.cs.washington.edu/solvers/cassowary-tochi.pdf
ตอนท้ายมีปุ่มสลับระหว่าง HTML กับ Canvas ซึ่งปกติแทบไม่เห็นความต่างเลย ถือว่าเจ๋ง
แต่บน iOS Safari ร่วมกับ Dark Reader หน้า HTML จะเปลี่ยนเป็นโหมดมืด แต่หน้า Canvas ไม่เปลี่ยน เลยทำให้จุดว้าวสำหรับผมหายไป
ถึงอย่างนั้นก็ดูดี
Canvas ก็เร็วพออยู่แล้ว แต่รู้สึกได้ถึงความต่างตอนดูแอนิเมชัน “High performance” หรือเลื่อนหน้า
เลือกข้อความทั้งหมดที่อยู่หลังแอนิเมชันไม่ได้
เหมือนมีอะไรบางอย่างแย่งโฟกัสไป และพอพยายามเลือกข้อความ การเลือกก็ถูกยกเลิกทันที
ที่น่าสนใจคือ Firefox เรนเดอร์หน้านี้ได้แย่กว่ามาก: https://imgur.com/a/DNYe2WN
เป็นการตีความสไตล์ UI ที่ผมชอบมากได้อย่างน่าพอใจ
วิธีแยกตรรกะ UI กับการวาดออกเป็นชุดคำสั่งการวาดนั้นยอดเยี่ยมและยืดหยุ่นมาก ผมเห็นครั้งแรกใน microui และการแยกแบบนั้นทำให้ใช้ไลบรารีบนเบราว์เซอร์ผ่าน WASM กับ Canvas2D ได้ง่าย: https://rxi.github.io/microui_v2_an_implementation_overview.html
อีกอย่าง ไอเดียการคำนวณเลย์เอาต์ใน WASM แล้วเรนเดอร์เป็น HTML ก็ดีมาก ไม่รู้ทำไมก่อนหน้านี้ถึงไม่เคยนึกถึง
ตอนแรกคิดว่าจำเป็นต้องทำด้วย C ไหม ใช้ Haskell หรือ OCaml น่าจะทำให้ปลอดภัยกว่าได้หรือเปล่า
แต่ถ้าเป็นโค้ด C 2000 บรรทัดและไม่มี dependency ก็ถือว่าเจ๋งทีเดียว
มี taffy ที่เขียนด้วย Rust ด้วย และ C binding กำลังอยู่ระหว่างทำ: https://crates.io/crates/taffy
ถือว่าดีสำหรับฉบับร่างแรก
แต่ก็น่าเสียดายที่เอาต์พุต HTML มีแต่ div element และถ้าใส่ใจเรื่องการเข้าถึงอีกสักหน่อยก็น่าจะดีขึ้นมาก
อีกปัญหาคือเมื่อพยายามเลือกข้อความในหลายจุด มันจะเรนเดอร์ใหม่จนการเลือกถูกยกเลิกก่อนจะกด control-c ได้
ถ้าถามกันจริงจัง ผมว่าแม้แนวคิดจะดูเจ๋ง แต่การเขียนเว็บเพจแบบนี้อาจจะเกินไปหน่อยหรือเปล่า
เป็นรูปแบบที่มีการเรียกอย่าง
CLAY(...),CLAY_TEXT(...),LandingPageBlob(...)ซ้อนลึกอยู่ในLandingPageDesktop()และซอร์สตัวอย่างอยู่ที่นี่: https://github.com/nicbarker/clay/blob/35d72e5fba6872be48d15ed9d84269a86cd72b4e/examples/clay-official-website/main.c#L50C1-L67C2AWT, SWT, Swing, Qt, Fyne และอื่น ๆ ก็คล้ายกันหมด
https://docs.oracle.com/javase/7/docs/api/java/awt/GridLayout.html
https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet75.java
https://stackoverflow.com/a/12867862/243613
https://stackoverflow.com/questions/37304684/qwidgetsetlayout-attempting-to-set-qlayout-on-mainwindow-which-already
จริง ๆ แล้ว Qt มีระบบเลย์เอาต์ที่ดูเหมือนอิง XML แต่ก็เขียนด้วยโค้ดได้เช่นกัน และโดยทั่วไปไม่แนะนำให้ทำ
https://gist.github.com/ledongthuc/9686787fe51bbe763fa1e50389d63351
ถ้าแยกแต่ละส่วนออกเป็นฟังก์ชันที่มีชื่อ ก็จะทำให้โครงสร้างอ่านง่ายขึ้น เช่น
LandingPageDesktop(...),LandingPage(...),LeftText()ปัญหาจริง ๆ อยู่ที่การพยายามแสดง วัตถุกราฟิกและความสัมพันธ์ของมัน ด้วยข้อความ เครื่องมือสร้างกราฟิกหรือเครื่องมือ RAD ดูเหมือนจะเป็นทางออกที่ชัดเจน แต่แนวทางแบบนั้นโดยมากถูกละทิ้งไปแล้ว
เป็นงานที่ยอดเยี่ยม ผมคิดว่าในเบราว์เซอร์ ข้อความที่เลือกได้ เป็นสิ่งจำเป็น
สำหรับไคลเอนต์และแอปที่ไม่ต้องการฟังก์ชันแบบนั้น หรือสามารถ提供เองได้ นี่ดูเป็นโซลูชันที่เล็กมากและดีมาก
อาจจะพูดเรื่องเดิมซ้ำอีกแล้ว แต่ผมรู้สึกว่าในบรรดาการเปลี่ยนแปลงของ UI ช่วงไม่กี่ปีที่ผ่านมา การถดถอยของ ความเป็นสากลของคลิปบอร์ด เป็นหนึ่งในความสูญเสียที่ถูกประเมินต่ำที่สุด ควบคู่ไปกับการถดถอยของความสามารถในการลิงก์และการฝังเนื้อหา