www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Fluid 0.7.0 has been released!

reply cookiewitch <artha samerion.com> writes:
After 9 months of development and 252 commits later, finally 
0.7.0 gets to see the sunlight! This is the biggest update to 
Fluid yet, almost twice as big as 
[0.6.0](https://git.samerion.com/Samerion/Fluid/releases/tag/v0.6.0), and also
almost half the size of Fluid itself. In fact, so big, that I decided to delay
some of the changes to a [separate future
update](https://git.samerion.com/Samerion/Fluid/milestone/19); too big.

**[Fluid is a general-purpose user interface 
library](https://git.samerion.com/Samerion/Fluid)**. It is D 
first, and comes with Raylib support. While largely a work in 
progress, and of pre-release quality, it's already mostly stable 
and ready for use.

Start a new Fluid project [with DUB](https://fluid.dub.pm):

```shell
dub fetch fluid
dub init -t fluid myproject
```

Add Fluid to your existing project:

```
dub add fluid
```

Among other stuff, this update addresses feedback from community 
members. Folks reported a lot of issue with building this thing. 
Fluid now has CI coverage and should work just fine on all the 
three major platforms. Fluid's tour now also features an 
experimental, fully interactive "moduleView" chapter, which at 
the moment only works properly on Linux.

In short term, I hope to improve Fluid's documentation and add a 
number of missing features, mostly nodes. In long term, I would 
like to work on animations and reactive programming.

[Full changelog can be seen on the releases 
page](https://git.samerion.com/Samerion/Fluid/releases/tag/v0.7.0)



Most importantly, 0.7.0 introduces a reworked theme system, one 
much more readable than the last:

```d
import fluid.theme;  // explicit import needed!

auto theme = Theme(
     rule!Node(
         typeface        = Style.loadTypeface("noto.ttf"),
         fontSize        = 12.pt,
         textColor       = color("#fff"),
     ),
     rule!Frame(

     ),
);
```

`when` rules can be used to alter the style based on any of the 
node's properties. Multiple `when` rules can be applied at the 
same time.

```d
border          = 1,


when!"a.isHovered"(

),
when!"a.isFocused"(
     borderStyle = colorBorder(color("#fff")),
),
when!(a => a.text.isValidPassword)(

),
```

Rules can now also affect children nodes through `children`:

```d
rule!PopupFrame(
     border = 1,

     children!Button(  // style all buttons inside PopupFrames
         margin = 0,
         padding.sideX = 4,
         padding.sideY = 2,
     )
),
```

For more advanced usecases, values can also be picked based on 
runtime values:

```d
rule!Node(
     a => rule(
         backgroundColor = pickColor(a.score),
     ),
),
```



[This feature was covered on this year's 
DConf!](https://youtu.be/AzezZhvIyS4?t=21141)

In the previous releases, `Layout` and `Theme` were taken 
directly as constructor arguments, which could be really 
confusing, and at the same time, limiting. Now, the `nodeBuilder` 
takes care of this automatically:

```d
alias label = nodeBuilder!Label;
class Label : Node {
     this(string text) {
         // ...
     }
     // ...
}

label(
     .layout!"fill",
     .myTheme,
     "Hello, World!"
),
```

More interestingly, it's now extensible — for example, 
`sizeLimit` can be used with `sizeLock` nodes:

```d
sizeLock!label(
     .sizeLimitX = 400,
     .layout!"fill",
     .myTheme,
     "Hello, World!"
)
```

You can define your own node property by creating a struct with 
an `apply` method:

```d
auto hidden(bool value = true) {
     static struct Hidden {
         bool value;
         void apply(Node node) {
             node.isHidden = value;
         }
     }
     return Hidden(value);
}

label(
     .hidden,
     "I'm invisible!"
),
```

Other node properties are now available: `.tags` (see below), 
`.ignoreMouse`, `.hidden` and `.disabled`.

`simpleConstructor` remains alive as an alias to `nodeBuilder`.



One can now define an enum marked ` NodeTag` to create *tags*. 
These are very similar to [CSS 
classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Globa
_attributes/class), making it possible to change how a node is styled in a
consistent, predictable manner:

```d
 NodeTag
enum Tag {
     heading,
     red,
}

vspace(
     myTheme,
     label(
         .tags!(Tag.heading),
         "This is a heading!"
     ),
     label(
         .tags!(Tag.red),
         "Red text!"
     ),
	label(
         .tags!(Tag.heading, Tag.red),
         "Red heading!"
     ),
);
```

Use tags in your theme by passing them into a `rule` argument:

```d
rule!(Label, Tag.heading)(
     fontSize = 22.pt,
),
rule!(Label, Tag.red)(
     textColor = color("#f00"),
).
```


TextInput now supports multiline input via `.multiline`, the 
caret can now be moved around, text can be selected, copied, 
pasted, and changes can be undone or redone with keyboard 
shortcuts.

```d
textInput();  // single line
lineInput();  // single line
multilineInput();       // multiline
textInput(.multiline);  // multiline
codeInput();  // code editor
```

This is a massive change under the hood. Fluid's text rendering 
is now a lot more performant, and can support rendering pages of 
text without slowdowns. Editing performance still needs some 
improvements.

A code editor is now available with `CodeInput` with support for 
syntax highlighting and automatic indentation. Performance issues 
are especially visible with this one, but it is expected they 
will be fixed in coming releases.

[Full changelog can be seen on the releases 
page](https://git.samerion.com/Samerion/Fluid/releases/tag/v0.7.0)
Oct 03
next sibling parent ryuukk_ <ryuukk.dev gmail.com> writes:
Congrats on the release

You should include some example with some screenshots, it'll be 
great way to advertise both the library and D

Your library has lot of potential
Oct 03
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
Nice!

On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 Start a new Fluid project [with DUB](https://fluid.dub.pm):

 ```shell
 dub fetch fluid
 dub init -t fluid myproject
 ```
I tried `dub run fluid:showcase`, but it gives an error about a raylib import it can't resolve. ``` ../../.dub/packages/fluid/0.7.0/fluid/tour/package.d(173,12): Error: unable to read module `raylib` ``` `dub run fluid:tour` does work though.
Oct 04
parent cookiewitch <artha samerion.com> writes:
On Friday, 4 October 2024 at 12:14:09 UTC, Dennis wrote:
 Nice!

 On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 Start a new Fluid project [with DUB](https://fluid.dub.pm):

 ```shell
 dub fetch fluid
 dub init -t fluid myproject
 ```
I tried `dub run fluid:showcase`, but it gives an error about a raylib import it can't resolve. ``` ../../.dub/packages/fluid/0.7.0/fluid/tour/package.d(173,12): Error: unable to read module `raylib` ``` `dub run fluid:tour` does work though.
`dub fluid:showcase` should issue a warning recommending `:tour` now. I'm going to be honest, I didn't check if it still works. ^^
Oct 05
prev sibling next sibling parent reply holyzantaclara <holyzantaclara home.zz> writes:
On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 After 9 months of development and 252 commits later, finally 
 0.7.0 gets to see the sunlight! This is the biggest update to 
 Fluid yet, almost twice as big as 
 [0.6.0](https://git.samerion.com/Samerion/Fluid/releases/tag/v0.6.0), and also
almost half the size of Fluid itself. In fact, so big, that I decided to delay
some of the changes to a [separate future
update](https://git.samerion.com/Samerion/Fluid/milestone/19); too big.

 [...]
Congratulations ! I will definitely try this one ! Thank you so much, I was looking for an UI native library, good timing !
Oct 05
parent cookiewitch <artha samerion.com> writes:
On Saturday, 5 October 2024 at 08:44:58 UTC, holyzantaclara wrote:
 On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 After 9 months of development and 252 commits later, finally 
 0.7.0 gets to see the sunlight! This is the biggest update to 
 Fluid yet, almost twice as big as 
 [0.6.0](https://git.samerion.com/Samerion/Fluid/releases/tag/v0.6.0), and also
almost half the size of Fluid itself. In fact, so big, that I decided to delay
some of the changes to a [separate future
update](https://git.samerion.com/Samerion/Fluid/milestone/19); too big.

 [...]
Congratulations ! I will definitely try this one ! Thank you so much, I was looking for an UI native library, good timing !
Hm, for what it's worth it uses OpenGL at the moment, but integrating with OS-specific toolkits is something I plan to do. At least it's not a web browser!
Oct 05
prev sibling next sibling parent reply WB <witold.baryluk gmail.com> writes:
On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 Start a new Fluid project [with DUB](https://fluid.dub.pm):
... Kind of does not work out of the box: ``` user debian:~/k$ dub run fluid:tour Package 'fluid:tour' was not found locally but is available online: --- Description: A straightforward and easy to use GUI library. Version: 0.7.0 --- Do you want to fetch 'fluid:tour' now? [Y/n]: Y Fetching fluid:tour 0.7.0 Building package fluid:tour in /home/user/.dub/packages/fluid/0.7.0/fluid/tour/ Fetching raylib-d 5.0.1 (getting selected version) Fetching silly 1.1.1 (getting selected version) Fetching bindbc-freetype 1.1.1 (getting selected version) Fetching fluid-tree-sitter 0.1.7 (getting selected version) Fetching bindbc-loader 1.1.5 (getting selected version) Fetching libdparse 0.23.2 (getting selected version) Starting Performing "debug" build using /usr/bin/gdc for x86_64. Building bindbc-freetype 1.1.1: building configuration [staticBC] Building bindbc-loader 1.1.5: building configuration [noBC] Building fluid 0.7.0: building configuration [linux-dynamic] Building fluid-tree-sitter:tree-sitter 0.1.7: building configuration [default] gdc: error: unrecognized command-line option ‘-P-I/home/user/.dub/packages/fluid-tree-sitter/0.1.7/fluid-tree-sitter/external/tree-sitter/lib/include’ gdc: error: unrecognized command-line option ‘-P-I/home/user/.dub/packages/fluid-tree-sitter/0.1.7/fluid-tree-sitter/external/tree-sitter/lib/src’ Error /usr/bin/gdc failed with exit code 1. user debian:~/k$ ``` versions: ``` user debian:~/k$ dub --version DUB version 1.36.0-3+b1, built on Aug 1 2024 user debian:~/k$ gdc -v Using built-in specs. COLLECT_GCC=gdc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/14/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 14.2.0-3' --with-bugurl=file:///usr/share/doc/gcc-14/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust --prefix=/usr --with-gcc-major-version-only --program-suffix=-14 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-14-14.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/reproducible-path/gcc-14-14.2. /debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2 Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 14.2.0 (Debian 14.2.0-3) user debian:~/k$ ```
Oct 20
parent reply cookiewitch <artha samerion.com> writes:
On Sunday, 20 October 2024 at 21:59:49 UTC, WB wrote:
 On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 Start a new Fluid project [with DUB](https://fluid.dub.pm):
... Kind of does not work out of the box: ``` user debian:~/k$ dub run fluid:tour [...] Building fluid-tree-sitter:tree-sitter 0.1.7: building configuration [default] gdc: error: unrecognized command-line option ‘-P-I/home/user/.dub/packages/fluid-tree-sitter/0.1.7/fluid-tree-sitter/external/tree-sitter/lib/include’ gdc: error: unrecognized command-line option ‘-P-I/home/user/.dub/packages/fluid-tree-sitter/0.1.7/fluid-tree-sitter/external/tree-sitter/lib/src’ Error /usr/bin/gdc failed with exit code 1. user debian:~/k$ ``` versions: ``` user debian:~/k$ dub --version DUB version 1.36.0-3+b1, built on Aug 1 2024 user debian:~/k$ gdc -v Using built-in specs. COLLECT_GCC=gdc [...] gcc version 14.2.0 (Debian 14.2.0-3) user debian:~/k$ ```
Fluid requires DMD≥2.098.1 or LDC≥1.28.1. The tour requires a newer compiler than that (meanining Fluid itself might still work) but I did not pinpoint the exact version. GDC is not supported.
Oct 20
parent reply IchorDev <zxinsworld gmail.com> writes:
On Monday, 21 October 2024 at 02:31:19 UTC, cookiewitch wrote:
 Fluid requires DMD≥2.098.1 or LDC≥1.28.1. The tour requires a 
 newer compiler than that (meanining Fluid itself might still 
 work) but I did not pinpoint the exact version. GDC is not 
 supported.
Is it going to be supported?
Oct 24
parent cookiewitch <artha samerion.com> writes:
On Thursday, 24 October 2024 at 11:26:14 UTC, IchorDev wrote:
 On Monday, 21 October 2024 at 02:31:19 UTC, cookiewitch wrote:
 Fluid requires DMD≥2.098.1 or LDC≥1.28.1. The tour requires a 
 newer compiler than that (meanining Fluid itself might still 
 work) but I did not pinpoint the exact version. GDC is not 
 supported.
Is it going to be supported?
Can't say for sure, because I haven't used it myself, but it's a matter of adding it to CI and doing some fix-ups where needed.
Oct 25
prev sibling next sibling parent reply IchorDev <zxinsworld gmail.com> writes:
On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 After 9 months of development and 252 commits later, finally 
 0.7.0 gets to see the sunlight!
This project certainly seems promising, although I’m rather worried about how many classes there are with almost no data—so many allocations for what could just be a struct with function pointers, no? That’s how FreeType does ‘inheritance’ in pure C. Anyway, at this stage, how difficult is it to make a custom Fluid backend? (e.g. SDL2 for input or a different renderer) Also does this project have proper text layout support? I noticed there’s a dependency on FreeType; but FreeType doesn’t do layout, only rendering. I’m about to release [BindBC-Pango](https://github.com/BindBC/bindbc-pango) if you need a text layout engine. They’re absolutely imperative for acceptable internationalisation support.
Oct 24
parent reply cookiewitch <artha samerion.com> writes:
On Thursday, 24 October 2024 at 12:02:00 UTC, IchorDev wrote:
 On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 After 9 months of development and 252 commits later, finally 
 0.7.0 gets to see the sunlight!
This project certainly seems promising, although I’m rather worried about how many classes there are with almost no data—so many allocations for what could just be a struct with function pointers, no? That’s how FreeType does ‘inheritance’ in pure C.
Fluid nodes aren't just pointers to functions, so no. I think switching to struct based code would intensely increase Fluid's complexity, both for me and anyone trying to start using it. I don't think it's worth it.
 Anyway, at this stage, how difficult is it to make a custom 
 Fluid backend? (e.g. SDL2 for input or a different renderer)
The backend API still isn't very polished. It's not difficult, but it takes some time to prepare. I also regret choosing a Raylib-like API rather than an event-based on. I want to change that in a later update, probably 0.8.0 or 0.9.0.
 Also does this project have proper text layout support? I 
 noticed there’s a dependency on FreeType; but FreeType doesn’t 
 do layout, only rendering. I’m about to release 
 [BindBC-Pango](https://github.com/BindBC/bindbc-pango) if you 
 need a text layout engine. They’re absolutely imperative for 
 acceptable internationalisation support.
That is true, Fluid can only do the basic left-to-right text layout right now. To be frank, it isn't even able to center or right align text. So far I've opted for Freetype, because I'm more familiar with APIs of its kind and I wanted to save some of my time, but it might be about time I tried Pango. Thank you for your work on the bindings, I'll check them out!
Oct 25
parent reply IchorDev <zxinsworld gmail.com> writes:
On Friday, 25 October 2024 at 09:44:18 UTC, cookiewitch wrote:
 On Thursday, 24 October 2024 at 12:02:00 UTC, IchorDev wrote:
 Anyway, at this stage, how difficult is it to make a custom 
 Fluid backend? (e.g. SDL2 for input or a different renderer)
The backend API still isn't very polished. It's not difficult, but it takes some time to prepare. I also regret choosing a Raylib-like API rather than an event-based on. I want to change that in a later update, probably 0.8.0 or 0.9.0.
I will check in again once that's done. :)
 Also does this project have proper text layout support? I 
 noticed there’s a dependency on FreeType; but FreeType doesn’t 
 do layout, only rendering. I’m about to release 
 [BindBC-Pango](https://github.com/BindBC/bindbc-pango) if you 
 need a text layout engine. They’re absolutely imperative for 
 acceptable internationalisation support.
That is true, Fluid can only do the basic left-to-right text layout right now. To be frank, it isn't even able to center or right align text. So far I've opted for Freetype, because I'm more familiar with APIs of its kind and I wanted to save some of my time, but it might be about time I tried Pango. Thank you for your work on the bindings, I'll check them out!
No problem! Only thing holding me from putting it on dub is that its dependency BindBC-GLib has no README yet. BindBC-Pango comes with an example that should adequately demonstrate the basics of how to make use of it with FreeType's renderer. If you want to make your own renderer, I've translated enough of GObject's macros into string mixin generators that you can sub-class Pango's base render class like you would in C without any extra hassle: ```d FT_Face[PangoFont*] faces; ///A simplified version of `pango_ft2_font_get_face` from PangoFT, which is not a public method. FT_Face pango_ft2_font_get_face(PangoFont* font) nothrow{ assert(font); if(auto face = font in faces) return *face; FcPattern* pattern = pango_fc_font_get_pattern(cast(PangoFcFont*)font); FT_Error err; char* filename; if(FcPatternGetString(pattern, FC_FILE, 0, &filename) != FcResultMatch){ //ERROR! } int id; if(FcPatternGetInteger(pattern, FC_INDEX, 0, &id) != FcResultMatch){ //ERROR! } FT_Face face; err = FT_New_Face(ftLib, filename, id, &face); if(err){ //ERROR! } double size; if(FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch){ //ERROR! } err = FT_Set_Char_Size( face, cast(FT_F26Dot6)(size * (1<<6)), cast(FT_F26Dot6)(size * (1<<6)), 0, 0, ); if(err){ //ERROR! } faces[font] = face; return face; } struct PangoCustomRenderer{ PangoRenderer parentInstance; //custom class instance data goes here } struct PangoCustomRendererClass{ PangoRendererClass parentClass; } mixin(G_DEFINE_TYPE("PangoCustomRenderer", "pango_Custom_renderer", "PANGO_TYPE_RENDERER")); extern(C) nothrow{ void pango_Custom_renderer_init(PangoCustomRenderer* self){ //per-instance initialisation } void pango_Custom_renderer_class_init(PangoCustomRendererClass* self){ auto rendererClass = &self.parentClass; //also written as `cast(PangoRendererClass*)` rendererClass.drawGlyph = &pangoCustomRendererDrawGlyph; //override drawGlyph } struct DrawState{ FT_Vector pos; } ///Move the pen position int moveTo(const(FT_Vector)* to, void* user) => 0; ///Draw a line int lineTo(const(FT_Vector)* to, void* user) => 0; ///Draw a quadratic bézier int conicTo(const(FT_Vector)* ctrl, const(FT_Vector)* to, void* user) => 0; ///Draw a cubic bézier int cubicTo(const(FT_Vector)* ctrl1, const(FT_Vector)* ctrl2, const(FT_Vector)* to, void* user) => 0; void pangoCustomRendererDrawGlyph(PangoRenderer* pangoRenderer, PangoFont* font, PangoGlyph glyph, double x, double y){ if(FT_Face face = pango_ft2_font_get_face(font)){ FT_Load_Glyph(face, glyph, 0); //NOTE: glyph outline direction may be reversed immutable fns = FT_Outline_Funcs( &moveTo, &lineTo, &conicTo, &cubicTo, 0, 0, ); auto state = DrawState(); FT_Outline_Decompose(&face.glyph.outline, &fns, &state); } } } //using the class: auto pangoRenderer = cast(PangoRenderer*)g_object_new(pango_Custom_renderer_get_type(), null); //... pango_renderer_draw_layout(pangoRenderer, layout, 0,0); ```
Oct 25
parent reply cookiewitch <artha samerion.com> writes:
On Friday, 25 October 2024 at 12:21:14 UTC, IchorDev wrote:
 On Friday, 25 October 2024 at 09:44:18 UTC, cookiewitch wrote:
 On Thursday, 24 October 2024 at 12:02:00 UTC, IchorDev wrote:
 Anyway, at this stage, how difficult is it to make a custom 
 Fluid backend? (e.g. SDL2 for input or a different renderer)
The backend API still isn't very polished. It's not difficult, but it takes some time to prepare. I also regret choosing a Raylib-like API rather than an event-based on. I want to change that in a later update, probably 0.8.0 or 0.9.0.
I will check in again once that's done. :)
 Also does this project have proper text layout support? I 
 noticed there’s a dependency on FreeType; but FreeType 
 doesn’t do layout, only rendering. I’m about to release 
 [BindBC-Pango](https://github.com/BindBC/bindbc-pango) if you 
 need a text layout engine. They’re absolutely imperative for 
 acceptable internationalisation support.
That is true, Fluid can only do the basic left-to-right text layout right now. To be frank, it isn't even able to center or right align text. So far I've opted for Freetype, because I'm more familiar with APIs of its kind and I wanted to save some of my time, but it might be about time I tried Pango. Thank you for your work on the bindings, I'll check them out!
No problem! Only thing holding me from putting it on dub is that its dependency BindBC-GLib has no README yet. BindBC-Pango comes with an example that should adequately demonstrate the basics of how to make use of it with FreeType's renderer. If you want to make your own renderer, I've translated enough of GObject's macros into string mixin generators that you can sub-class Pango's base render class like you would in C without any extra hassle [...]
Looking through Pango's documentation and code, [integrating it with Fluid might be tricky](https://git.samerion.com/Samerion/Fluid/issues/197). This topic is also complex and there are things I don't understand about Pango's approach that I can't find an explanation of. This is probably something I'll have to spend a lot of time on to get a proper grasp of. I'd really appreciate it if there were some other, more comprehensive learning resources. Do you know any?
Oct 25
parent reply IchorDev <zxinsworld gmail.com> writes:
On Friday, 25 October 2024 at 12:49:36 UTC, cookiewitch wrote:
 Looking through Pango's documentation and code, [integrating it 
 with Fluid might be 
 tricky](https://git.samerion.com/Samerion/Fluid/issues/197). 
 This topic is also complex and there are things I don't 
 understand about Pango's approach that I can't find an 
 explanation of. This is probably something I'll have to spend a 
 lot of time on to get a proper grasp of.

 I'd really appreciate it if there were some other, more 
 comprehensive learning resources. Do you know any?
Sorry for my late reply! Anyway, here we go… I was able to figure out how to use it for *static* text pretty easily from just the official docs. I found it best to look at what you want at the end (e.g. rendering the text), find how you do that, and then work backwards from there. This might not help you that much, but here's a step-by-step outline on getting started using Pango, with links to the relevant documentation, which provide a decent amount of insight into what each class is/does. This boils down to [about 20 lines of code from my example](https://github.com/BindBC/bindbc-pango/blob/master/example/source/app.d#L54). - First you need a [`PangoFontMap`](https://docs.gtk.org/Pango/class.FontMap.html). You can, for instance, get a `PangoFontMap` implemented using FreeType with `pango_ft2_font_map_new`. - Then, create a [`PangoContext`](https://docs.gtk.org/Pango/class.Context.html) from the `PangoFontMap`. - Create a [`PangoFontDescription`](https://docs.gtk.org/Pango/struct.F ntDescription.html) (e.g. using `pango_font_description_new`) and use whichever setter functions on it to provide info about what font you want it to use, how large it should be, et cetera. Pango automatically uses fallback fonts when needed, which is why you don't *just* give it which font to use. This way, when users write glyphs that are not included in your preferred font, the overall appearance of the text can remain more-or-less consistent. In my example I only set the size of a `PangoFontDescription`, so Pango will basically default to using the system's preferred default fonts. - Set the `PangoContext`'s `PangoFontDescription` to the one you just created/populated. - Create a [`PangoLayout`](https://docs.gtk.org/Pango/class.Layout.html) from the `PangoContext`. This layout object handles a lot of things for you that you can mostly otherwise do yourself with components of the Pango library like . - You can set the layout's contents with: - an unformatted string: [`pango_layout_set_text`](https://docs.gtk.org/Pango/method.Layout.set_text.html) - a string formatted with [Pango Markup](https://docs.gtk.org/Pango/pango_markup.html): [`pango_layout_set_markup`](https://docs.gtk.org/Pango/method.Layout.set_markup.html) - a string specially-formatted by your own code: `pango_layout_set_text` and [`pango_layout_set_attributes`](https://docs.gtk.org/Pango/method.Layout.set_attributes.html) From there, you'd mostly be interacting with the layout. A few things to note: - Pango uses reference counting for basically everything, so make sure to read up on how you're supposed to `unref` objects you've created so that they get deallocated. Often this is done with `g_object_unref` instead of a function from Pango. - The API & API reference use the term 'character' confusingly. Mostly it seems to refer to code-points, but occasionally I believe it is used to refer to individual bytes (i.e. code-units). I really don't think the stock `PangoLayout` implementation provides a way to make using ropes work in a fast manner without just capitulating and using strings. However, you could re-implement **most** (but not all) of `PangoLayout`'s layout functionality from functions that Pango exposes. [This page should give you an idea](https://docs.gtk.org/Pango/pango_rendering.html). Pango is far from perfect, and in some ways using it is an inherent compromise between what you want, and what's possible using libraries out there today. Even TextKit is [not without its problems](https://atadistance.net/2021/07/13/apple-text-layout-architecture-evolution-textkit-reboot/). There is one way around all of this, which is building a new text layout engine… Some consideration should be given to where to start. For instance, one could build up from HarfBuzz. However, HarfBuzz is another GLib-based FSF C library, just like Pango, so perhaps this would just shift the compromises further down the line. Say we want *no* compromises; then we have to do everything 100% in D. Certainly neither of these options are trivial, and it would be a fool's errand to embark upon them alone. Having a full D text layout engine is a pet dream of mine of course, so if you're interested in contributing to a project like that then I might be able to dedicate some of my spare time to it.
Oct 31
parent reply cookiewitch <artha samerion.com> writes:
On Thursday, 31 October 2024 at 13:08:55 UTC, IchorDev wrote:
 On Friday, 25 October 2024 at 12:49:36 UTC, cookiewitch wrote:
 [...]

 I'd really appreciate it if there were some other, more 
 comprehensive learning resources. Do you know any?
Sorry for my late reply! Anyway, here we go… [...] Some consideration should be given to where to start. For instance, one could build up from HarfBuzz. However, HarfBuzz is another GLib-based FSF C library, just like Pango, so perhaps this would just shift the compromises further down the line. Say we want *no* compromises; then we have to do everything 100% in D. Certainly neither of these options are trivial, and it would be a fool's errand to embark upon them alone.
Thank you for dedicating your time to this, and for the very elaborate response! Reimplementing PangoLayout does sound like an option, but as far as I've seen, Pango operates on strings all the way through. I suppose it is possible to assemble parts of the rope into a string on the stack, and only operate on a single part at once, but I don't think this is going to be very bug-proof. Alternatively, Fluid's text engine does distinguish between static and dynamic text with an "edit mode" flag, so it could be possible to apply Pango on stringified static text, but that's never going to be enough. Oh, and also, since Pango is LGPLv2 licensed, I think a port of `PangoLayout` could run into licensing problems the way D programs are usually built \[with DUB\].
 Having a full D text layout engine is a pet dream of mine of 
 course, so if you're interested in contributing to a project 
 like that then I might be able to dedicate some of my spare 
 time to it.
I've been thinking about this last week and I'm under the impression this is what is going to happen anyway. For what it's worth, I'm currently working on another large PR which upscales Fluid's text engine, splitting it into a separate `fluid.text` package and optimizing it on the way through. It is mostly standalone, so if you find it appropriate, I could create a new Git & DUB repository for it once I'm done. I would like to have your input on it.
Nov 01
parent reply IchorDev <zxinsworld gmail.com> writes:
On Friday, 1 November 2024 at 12:14:25 UTC, cookiewitch wrote:
 On Thursday, 31 October 2024 at 13:08:55 UTC, IchorDev wrote:
 Sorry for my late reply! Anyway, here we go…

 [...]

 Some consideration should be given to where to start. For 
 instance, one could build up from HarfBuzz. However, HarfBuzz 
 is another GLib-based FSF C library, just like Pango, so 
 perhaps this would just shift the compromises further down the 
 line. Say we want *no* compromises; then we have to do 
 everything 100% in D. Certainly neither of these options are 
 trivial, and it would be a fool's errand to embark upon them 
 alone.
Thank you for dedicating your time to this, and for the very elaborate response! Reimplementing PangoLayout does sound like an option, but as far as I've seen, Pango operates on strings all the way through. I suppose it is possible to assemble parts of the rope into a string on the stack, and only operate on a single part at once, but I don't think this is going to be very bug-proof. Alternatively, Fluid's text engine does distinguish between static and dynamic text with an "edit mode" flag, so it could be possible to apply Pango on stringified static text, but that's never going to be enough.
Compromises!
 Oh, and also, since Pango is LGPLv2 licensed, I think a port of 
 `PangoLayout` could run into licensing problems the way D 
 programs are usually built \[with DUB\].
If the modified source is available then I don't think that's a problem?
 Having a full D text layout engine is a pet dream of mine of 
 course, so if you're interested in contributing to a project 
 like that then I might be able to dedicate some of my spare 
 time to it.
I've been thinking about this last week and I'm under the impression this is what is going to happen anyway. For what it's worth, I'm currently working on another large PR which upscales Fluid's text engine, splitting it into a separate `fluid.text` package and optimizing it on the way through. It is mostly standalone, so if you find it appropriate, I could create a new Git & DUB repository for it once I'm done. I would like to have your input on it.
Sure, that sounds like a great idea! Here are my thoughts about what could be changed based on how `fluid.text` is currently set up: - `StyledText` *only* stores its strings as ropes, making it less ideal for static text, and not very flexible (e.g. can't use a custom type with fast insertion). Ideally there'd be a layout option for static text that uses finite forward ranges of `const(char)` internally for static text, and also one that uses a mutable string templated interface internally for user-editable text. - `StyledText` only performs rendering to a texture, preventing it from working with renderers that use modern text rendering techniques, and not giving the user any control over the rendering pipeline. Any kind of rendering functionality should be separated out, and `StyledText` should provide a renderer-agnostic way to get all of the necessary information for rendering (e.g. each font glyph ID, its transformation, and style) as a forward range interface or similar. - Different methods of cutting off overflowing text should be provided, like placing ellipses at the end of the visible text. - You will probably want to use FontConfig (see: BindBC-FontConfig) by default (but ideally with an option for a custom implementation) to figure out what fonts each system prefers as fallbacks. And same story with using FreeType by default to load & process font data. - `TextStyleSlice` uses a palette right now, which is pointlessly limited to 256 different items. It might be worth attempting to use a `SumType` containing each individual styling option, since then the defaults don't have to be re-specified with 1 exception every time someone uses **bold** or *italic*. Otherwise, `TextStyleSlice` should probably use a `size_t` for style indices. Also, things like colour that do not affect text layout should not be part of the `Style` used when making layouts in the first place. A user's renderer can worry about that. - The `Typeface` interface expects implementations to have way too much state. - Everything that goes into text-shaping should be accessible in isolation so that people who need to perform more fine-grained tasks can use them. For example, the bi-directional algorithm. - The styling needs to be known when generating a layout, and it might be desirable for the layout to be lazily re-computed. So all in all, a bit like this example: ```d alias LayoutStyle = SumType!( FontFace, Padding, TextDirection, Alignment, Justify, /+et cetera+/ ); struct LayoutStyleRange{ size_t from, to; Style style; } struct Caret{ Vec2!float pos; float height; } mixin template SharedTextLayoutStuff(Text){ Text text; RedBlackTree!(LayoutStyleRange, ".from", true) styleRanges; //styles are sorted by `.from` for efficiency uint textSerialNumber; //to see if text was updated ///calculates the layout. Called when `textSerialNumber` is no longer current by anything that uses the layout (e.g. getRenderData) void calculate(); ///gets data used for rendering the glyphs SomeForwardRange!GlyphRenderData getRenderData(); //more functions... ///the position & height of a caret at a certain string index Caret getCaret(size_t ind) const; ///the string index of a caret at a certain visual position size_t getIndex(Vec2!float pos) const; } ///rarely changes, uses string-like forward ranges struct TextLayout(Text) if(IsSomeFiniteCharRange!InternalText){ mixin SharedTextLayoutStuff!(InternalText); //static-specific stuff } ///changes with random insertion and deletion, uses an abstract interface struct DynamicTextLayout(Text) if( isDynamicLayoutText!Text //`Text` needs at least: //a forward range interface (empty, popFront, front) //opIndex //opIndexAssign with overloads for slicing //insertion & deletion //a number that is incremented every time it is updated ){ mixin SharedTextLayoutStuff!(Text); //dynamic-specific stuff } ```
Nov 02
parent reply cookiewitch <artha samerion.com> writes:
On Saturday, 2 November 2024 at 17:33:19 UTC, IchorDev wrote:
 [...]
 Sure, that sounds like a great idea!

 Here are my thoughts about what could be changed based on how 
 `fluid.text` is currently set up:
 [...]
 - `TextStyleSlice` uses a palette right now, which is 
 pointlessly limited to 256 different items. It might be worth 
 attempting to use a `SumType` containing each individual 
 styling option, since then the defaults don't have to be 
 re-specified with 1 exception every time someone uses **bold** 
 or *italic*. Otherwise, `TextStyleSlice` should probably use a 
 `size_t` for style indices. Also, things like colour that do 
 not affect text layout should not be part of the `Style` used 
 when making layouts in the first place. A user's renderer can 
 worry about that.
Inline styling of `Text` was just a dirty hack to have syntax highlighting working instead of offering a proper, full solution, so it's far from being the final design. Some of my concerns: * How would you approach coloring text from the user's perspective? * What about handling text mixed with other content (like images, icons, UI components), something akin to CSS `display: inline-block`?
 - The `Typeface` interface expects implementations to have way 
 too much state.
`Typeface` is something I'm not very happy about either.
 So all in all, a bit like this example: [...]
Looks good to me. Having a separate structure for static and dynamic text is reasonable, and as you've seen Fluid currently only distinguishes between this at runtime. I'd opt for a regular struct instead of mixin, though. I think it's worth noting that I also care about maintaining performance for large files (100K–10MB), which is what I'm attempting to fix on the [text-input-ranges](https://git.samerion.com/Samerion/Fluid/src/branc /text-input-ranges) branch, but I'm still struggling. Fluid will only draw the text that is visible on the screen (which is handled by `CompositeTexture`). I've introduced a `TextRulerCache` structure which maps characters (by index) to position in text in a way that also preserves layout data, so a piece of text can be edited without recalculating the layout for the rest of it. I'm not very proud of it, it's apparently very fragile, so I'm curious what is your idea of handling this, too.
Nov 03
parent reply IchorDev <zxinsworld gmail.com> writes:
On Sunday, 3 November 2024 at 12:13:07 UTC, cookiewitch wrote:
 On Saturday, 2 November 2024 at 17:33:19 UTC, IchorDev wrote:
 [...]
 Sure, that sounds like a great idea!

 Here are my thoughts about what could be changed based on how 
 `fluid.text` is currently set up:
 [...]
 - `TextStyleSlice` uses a palette right now, which is 
 pointlessly limited to 256 different items. It might be worth 
 attempting to use a `SumType` containing each individual 
 styling option, since then the defaults don't have to be 
 re-specified with 1 exception every time someone uses **bold** 
 or *italic*. Otherwise, `TextStyleSlice` should probably use a 
 `size_t` for style indices. Also, things like colour that do 
 not affect text layout should not be part of the `Style` used 
 when making layouts in the first place. A user's renderer can 
 worry about that.
Inline styling of `Text` was just a dirty hack to have syntax highlighting working instead of offering a proper, full solution, so it's far from being the final design. Some of my concerns: * How would you approach coloring text from the user's perspective?
I think we should give users the tools to do rendering themselves, which lets them implement non-layout features like colour on top however they please. For instance, when colour is updated the layout remains the same, so colour can be denoted by a structure on the user’s side that the renderer reads from.
 * What about handling text mixed with other content (like 
 images, icons, UI components), something akin to CSS `display: 
 inline-block`?
That’s a tricky one! I was thinking that an API to add shapes that the text ‘goes around’. The shape type would be a sumtype of different shapes, probably stored in an optimisation structure like a tree. But what if you want an *inline* rectangle that will move depending on the text layout? For that case we *could* ask that the user just get the position that the text ends, place their element manually, and then use a second layout object, with the first line set to start after the end of the element. I acknowledge that’s a lot of work on the user end, but I’d argue that it’s an obscure (and frankly absurd) use-case, and requires a lot of implementation-specific decisions that make it impossible to account for everyone’s needs with our own implementation. Can the rectangle overflow the margins at the end of a line, or does it wrap? How is the line with the inline rectangle on it vertically aligned? How and when can shapes intersect with text if at all? Placing icons inline could probably work the same way as image-based emoji, and we could provide a function to add surrogate image glyphs (either to a font or to a layout? Not sure) for people who don’t want to make a font just to write their custom icons. There’s a whole section of Unicode code points set aside for ‘private use’ like this.
 Looks good to me. Having a separate structure for static and 
 dynamic text is reasonable, and as you've seen Fluid currently 
 only distinguishes between this at runtime. I'd opt for a 
 regular struct instead of mixin, though.
The mixin template there is just for shared code and wouldn’t have any meaningful functionality on its own. It’s also the kind of thing that might be better as a string mixin due to sections of 100% duplicated code needing to call non-duplicated functions.
 I've introduced a `TextRulerCache` structure which maps 
 characters (by index) to position in text in a way that also 
 preserves layout data, so a piece of text can be edited without 
 recalculating the layout for the rest of it. I'm not very proud 
 of it, it's apparently very fragile, so I'm curious what is 
 your idea of handling this, too.
I *think* (but I could be wrong) that text can’t transcend line-breaks. If I’m right then perhaps text could be segmented at line-breaks, and then requesting to lay out and render it could be done by requesting an index range. And then the dynamic Text type needs an interface to report whether a certain range of lines is ‘clean’ (unmodified since last calculation) and an interface to mark lines as clean. Note that this **requires** special user-side handling for top-to-bottom text, making it more of a pain to use.
Nov 08
parent reply cookiewitch <artha samerion.com> writes:
On Friday, 8 November 2024 at 21:13:19 UTC, IchorDev wrote:
 That’s a tricky one!
 I was thinking that an API to add shapes that the text ‘goes 
 around’. The shape type would be a sumtype of different shapes, 
 probably stored in an optimisation structure like a tree.
 But what if you want an *inline* rectangle that will move 
 depending on the text layout? For that case we *could* ask that 
 the user just get the position that the text ends, place their 
 element manually, and then use a second layout object, with the 
 first line set to start after the end of the element. I 
 acknowledge that’s a lot of work on the user end, but I’d argue 
 that it’s an obscure (and frankly absurd) use-case, and 
 requires a lot of implementation-specific decisions that make 
 it impossible to account for everyone’s needs with our own 
 implementation. Can the rectangle overflow the margins at the 
 end of a line, or does it wrap? How is the line with the inline 
 rectangle on it vertically aligned? How and when can shapes 
 intersect with text if at all?
Measurement and rendering uses the `TextRuler` struct (frankly more important than the Fluid-centric `StyledText` one we keep discussing), which I hope to keep super simple so it can be easily manipulated by the user. It keeps track of the pen position and text size. The idea is `TextRuler` is fed pieces of unbreakable content ("words") which may be text, but it can be anything of known width. A textual word would be first measured and then placed in the ruler to place it in the correct spot in the text. Whatever manipulates the ruler, like the `Text` struct, could handle other pieces through a hook that would run between words. I imagine it would have a signature like `void delegate(ref TextRuler, WordSize)`. `inline-block`-like content (images, buttons, etc.) would increase line height and add a single "word" both matching the image's size. `inline` elements could add multiple words. `float` content could be simulated by inserting a word on every line at its supposed location, and thus would even support non-rectangular elements anywhere within the line. I'm not sure yet when to move the engine into its own project, but I would like to have this ability in Fluid soon; I thought I would let you know about this idea. As for `TextRulerCache` I mentioned earlier, I'm almost sure I've ironed out all the quirks and bugs. I think it's OK now, but certainly coding a pretty weird and complex data structure while having a fever wasn't my brightest idea.
Nov 18
parent reply IchorDev <zxinsworld gmail.com> writes:
On Monday, 18 November 2024 at 14:27:41 UTC, cookiewitch wrote:
 Measurement and rendering uses the `TextRuler` struct (frankly 
 more important than the Fluid-centric `StyledText` one we keep 
 discussing), which I hope to keep super simple so it can be 
 easily manipulated by the user.
Good luck keeping it simple!
 It keeps track of the pen position and text size.
This sounds like it would break when RTL text needs to be embedded into LTR text. (because RTL text needs to be right-aligned within the LTR block)
 The idea is `TextRuler` is fed pieces of unbreakable content 
 ("words") which may be text, but it can be anything of known 
 width. A textual word would be first measured and then placed 
 in the ruler to place it in the correct spot in the text.

 Whatever manipulates the ruler, like the `Text` struct, could 
 handle other pieces through a hook that would run between 
 words. I imagine it would have a signature like `void 
 delegate(ref TextRuler, WordSize)`. `inline-block`-like content 
 (images, buttons, etc.) would increase line height and add a 
 single "word" both matching the image's size. `inline` elements 
 could add multiple words. `float` content could be simulated by 
 inserting a word on every line at its supposed location, and 
 thus would even support non-rectangular elements anywhere 
 within the line.
What about the majority of languages which do not use spaces? Do they submit all of their text in one huge block? And surely basing your rendering idioms around the English concept of a word is surely a bit exclusionary, no?
 As for `TextRulerCache` I mentioned earlier, I'm almost sure 
 I've ironed out all the quirks and bugs. I think it's OK now, 
 but certainly coding a pretty weird and complex data structure 
 while having a fever wasn't my brightest idea.
Well done! When it becomes its own package I'll have to have a look at how it's going. :) I might end up doing my own thing if time allows.
Nov 23
next sibling parent reply claptrap <clap trap.com> writes:
On Saturday, 23 November 2024 at 11:58:06 UTC, IchorDev wrote:
 On Monday, 18 November 2024 at 14:27:41 UTC, cookiewitch wrote:


 What about the majority of languages which do not use spaces? 
 Do they submit all of their text in one huge block? And surely 
 basing your rendering idioms around the English concept of a 
 word is surely a bit exclusionary, no?
Everything is exclusionary. You go to a restaurant it doesn't have menus in every language on earth, just their native language probably. The question is not "is it exclusionary", the question is "Is it reasonable to expect them take on the extra burden of catering to X,Y and Z". Do you think it is reasonable to expect him to cater to your needs given that he is a lone developer working on a fairly large project, and he's doing it unpaid and giving it away for free. Do you think it is reasonable for you to effectively guilt trip him about being exclusionary?
Nov 23
parent cookiewitch <artha samerion.com> writes:
On Saturday, 23 November 2024 at 12:53:30 UTC, claptrap wrote:
 Do you think it is reasonable for you to effectively guilt trip 
 him about being exclusionary?
No need to be defensive. Having full Unicode support may be beyond my knowledge and skills, but it is why I am open to feedback. I'm thankful for the advice I have received, even if it might be overwhelming at times.
Nov 23
prev sibling parent cookiewitch <artha samerion.com> writes:
On Saturday, 23 November 2024 at 11:58:06 UTC, IchorDev wrote:
 On Monday, 18 November 2024 at 14:27:41 UTC, cookiewitch wrote:
 Measurement and rendering uses the `TextRuler` struct (frankly 
 more important than the Fluid-centric `StyledText` one we keep 
 discussing), which I hope to keep super simple so it can be 
 easily manipulated by the user.
Good luck keeping it simple!
😆
 What about the majority of languages which do not use spaces? 
 Do they submit all of their text in one huge block? And surely 
 basing your rendering idioms around the English concept of a 
 word is surely a bit exclusionary, no?
I… don't know? My idea of a "word" here is any unbreakable unit. I guess I have another round of looking up Unicode algorithms ahead of me.
Nov 23
prev sibling parent cookiewitch <artha samerion.com> writes:
On Thursday, 3 October 2024 at 11:15:16 UTC, cookiewitch wrote:
 After 9 months of development and 252 commits later, finally 
 0.7.0 gets to see the sunlight! This is the biggest update to 
 Fluid yet, almost twice as big as 
 [0.6.0](https://git.samerion.com/Samerion/Fluid/releases/tag/v0.6.0), and also
almost half the size of Fluid itself. In fact, so big, that I decided to delay
some of the changes to a [separate future
update](https://git.samerion.com/Samerion/Fluid/milestone/19); too big.

 **[Fluid is a general-purpose user interface 
 library](https://git.samerion.com/Samerion/Fluid)**. It is D 
 first, and comes with Raylib support. While largely a work in 
 progress, and of pre-release quality, it's already mostly 
 stable and ready for use.
[0.7.1 is now out](https://git.samerion.com/Samerion/Fluid/releases/tag/v0.7.1) and [available on DUB](https://code.dlang.org/packages/fluid/0.7.1). It adds support for Raylib 5.5 and some other, small, miscellaneous changes. Next updates in the 0.7.x series will still include improved documentation and new nodes, but Fluid is now moving focus to the next 0.8.0 release. To speed up the development process I published [a simple guideline for breaking changes](https://git.samerion.com/Samerion/Fluid/issues/218). The process is now also becoming more formal, as I am standardizing and writing down practices I've established during development.
Nov 21