bgrabitmap / bgracontrols

🆗 BGRA Controls is a set of graphical UI elements that you can use with Lazarus LCL applications.
https://bgrabitmap.github.io/bgracontrols/
189 stars 32 forks source link

SVG GUI #94

Closed lainz closed 4 years ago

lainz commented 4 years ago

What about an entire UI made with SVG? I'm looking for ideas.

Say we have just resources and we forget about the complex properties system we have now. Anything you can draw with SVG can be drawn on screen at any scale factor.

If you want other style just change the SVG images. That's what we do on Android Studio apps.

Like we currently have for svg button in bgracontrols.

I think the missing part will be 9 slice scaling for SVG. So we can create scalable rectangles. Most vectors app has this stuff. Where you can define a smart rectangle object with rounded borders. If you scale it the borders rounded ratio remains the same for the corners.

I think with that we can do any UI. Of course we can have properties, but these can be less. Themes can use different images so no need to define gradients, line thickness, rounding and so on... change the images and you change the entire app look.

As well is easier because you can hire a designer that can do all the svg files for you.

How it is done on android? The answer is XML. I think that I've seen XML as well on Visual Studio apps for Windows 10.

Not sure if this is reinventing the wheel but the LCL looks really old stuff even with BGRAControls.

Maybe we can have LCL controls in the Lazarus IDE but all SVG and nothing more.

I opened this bug report because I've used the svg viewer of bgracontrols and get amazed. It scales well even if I change the windows dpi when the app is running. The imagelist trick didn't work in that case... I need to close and open lazpaint again to see the effect.

Well that's it. It sounds hard but I think I will start another package only with svg controls. And this time I will not quit soon, even if I don't see the results immediately.

I will try to learn more about how android studio does it because we have really nice looking apps made with that and i want that for Lazarus desktop. Tired of Electron .js I must say.

lainz commented 4 years ago

I created bgravector repository to start making things in the weekend.

lainz commented 4 years ago

And of course bgracontrols is still the main gui project. Just wanting to experiment the idea in the new repository and don't mess this repository that's already good and organized. If I break things better not here 👍

circular17 commented 4 years ago

I agree that SVG / vector stuff can be resized on the fly and that we can do almost everything with it.

I suppose we indeed need to have slice scaling and for the text have a font definition that would be relative to the current DPI / zoom.

For a button in SVG, that would be a slice scaled SVG for the button shape and an SVG for the glyph. I suppose we need a zoom factor for the glyph because:

For the slice scaling, we can in fact do that programmatically. As long as we have an SVG and the position of the slices, we can stretchdraw the SVG with clipping to draw each of the 9 parts.

Files could be either SVG or LZP, even though for LZP, we need to think about exporting the format for vector originals (for now in LazPaint project) and implementing an adapted stretch draw function. Or... consider exporting as SVG the images made in LazPaint.

lainz commented 4 years ago

I like all the ideas, the clipped drawing for slice scaling, the zoom for the glyph and the font. As well using SVG or LZP format, all good stuff.

A thing that puzzles me is how to preview in the IDE the drawings, if we will be using resources from the IDE project options or external files.

circular17 commented 4 years ago

That's a good question. I think we need to find how it is done for TBitmap for example. But instead it would be a class like TBCVectorialImage, that could contain an SVG or an LZP, or even a PNG that would be stretched.

I suggest that from the start we do a theme manager and an image list. So everything could be updated via a dialog, like the current TImageList ou TBCButton style. And of course we need to serialize all the information, maybe that would simply be through TBCVectorialImage type.

For example the theme could have a vectorial image property for each control type and state. Or we could group them to put together normal / hover / clicked. A check box would have 3 group entries: unchecked, grayed and checked.

Can you share what you learn from your experience with theme managers?

lainz commented 4 years ago

Can you share what you learn from your experience with theme managers?

Yes sure.

First I did one theme with Lua with Autoplay Media Studio, where basically there was a folder with resources and everything was loaded from files. You change the folder name and you change the theme. There was an .ini file containing colors, fonts and so on.

Then in Lazarus I used Custom Drawn package that overrides the drawing for each element, say there is a method DrawButton, other DrawGlyph, other for measuring the AutoSize. I've used zip files there, where a zip file contains a full theme, with .ini files as well for determining slice scaling, bitmap names and other colors.

And more recently I did some theme manager for BGRAControls. In fact I did 2. The first one changes the properties for all controls in the form, say all BCButtons. So you have a template BCButton to grab the styles from.

The last theme manager I did called BGRATheme (3 controls, button, checkbox and radio button) is similar to the Custom Drawn one, you override a template class where you implement the methods to draw. As well you can add as many properties you need like color, fonts and so on. This is used for all controls that contains a specific theme attached into it (currently color theme, default theme and image theme).

