Gio UI - GUI ข้ามแพลตฟอร์มสำหรับ Go
(gioui.org)- เป็นไลบรารีที่ช่วยให้นักพัฒนา Go สามารถสร้าง GUI แบบ immediate mode สำหรับหลายระบบปฏิบัติการและ WebAssembly ได้
- รองรับ ขอบเขตแพลตฟอร์ม ที่กว้าง ตั้งแต่ Linux, macOS, Windows, Android, iOS, FreeBSD, OpenBSD ไปจนถึง WebAssembly
- ออกแบบมาเพื่อลดการพึ่งพา และใช้ ไลบรารีของแต่ละแพลตฟอร์ม สำหรับการจัดการหน้าต่าง, อินพุต และการวาดด้วย GPU
- การเรนเดอร์มี Pathfinder vector renderer ที่ทำงานบน OpenGL ES และ Direct3D 11 และกำลังย้ายไปใช้คอมพิวต์เชดเดอร์เรนเดอเรอร์ที่อิงกับ piet-gpu
- เรนเดอร์ข้อความและรูปทรงด้วย เส้นขอบ แทนการ bake เป็นเท็กซ์เจอร์ จึงรองรับแอนิเมชัน, การวาดแบบแปลงรูป, และความเป็นอิสระจากความละเอียดพิกเซล
จุดประสงค์และขอบเขตการรองรับของ Gio
- Gio เป็นไลบรารีสำหรับสร้าง GUI ที่มีประสิทธิภาพ ลื่นไหล และพกพาได้ด้วย Go
- แพลตฟอร์มที่รองรับคือ Linux, macOS, Windows, Android, iOS, FreeBSD, OpenBSD, WebAssembly
- มีเดโม WebAssembly สำหรับการสาธิตอย่างรวดเร็ว และต้องใช้ เบราว์เซอร์ที่รองรับ WebAssembly ในการรัน
- สามารถดูซอร์สตัวอย่างได้ที่ Kitchen project
การติดตั้งและเส้นทางการเรียนรู้
- Gio ถูกออกแบบโดยมีเป้าหมายคือ การพึ่งพาให้น้อย
- สามารถตรวจสอบ dependency ที่จำเป็นได้จากเอกสารการติดตั้งของแต่ละแพลตฟอร์ม
- หลังติดตั้งแล้วสามารถเริ่มได้จากเอกสาร Learn และ Hello World
- ในโชว์เคสมี godcr, Tailscale, gotraceui, Sointu, Protonet รวมอยู่ด้วย
เทคโนโลยีการเรนเดอร์
- Gio ผสานความยืดหยุ่นของแนวคิดกราฟิกแบบ immediate mode เข้ากับ เทคโนโลยีกราฟิก 2D สมัยใหม่
- vector renderer อิงจาก Pathfinder project และทำงานบน OpenGL ES และ Direct3D 11
- เรนเดอเรอร์กำลังย้ายไปสู่ เรนเดอเรอร์แบบคอมพิวต์เชดเดอร์ ที่มีประสิทธิภาพมากกว่า ซึ่งสร้างบน piet-gpu
- ข้อความและรูปทรงจะ เรนเดอร์โดยใช้เฉพาะเส้นขอบ โดยไม่ bake ล่วงหน้าเป็นภาพเท็กซ์เจอร์
- รองรับแอนิเมชันได้อย่างมีประสิทธิภาพ
- เหมาะกับการวาดที่มีการแปลงรูป
- คงความเป็นอิสระจากความละเอียดพิกเซลได้
โมเดลการสนับสนุน
- เงินทุนสำหรับการพัฒนา Gio มาจาก การสนับสนุน
- หากโปรเจกต์นี้มีประโยชน์ คุณสามารถพิจารณาสนับสนุนได้ที่ Gio project บน OpenCollective หรือสนับสนุนนักพัฒนาโดยตรง
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
พอลองใช้งานจริงแล้ว รู้สึกว่าเอามาสร้าง แอปที่ซับซ้อนจริงจัง ไม่ได้
ไม่มีคอมโพเนนต์อย่างวิดีโอ แผนที่ หรือ rich text ที่แพลตฟอร์มอื่นมีมาให้เป็นพื้นฐาน และก็ไม่มีแนวทางที่ชัดเจนและง่ายในการเพิ่มเข้าไปเอง
API พังทุก ๆ ไม่กี่เดือน และก็ไม่มีวิธีใส่ธีมด้วย
กราฟิกแบบ immediate mode นั้นดีจนกว่าจะต้องจัดการ state ที่ซับซ้อน แต่พอถึงจุดนั้นสุดท้ายก็ต้องไปทำ retained mode graphics ขึ้นมาเอง เลยกลายเป็นการดึงปัญหาเก่าที่ถูกแก้ไปนานแล้วกลับมาอีก
เรนเดอเรอร์หรู ๆ ที่อิงกับ
piet-gpuก็รับอินพุตเป็นแค่จุดควบคุมของเส้นเบซิเยร์แล้วทำ tessellation ทั้งหมด ดังนั้นแม้คอนเซปต์จะดูเท่ แต่เวลาวาดวงกลมจริง ๆ ก็ต้องพึ่งการประมาณด้วยเบซิเยร์ 4 เส้นWasm ยังใกล้เคียงกับงานพิสูจน์แนวคิดที่ทีมคอมไพเลอร์ต้องขัดเกลาเชิงวิศวกรรมอีกหลายปีกว่าจะถึงระดับพร้อมใช้งานจริง และโดยรวมแล้วมันดูโอเคสำหรับนักพัฒนา Go ที่จะทำ UI เรียบง่ายอย่างลิสต์กับช่องกรอกข้อมูล
มีธีม Material Design และมีทั้งโหมดสว่าง/มืด
ตัวอย่างแอป gioui ที่ยอดเยี่ยมซึ่งมีทั้งธีมสว่าง/มืดและธีมแบบกำหนดเองคือ https://github.com/chapar-rest/chapar
บน Mac หรือ Windows ก็แค่
go run .kerning ของข้อความ, ข้อความที่ไหลตามส่วนโค้ง, และ RTL/LTR ก็ทำได้ด้วย
github.com/go-text/typesettingวิดเจ็ตซับซ้อนอย่าง calendar spinner หรือไดอะแกรมก็มีอยู่บน GitHub แต่ยังขาดความพยายามในการรวบรวมสิ่งเหล่านี้เข้าด้วยกัน
ถ้าสิ่งเหล่านี้ถูกรวมกันได้ ก็น่าจะมีแรงจูงใจมากพอให้มีนักพัฒนาเข้ามามากขึ้น
ดูเหมือนจะไม่มีวัฒนธรรมที่ให้ความสำคัญกับการไม่ทำให้โค้ดของผู้ใช้พัง
ตอนนี้ทั้งคู่เสถียรและมีชุดคอนโทรลกับไลบรารีที่ค่อนข้างครบ
บนเว็บมันดูเหมือน Flutter ตรงที่เรนเดอร์ทุกอย่างลงบนแคนวาส ซึ่งเป็นแนวทางที่ขึ้นชื่อว่ามีปัญหาเรื่อง การเข้าถึง และความรู้สึกแบบเนทีฟ
ใช้ Tab ย้ายระหว่างปุ่มวิทยุไม่ได้ และบน macOS
CMD+Aก็ไม่ได้เลือกข้อความทั้งหมดในช่องข้อความ แต่CTRL+Aทำได้ฟังดูน่าจะทำได้ แต่ปริมาณงานก็คงไม่น้อย
แม้จะออกนอกประเด็นไปนิด แต่ช่วงนี้ผมสงสัยว่าวิธีที่ดีที่สุดในการสร้าง แอปมือถือและเว็บแบบข้ามแพลตฟอร์ม คืออะไร
ไม่ว่าจะเป็นกรณีที่แชร์ทั้ง business logic และ UI หรือแชร์แค่ business logic ก็ตาม
เคยชั่งใจระหว่างตัวเลือกอย่าง gomobile, Rust, TypeScript
ช่วงหนึ่ง TypeScript ดูเหมือนเป็นเทคโนโลยีที่พกพาได้ดีที่สุดจนคิดจะใช้กับ business logic ทั้งหมด แต่ก็พบว่าไม่มีวิธีดี ๆ ในการรัน JavaScript บน iOS ด้วยประสิทธิภาพที่น่าพอใจ
ถ้าเขียน UI แบบ native และแชร์เฉพาะ business logic ก็ใช้ Kotlin ได้เช่นกัน: https://kotlinlang.org/docs/multiplatform.html#kotlin-multip...
ถ้าใช้ Compose ก็ทำ UI ด้วย Kotlin ได้: https://www.jetbrains.com/lp/compose-multiplatform/
แต่การรองรับ iOS ยังอยู่ในระดับ alpha และเว็บยังเป็น “experimental” ดังนั้นถ้าไม่อยากรับความเสี่ยงที่ต้องแก้โค้ดตามการพัฒนาของเฟรมเวิร์ก Flutter ที่ค่อนข้างเสถียรแล้วในทุกแพลตฟอร์มน่าจะเหมาะกว่า
ถ้าคุ้นกับ TypeScript และ React อยู่แล้ว React Native ก็เป็นตัวเลือกที่น่าพิจารณา แต่เรื่องประสิทธิภาพบน iOS หรือที่อื่น ๆ ยังพูดรับประกันได้ยาก: https://reactnative.dev/
เจอเฟรมเวิร์กที่สัญญาว่าจะแก้ได้ทุกอย่างแต่ทำไม่ได้จริงมาเยอะเกินไปแล้ว
ตอนแรกเริ่มต้นได้เร็วกว่า แต่ไม่นานหลังจากนั้นพอมี OS อัปเดต ก็ต้องไปแพตช์ไลบรารีหลักเพื่อให้ FPS ใกล้เคียง native หรือให้ system animation ดูเหมือนเดิมมากขึ้น
จะประหยัดเวลาได้ก็ต่อเมื่อไม่ได้ตั้งใจขัดเกลา UI
แต่ logic หลักยังแชร์กันได้
ผมใช้ gomobile และโดยรวมก็ชอบ แต่ runtime overhead 3MB ทำให้ไม่เหมาะกับเว็บ
Kotlin Multiplatform ดูดี แต่ขาดไลบรารีพื้นฐานหลายอย่าง และเพราะ Kotlin Android มีของพวกนี้อยู่แล้ว คนเลยไม่ค่อยทำเวอร์ชันข้ามแพลตฟอร์มกัน
Rust กับชั้น language binding ของ Mozilla ก็ดูน่าสนใจ แต่ยังไม่ได้ลองใช้
Flutter ก็ไม่เลว แต่บนเว็บมันเรนเดอร์ลง canvas เลยให้ความรู้สึกและการเข้าถึงที่ไม่ค่อยดี
บน iOS เองก็ยังมีปัญหาเรื่อง latency แม้จะใช้เอนจินเรนเดอร์ Impeller แล้วก็ตาม
ดูจากไคลเอนต์ Bluesky แล้ว ประสิทธิภาพถือว่าโอเคในทุกแพลตฟอร์มที่รองรับ และใช้ codebase เดียว
https://github.com/bluesky-social/social-app
เป็นโอเพนซอร์ส และรองรับ Android, iOS, Windows, Mac, Linux: https://platform.uno/platforms/
ใช้ C# และสร้าง view กับ control โดยอัตโนมัติด้วย native UI framework ของแต่ละแพลตฟอร์ม
รองรับ IDE อย่าง Visual Studio, VS Code, Rider ได้ดี และไม่ได้ถูกจำกัดว่าต้องใช้เครื่องมือเฉพาะ
ยังมีปลั๊กอิน Figma สำหรับการทำงานร่วมกับทีมออกแบบด้วย
ไม่แน่ใจว่าเป็นเครื่องมือที่ดีสำหรับผลิตภัณฑ์ผู้บริโภคด้วยหรือไม่
แต่สำหรับงานที่เราใช้มันเหมาะมาก เพราะส่วนใหญ่เป็นเครื่องมือสำหรับช่างเทคนิคโรงไฟฟ้าพลังงานแสงอาทิตย์ และการทำ TypeScript แบบข้ามแพลตฟอร์มในสภาพอินเทอร์เน็ตที่แย่มากกลายเป็นภาระหนักเกินไปสำหรับทีมเล็ก ๆ
ผมกำลังทำ แอปสตรีมมิง ด้วย gioui อยู่ และมันง่ายมาก แถมการอัปเกรดก็ราบรื่นเสมอ
เพราะมันเป็น Go และทีมพัฒนาหลักก็จริงจังกับการเปลี่ยนแปลงพอสมควร
เวลาต้องการเว็บ GUI ก็ใช้ระบบปลั๊กอิน gioui นี้: https://github.com/gioui-plugins/gio-plugins
น่าทึ่งที่ WebView ใช้งานได้ทั้งบนเว็บ เดสก์ท็อป และมือถือ
รองรับ deep link ด้วย ดังนั้นถ้าส่งอีเมลหรือลิงก์การแจ้งเตือน Monike แอปของผู้ใช้จะเปิดไปยังตำแหน่งที่ถูกต้องใน GUI ได้เลย
ยังมีการแจ้งเตือนและ share extension สำหรับทุก OS จึงมองว่าเกือบเป็นระบบที่สมบูรณ์แล้วจริง ๆ
ผมเข้าใจดีว่าการรองรับทุก OS เป็นเรื่องยาก แต่โลกทุกวันนี้มีความหลากหลายเป็นพื้นฐานอยู่แล้ว
ผมชอบที่ทำทั้งหมดนี้ได้ด้วย Go อย่างเดียว โดยไม่ต้องสลับไปมาระหว่างหลายเทคโนโลยี
ฝั่ง Go backend จะเขียนให้ทำงานได้เสมอทั้งกับ gio และ HTML
ถ้าต้องการ SEO หรือการเล่นวิดีโอ ก็จัดการใน WebView ได้ และยังทำให้ Google SEO ฝั่งเว็บของ gio พอใจได้ด้วย
ใส่ Markdown ลงใน Hugo เพื่อให้ Google SEO มองเห็น
ในมุมของคนที่เพิ่งเริ่มใช้ Go ฉันสงสัยส่วนนี้ของเอกสาร
มีการบอกว่าเหตุผลที่ใช้
op.ColorOp{Color: red}.Add(ops)แทนops.Add(ColorOp{Color: red})ก็เพื่อไม่ให้เมธอดAddรับอาร์กิวเมนต์ชนิด interface และหลีกเลี่ยง allocation ตอนเรียกใช้ ซึ่งเป็นแกนสำคัญของแนวทางออกแบบแบบ “zero allocation” ของ Gioอยากรู้ว่าทำไมถึงเกิด allocation, มีอะไรที่ถูก allocate, และประหยัดได้อย่างไร
ถ้าฟังก์ชันรับอาร์กิวเมนต์เป็นชนิด interface แล้วส่ง struct ตรง ๆ เข้าไป Go จะสร้าง wrapper รอบค่านั้น และนี่คือ allocation ที่อ้างถึงในข้อความ
wrapper นี้เป็นคู่ของพอยน์เตอร์ ซึ่งประกอบด้วยพอยน์เตอร์ไปยัง type/vtable และพอยน์เตอร์ไปยังข้อมูลของ struct
วิธีนี้ทำให้มีทั้งการอนุมานชนิดตอนรันไทม์และการขยาย interface แบบ implicit ได้
กล่าวคือ การ implement interface แค่มีเมธอดครบก็พอ ไม่จำเป็นต้องประกาศชนิดแบบชัดเจนเหมือน
ByteReader extends Readerค่าใช้จ่ายนี้จะเกิดเฉพาะตอนใช้งาน ดังนั้นโค้ด fast path จำนวนมากจึงพยายามใช้แต่ struct ถ้าเป็นไปได้
v := interfaceType(concreteTypeValue)สิ่งที่เกิดขึ้นในระดับล่างจะประมาณนี้dataPtr := &concreteTypeValue,typePtr := typeData[concreteType]()แล้วสร้างค่า interface ที่มีทั้ง data pointer และ type pointerตรงนี้บรรทัดแรกคือ allocation เพราะตามกฎที่ฉันจำได้ พอยน์เตอร์ใน Go จะไม่ชี้ไปยังค่าบนสแตก ดังนั้น
concreteTypeValueจึงต้องถูก allocate บนฮีปกฎที่ว่าพอยน์เตอร์ไม่ชี้เข้าสแตกมีไว้เพื่อให้สแตกของ goroutine ขยายแบบไดนามิกได้ง่าย
ดู https://go.dev/doc/faq#stack_or_heap
ColorOp{Color: red}ต้องถูก boxing และ allocate ลงฮีปเพราะโดยทั่วไป
ops.Addรับ fat pointer ไปยังค่าที่ implement interface หนึ่ง ไม่ได้รับค่า concrete type โดยตรงop.ColorOp{Color: red}.Add(ops)อ่านแล้วแปลกสำหรับฉันมันเหมือน “เอา
opsไปบวกกับผลลัพธ์ของop.ColorOp{Color: red}”เลยอยากตั้งชื่อฟังก์ชันเป็น
AddTo:op.ColorOp{Color: red}.AddTo(ops)ถึงจะยังไม่ค่อยเป็นธรรมเนียมนัก แต่ก็อย่างน้อยส่งสัญญาณว่าอาร์กิวเมนต์ของฟังก์ชันจะถูกแก้ไข
น่าสนใจที่บนพีซีค่อนข้างธรรมดาซึ่งใช้ Windows 10 และ Chrome นั้น เดโม WASM ในหน้าแรกเรนเดอร์เป็นสี่เหลี่ยมสีดำตรงตำแหน่งที่ควรมีข้อความ
แต่บน Chrome ของโทรศัพท์ Android กลับเรนเดอร์ได้ถูกต้อง
แถมยังทำงานช้ามากด้วย
ฉันเคยทำแอปเล็ก ๆ ด้วย Fyne บน Go และคงไม่ใช้อีกแล้ว
ทั้ง Gio และ Fyne ยังขาดความขัดเกลาและความสามารถที่ Flutter ให้มาอย่างมาก
ฉันลงเอยด้วยการเขียนแกนหลักเป็น Golang แล้วห่อเป็นแอป Android เพราะ GUI ดูเหมือนหลุดมาจากปี 2003 และแทบไม่มีทางแก้ได้มากนัก
คุณเขียน logic ทั้งหมดด้วย Go ได้ และเขียน UI เป็น HTML จะใช้เว็บเฟรมเวิร์กหรือไม่ใช้ก็ได้
มันคล้าย Electron แต่เบากว่าเพราะใช้ system web viewer แทนการ bundle Chrome มาด้วย
[1] https://github.com/wailsapp/wails
ถ้าอธิบายได้ว่าเอาไปห่อเป็นแอป Android อย่างไรจะดีมาก
ทำไม GUI ข้ามแพลตฟอร์ม พวกนี้ถึงดูเหมือนถูกออกแบบมาเมื่อ 50 ปีก่อนกันหมด
เดโมใช้ไม่ได้สำหรับฉัน
บน Chromium ของ Win 11 เห็นปุ่มอยู่ไม่กี่ปุ่ม แต่ที่เหลือเกือบทั้งหมดเป็นสีดำ
ต่างจาก Fyne ตรงที่ไลบรารีนี้ผ่านบททดสอบแรกที่ฉันโยนให้มันคือ การเรนเดอร์ข้อความ CJK ซึ่งถือเป็นสัญญาณที่ดี
Fyne ทำแบบนี้ไม่ได้ถ้าไม่ป้อนฟอนต์แบบกำหนดเองเพียงตัวเดียวที่ใช้เรนเดอร์ทุกอย่าง
การหาฟอนต์ตัวเดียวที่ครอบคลุมระบบอักษรที่ใช้กันทั่วไปทั่วโลกทั้งหมด รวมถึงอีโมจิ ได้อย่างน่าพอใจนั้นแทบต้องพึ่งดวง
เพราะงั้นถ้าจะทำอะไรที่มีเนื้อหาจากผู้ใช้, เนื้อหาเว็บ หรือมีโอกาสต้องรองรับการแปลภาษาแม้แต่น้อย Fyne ก็หลุดออกจากตัวเลือกของฉันทันที