Both in Custom Drawn and the BGRATheme each control can assign a theme from the object inspector, when you change the theme it invalidates the control and paints with the desired theme.

So basically themes can be from folders and files, from zip files, entirely drawn with methods and contain or not extra properties.

As well I've used Delphi themes, but is basically like Custom Drawn package.

In Android Studio there is a kind of default theme, with a lot of XML. Then you can create your own theme creating new XML files based on the default ones. There is the concept of resources, that are just XML with vector graphics. Resources can be images as well.

What I learned is that there is no perfect solution, but one that works, writing more or less code, using the IDE or not. In Lua I did all with code, there was no good object inspector to use, but still the best theme I ever did (check some screenshots here https://xn--leandrodiazdiseo-lub.blogspot.com/2014/08/winds-pro-ui.html).

A thing to take into consideration is AutoSize, in Custom Drawn there was not implemented.

For example the theme could have a vectorial image property for each control type and state. Or we could group them to put together normal / hover / clicked. A check box would have 3 group entries: unchecked, grayed and checked.

Yes, that will be good. And as well loading from file, like a zip file or a folder with named resources. So the end user can create his own themes as well. That's the idea. Say all toolbars can be customized for lazpaint by the user. Telegram does like that with themes for desktop for example.

And of course the hability to override the theme for individual controls, that is really needed, say you drop a Theme in the form, then you drop another for special buttons, another for using images instead of SVG, another for drawing with bgrabitmap. The thing is that the theme must be so generic we can have themes of any kind.

circular17 commented 4 years ago

Ok so basically all drawing are done in a theme and each control depend on a theme. Could we do that with BGRATheme?

Something to consider is to tackle DPI / Retina at the core level. Basically DPI and Retina can be merged where Retina is equivalent to 192 DPI (96x2).

I am thinking that the definition of an image needs to include the size of the slices when scaling as well as the DPI scaling.

lainz commented 4 years ago

Ok so basically all drawing are done in a theme and each control depend on a theme. Could we do that with BGRATheme?

Yes just check the sources, is really a simple method and better than Custom Drawn. With Custom Drawn there was a limited set of themes, defined by an enum property, say csWindowsXp, csAndroid and so on, with BGRATheme you can choose any theme for any control.

But I don't remember if it was working 100% as I wanted, I need to look at it again. But yes, we can define a BGRAThemeSVG or BGRAThemeVector and we already have 3 controls, button, checkbox and radiobutton on bgracontrols. These controls are not too much finished but already work fine. At least to test the concept it can work.

Something to consider is to tackle DPI / Retina at the core level. Basically DPI and Retina can be merged where Retina is equivalent to 192 DPI (96x2).

Ok, good idea, so we don't need to worry about that.

I am thinking that the definition of an image needs to include the size of the slices when scaling as well as the DPI scaling.

Great, yes is good to have all in one place.

lainz commented 4 years ago

Check BGRAThemeButton for example, in the BGRA Themes tab.

lainz commented 4 years ago

Here is some approach for 9 slice scaling, if I understand it well, seems that it does it by hand based on an SVG he draw in some other app https://www.bennadel.com/blog/3571-trying-to-implement-9-slice-scaling-with-svg-components-in-angular-7-2-4.htm

I'm not sure if using stretch draw we can draw that tooltip?

Here is another https://mastery.games/post/dynamic-svg-components/

lainz commented 4 years ago

Hi, I started the TBGRASVGTheme, I'm just using strings to store the theme and it is working fine. I just did the checkbox and the radiobutton.

Screenshot: svgcontrols

Here on High DPI (I've lowered the size of the green checkboxes). svgicons

Demo (the svg are free from flaticon.com, but it requires to mention the author, so better don't use them in an application. With the material design SVG I think there is no problem).

Just download the latest commit from dev-bgracontrols and rebuild the IDE. svg theme.zip

circular17 commented 4 years ago

Nice, that's a proof of concept.

I have modified the code for Retina scaling, so that the code is the same whether it is Retina or something else.

So basically you have the following property of the ASurface parameter:

lainz commented 4 years ago

Thanks =)

circular17 commented 4 years ago

Some remarks for what's next:

lainz commented 4 years ago

I made some button with Inkscape to test the 9 slice scaling. button.zip

I will try to make the scaling feature.

lainz commented 4 years ago

I did my best but still looking bad. Can you spot the problem? I think it has to do because I used percentage of margins to calculate the middle drawings.

Here is the project: svgscaling.zip

But something good is that is extremely fast!

9 slice scaling svg

circular17 commented 4 years ago

Here it is fixed. Also added support for DPI / Retina. svgscaling-dpi.zip Note that when the wanted margin is too big for the SVG, then there is no middle anymore.

Capture d’écran 2020-11-07 à 18 12 52
lainz commented 4 years ago

Wow amazing! Thanks. I think we did enough for this weekend. Next week we can continue.

lainz commented 4 years ago

I couldn't resist =)

So, I've added the SVG button with a default one. Still I think I did the DPI scaling wrong, can you check?

Also I'm preventing that the user enters empty resources, always must be a string there.

Thanks for the colorize, now with a single image we can have all the states, since adding light, darkness and graying out the image results in all the states with less effort.

Anyways I've added Normal, Hover and Active for Button.

I've used only a single slice scaling measure for all states, anyways is the good way to go, since button should not change too much in each state, just color for example.

Anyways if you want, change it and structure it as you wish (using classes for each kind of drawing, or anything) before we do the release so it is backward compatible =) buttons

lainz commented 4 years ago

I found a bug, seems that there's a limit in the string property, so I can't set large strings, I will replace with TStringList.

lainz commented 4 years ago

Fixed.

Check this button =) Like 3D (is just a diagonal gradient with 3 colors). nice

Another with a custom bottom. better shapes

I found a way to make pixel perfect icons with Inkscape. So 1px is actually 1px drawn on screen.

You need to setup 2 configurations. First disable this option Behaviour > Transformations > Scale paths width. setting1

Second in document options change scale x and scale y to 1. As well I set the units to pixel in the top combobox and in the bottom combobox as well. setting2

Then when you resize a path, it will not change the width defined (usefull for creating rectangles and buttons, to don't make it wider than it should be), and it will be 1px=1px on screen.

lainz commented 4 years ago

Hi, I've implemented saving and loading from file, stream and resource.

If you right click the component in the IDE there are 2 menus, one for load from file and other for save to file.

Here are 2 demo themes to test. green_and_red_themes.zip

circular17 commented 4 years ago

Awesome.

I'm adding a few things: the blend operation for colorize, fixed the focus rect (was filling rectangle), DPI, anchors, optional SVG for alternative states themetest.zip Capture d’écran_2020-11-08_12-03-19Capture d’écran 2020-11-08 à 12 09 59

lainz commented 4 years ago

Hi, thanks for the improvements.

I found a bug, if you delete a control that has an assigned theme and you change the theme there is an access violation. Seems that the control is still assigned in the controls list of the theme. Maybe on destroy we can remove that control from the list.

circular17 commented 4 years ago

Oh I see. I fixed that.

That may be the fastest fix I've ever done :D

lainz commented 4 years ago

Indeed, you was really fast, when I commented then I went to fix it as well.

In less than a minute you posted the solution. I was writing the same code as you, but you was faster, when I was going to commit you already posted here =)

circular17 commented 4 years ago

At least we agree on the solution. :)

lainz commented 4 years ago

I fixed the ThemeButton behaviour, the bug that it keeps highlighted when opening a show message dialog.

So I think we're good to release, of course is missing the glyph property but that's complex to add, so better for the next time.

lainz commented 4 years ago

Well I think next is the svg image list. Because without that will be hard to define the glyph for the button or at least not very handy for other uses like a svg toolbar.

I was investigating and found this http://docwiki.embarcadero.com/RADStudio/Sydney/en/Storing_and_Loading_Unpublished_Properties

Maybe is the same thing in Lazarus.

Maybe is not that hard to make the editor. We just need to store in a hidden list of tstringlist. The editor just with the buttons add, remove, move up and down, replace and nothing more.

I will take a look in the morning.

The target width and height can be the same for all images inside, to be consistent. By default proportionally scaled and centered options turned on if that makes sense to have that..

circular17 commented 4 years ago

Sounds good to me. I suppose it is not exactly the same with Lazarus. We can look at how TImageList saves things. You need to override the DefineProperties procedure.

procedure TCustomImageList.DefineProperties(Filer: TFiler);

  function DoWrite: Boolean;
  begin
    if (Filer.Ancestor <> nil) and (Filer.Ancestor is TCustomImageList) then
      Result :=  not Equals(Filer.Ancestor)
    else
      Result := Count > 0;
  end;

  function NeedsBitmapAdv: Boolean;
  var
    R: TCustomImageListResolution;
  begin
    for R in Resolutions do
      if not R.AutoCreatedInDesignTime and (R.Width<>Width) then
        Exit(True);
    Result := False;
  end;

var
  ADoWrite: Boolean;
begin
  inherited DefineProperties(Filer);
  ADoWrite := DoWrite;
  Filer.DefineBinaryProperty('Bitmap', @ReadData, @WriteData, ADoWrite);
  Filer.DefineBinaryProperty('BitmapAdv', @ReadAdvData, @WriteAdvData, ADoWrite and NeedsBitmapAdv);
end;   
lainz commented 4 years ago

Ok, seems that we have to deal with binary saving and loading from stream.

circular17 commented 4 years ago

From my experience, binary is ok, you just need to make sure that it allows to be extended. The basic way to do it is to add at the beginning the size of the header, so that if new fields are added, you can increase it, and previous readers can just skip the additional fields:

offset  size  format    content
------------------------------------------
0       4     LE 32bit  header size = HS
4       4     LE 32bit  number of images
HS

and then for the first image:

offset  size  format    content
-------------------------------------------------
HS      4     LE 32bit  size of image file = IS
HS+4    IS    bytes     content of the image file
HS+IS

etc.

To ensure little endian (LE) in a stream you can use LEReadLongint and LEWriteLongint functions from BGRAUTF8 unit.

lainz commented 4 years ago

Hi, seems that using just XML works fine. I've implemented the image list.

The editor has some bug, I can't make to draw the icon in the ListBox, maybe I have implemented badly the draw method or something I can't see.

Captura de pantalla 2020-11-14 105346

lainz commented 4 years ago

Seems it has a bug, it can be edited only the first time the form is edited, then it doesn't works if you add more images.. close and open lazarus to try.

Edit: Seems that it works if you force saving the form, so maybe there is a way to mark that the form must be saved, somewhere...

Edit 2: I fixed it forcing saving when closing the editor.

lainz commented 4 years ago

Fixed =)

Captura de pantalla 2020-11-14 111701

Now we have some basic SVG ImageList.

circular17 commented 4 years ago

Looking good!

lainz commented 4 years ago

I'm testing the svg image list, I've added some glyph to test in the button, but is very limited not like the one in bcbutton. Need a bit of help.

BTW I've implemented that when there is no room for the caption, to display only the glyph, that is a thing that I want to keep.

Captura de pantalla 2020-11-14 132244

Well for me is enough for today, I need to do homework from the university so no more free time. Feel free to change anything if you want.

circular17 commented 4 years ago

Sure I will have a look.

lainz commented 4 years ago

Thanks. I'm seeing a lot of commits about SVG, that's really good =)

lainz commented 4 years ago

Hi, I have implemented the method to populate the image list. You pass an array of integer like this:

BGRASVGImageList1.PopulateImageList(ImageList1, [16, 20, 24, 32]);

There is a memory leak somewhere in the svg image list, I can't see where (not related with the image list populate method, just placing it on a form causes the leak).

circular17 commented 4 years ago

Perfect for people still using other components.

Thanks. I'm seeing a lot of commits about SVG, that's really good =)

Yes, I am trying to fix various things.

lainz commented 4 years ago

Thanks, yes maybe it can work in LazPaint, because I think redoing the toolbar component can be a lot of work.

circular17 commented 4 years ago

Yes I have been postponing it for some time

lainz commented 4 years ago

Some time ago when I did the bctoolbar I had this idea: copy and paste the toolbar code. It didn't work and the editor was not working as well. So I finally inherited it.

circular17 commented 4 years ago

Hi there,

I have done the basic text layout for SVG button. I invite you to look at the code and see if that seems understandable to you.

As you will see, it is now easy to add glyph and text alignment, by giving those instead of bcaCenter.

To use this on the other themes, this needs the text to be drawn in the bitmap and not directly on the canvas.

Note: I have tested it on Retina display, not with DPI scaling on Windows/Linux.

Capture d’écran 2020-11-17 à 10 43 38
lainz commented 4 years ago

Hi looks nice, I will test on high dpi. Edit: It works fine on HighDPI. Feel free to add the property that changes the glyph, with that I think we can do a release, what do you think?

circular17 commented 4 years ago

Property ?

lainz commented 4 years ago

Property ? As you will see, it is now easy to add glyph and text alignment, by giving those instead of bcaCenter.

Yes the "alignment of the glyph or text" property, I think is better in the button, or do you think is good to add in the theme?

circular17 commented 4 years ago

I would rather put the maximum of things in the theme. If you have let's say two types of button layout, then you can use 2 themes. We are rarely going to have one different alignment for each button. There is glyph alignement et text alignment, both can be in the theme. I feel like it is better if we make things simple.

Though that's not a priority in my view. I am focusing on other things now. On BGRABitmap side, I am thinking about adding the ability to save the layered SVGs. This will be useful in the next release of LazPaint.

If you would like to publish BGRAControls, I can publish a version of BGRABitmap now. I think it is functional and people can start experimenting with it.

lainz commented 4 years ago

Ok, yes better in the theme. But is not important we can release without that.

Yes we can release, so people if interested can report bugs or suggestions.