MaxGUI custom gadget font (external font for GUI)

BlitzMax Forums/MaxGUI Module/MaxGUI custom gadget font (external font for GUI)

dw817(Posted 2016) [#1]
Hello.



I am deciding that the 70's menu interface I currently have for my Carryall 750k Method is not going to be well received, especially since you hit numbers from 1-5 followed by the ENTER key in a menu to do something. Not very modern.

So ... I am delving into the mysterious world of MaxGui, a leaner meaner GUI for more serious applications - which I hope is what this forum area is about.

I would like to know please how to use a custom Truetype *.ttf font, not one installed in Windows, to be used for all my gadgets, including pull-down menus, fileboxes, alerts, the works.

I know how to do this for standard BlitzMAX apps:
Strict
Graphics 640,480
Global font:timagefont=LoadImageFont("fonts/audimrg.ttf",32)
SetImageFont font
DrawText "Amazing Grace.",50,50
Flip
WaitKey
But no, it's different apparently in MaxGUI.
Strict
Import maxgui.drivers

Global scrn:TGadget=CreateWindow("Carryall The 750k Method", 0,0,512,512,,WINDOW_TITLEBAR|WINDOW_CENTER)
Global gadget:tgadget[99]
Global font:tguifont=LoadGuiFont("fonts/audimrg.ttf",16)
SetGadgetFont scrn,font
addgadget 0,"text",0,0,200,50
WaitKey

Function addgadget(id,typ$,h,v,x,y)
  If typ$="text"
    gadget[id]=CreateTextArea(h,v,x,y,scrn)
  EndIf
EndFunction
And no, this code does not work. It does not change the font.

So if you guys have any ideas on how to do this, it might just help some other coders as well who aren't content with default Arial or New Courier for their MaxGUI projects as well.

Thanks in advance !


degac(Posted 2016) [#2]
Hi

to change a gadget font you need to use LookupGuiFont() or LoadGuiFont()
The first one is needed to find a system-font with some features (ie: GUIFONT_MONOSPACED), the latter just to load what font you want.

The you can use SetGadgetFont gadget,TGUIFont


dw817(Posted 2016) [#3]
Hi Degac.

No, I need to load specifically that one and very font from a directory that is not inside \windows\, no, it is inside c:\david\blitz code\fonts\.

Do you have code or an example that can load and load and make use of a font from such a directory for use specifically with MaxGUI ?


degac(Posted 2016) [#4]
Ok, I understand now.
But I doubt you can do it, as the 'fonts' (and the gadgets etc) are 'handled' by the underneath OS. So, if the OS cant' see the font, it can't use it.

Possible solutions:
- use panel & pre-loaded images (overkill in terms of work and speed)
- use your own font, forcing to install it in the font-system folder

Here should be a possible solution for this (see latest post, but I never found if anyone developed it) http://www.blitzbasic.com/Community/posts.php?topic=88949


dw817(Posted 2016) [#5]
So what I'm looking for is a working example of AddFontResource() for MaxGUI BlitzMAX then - which hopefully will let me use a font that is not currently installed in Windows.

I wish it was easier than this. LoadImageFont() to load or INCBIN any TrueType font at all is one of the very definite boons of BlitzMAX. :/


Kryzon(Posted 2016) [#6]
That would be unproductive. The technique used in binding that WinAPI function in BlitzMax is the same for all other WinAPI functions.
You use an Extern "win32" block, and you consult the MSDN documentation on the function to know the amount and types of parameters etc.

Instead of looking for an example, you should be looking for how to make these Extern bindings yourself, and how to consult the documentation on the function.
Then when you find a new OS function or C library that you want to bind you'll do it yourself instead of reaching a dead end.

EDIT: if you don't know how to start, I will give you an example for that particular function.


grable(Posted 2016) [#7]
About your top example. You cant set the font of a window, as the only thing that has text is the titlebar and that font is governed by the system. IE its a system wide setting.

Try setting the font of individual gadgets instead.

Now, i believe there is a way for windows to inherit the font of their parent window, but that bit is probably not enabled for maxgui.

EDIT: I didnt notice that you also was trying to load one from disk...
Youd have to intern a font for that to work properly.


grable(Posted 2016) [#8]
I do have some code to register fonts though, as i had the same problem a few years back ;)

RegisterSystemFont.bmx

Registry.bmx



dw817(Posted 2016) [#9]
All THIS, just to load a font ? Wow ... I appreciate your immensely hard work, Grable.

I did come across this by DEGAC.
Function SetGlobalGadgetFont:Int(par:tgadget=Null,fnt:tguifont=Null)
	If fnt=Null Return -1
	If par=Null Return -1
	If par.kids=Null Return -1
	For Local s:tgadget=EachIn par.kids 
			SetGadgetFont s,fnt
	Next
End Function
This changes the SIZE of the font and lets you select from a limited choice of system fonts which is nice.

But isn't there any code about this size or smaller just to load a font not installed in Windows ?

And no, I don't want to modify a user's system font directory by directly installing a new one just to display in a program because it's not part of Windows.
. . .



Let me ask this, is there a way to place a gadget directly on top of another and have it appear. In this case from the code I posted above, I want the Text gadget to appear above the Canvas gadget.

If I can do this, then I can use BlitzMAX's own LoadImageFont to display text details of any controls I add to the ZVideo.


grable(Posted 2016) [#10]
All THIS, just to load a font ? Wow ... I appreciate your immensely hard work, Grable.
Well not really, the meat of the code is in RegisterSystemFont(). The other file is just an api for accessing the registry, and its fairly complete set of functions for doing that.
Even though you only really need one of them.

Let me ask this, is there a way to place a gadget directly on top of another and have it appear. In this case from the code I posted above, I want the Text gadget to appear above the Canvas gadget.
If you create the text gadget AFTER the canvas gadget it should appear on top.
But it wont help you with what you want to do, as it will draw its own thing no matter what is beneath it.

And no, I don't want to modify a user's system font directory by directly installing a new one just to display in a program because it's not part of Windows.
As said by others in this threads, you just cant. If you want to use Windows GUI controls, you MUST register a font with the system for it to be able to be rendered by the system.

I personally dont see the problem with doing it like that, as EVERY other application that has its own fonts and uses windows gui allso must do this.
But it can be done transparently to the user, by registering it upon application startup and unregistering it when exiting.


dw817(Posted 2016) [#11]
Unfortunately it is not, Grable. It does not appear at all. Here is current code:




grable(Posted 2016) [#12]
Well canvases are a bit special, in that the system takes over its rendering, essentially sidestepping the normal gui drawing order.
So with any other control it will work.

To simulate a canvas, you can use a Panel with a Pixmap attached to it. Though it would probably not be a good idea to do realtime rendering to it that way.

EDIT: Hmm seems it actually will work if you attach it to the canvas instead (like you should really).
But dont be surprised if they start fighting about who should be on top ;)


dw817(Posted 2016) [#13]
No, I was talking with someone else, and they said this font technique of yours is NOT permanent. That it can be removed. Okay, let's do this the right way. Could you please show in =ONE= source code the method to load and use/install a font from said location and file, /fonts/cpmono.ttf.

In this demo code it maybe show two fields of proof it is working, one a text field, where you can type in, the other static text.

If exit is chosen from here, then the font is unloaded/UNINSTALLED and the program ends.

With that code I should be able to glean what is going on.


grable(Posted 2016) [#14]
Actually it is permanent. That is why i made it in the first place.
That is, permanent until you or the user explicitly remove it.

To make it NOT permanent, use only the AddFontResource() function.
It will persist until you reboot the computer or log off.

To remove it yourself before that, use RemoveFontResource() function.


dw817(Posted 2016) [#15]
Okay, I am looking at your two functions, Grable. RegisterSystemFont and Registry Could you please post an example using these functions correctly.

And - doesn't there need to be a routine for UnregisterSystemFont ?


grable(Posted 2016) [#16]
I would think its use is self evident by its prototype.
But i also just said you dont need to use that version IF you dont want it to persist across reboots.

Against my better judgement i modified your sample to use the two functions i mentioned in my post above (and others have mentioned before that)

It literally cant be simpler to use those functions! Do you REALLY need code to tell you how to call a function with a single argument?
Because it honestly feels like you refuse to read the posts detailing HOW to do something and demand source code for every little damn thing.


dw817(Posted 2016) [#17]
Understand Grable. I'm a BASIC programmer. My mindset is still very much in the 70s. Any help you give here I very much and deeply appreciate. A lot of this stuff is very new to me.

I in turn am more than happy and generous to give out my works and source code in the efforts of helping and educating others. While my coding methods are dated, I am usually pretty good about solving problems involving logic, math, and method.

I know you have seen the code submits I've done recently and since I arrived.

Despite your blustering your program does not work here. I went ahead and made the directory "c:\dump\" and put a select font there and renamed it to "destroy x.ttf" - and running your program, it still does not work.

Now just think of me trying to write on my own what you did above - and it still didn't work. Where would I be then ? :)

BTW, here is the current model. I have no problem about sharing this w others. AddGadget and FirstCap are my own routines.



The next thing I was going to do was have a short-cut keeper, where a short-cut letter is always used but it checks to see if it's already in use, and if so, it reads the next character and instead makes that the shortcut key. I.E.:

[C]opy
C[r]eate


grable(Posted 2016) [#18]
Its not about about the lack of will to share code. I too have no problem with that.
But when detailing how to perform some action or other, the willingness to learn of the receiving party is paramount.
Do some research yourself, put some function names into google and see what pops up etc.
Usually all it takes is a nudge in the right direction and people manage to figure out the rest on their own.
You have a direct line to whatever kind of information you could ever want in the form of "the internet", which is also my source of knowledge half the time ;)
What i dont know, i figure out. Maybe im special i dont know, but i actually LIKE figuring things out and solving problems. Even if they arent even MY problems to begin with.

And thus ends my rant :P
On to coding...

The reason my example does not work for you is because fonts have both a filename (its actual file) and a name (the name of the font itself).
Renaming a font to something else only gives you ONE part of that piece.

Try using your own font instead, your own file-name in the calls to AddFontResource() and RemoveFontResource(). And also use your own font-name in the call to LoadGuiFont().

EDIT: To get the name of the font, use the builtin Font Preview program that comes with windows (the default for fonts) and look for the text "Font Name:" in the upper left part of the window.


Kryzon(Posted 2016) [#19]
I think there's a cleaner way by making use of the TWindowsFont class from MaxGUI.
The API needed is used here:
https://autohotkey.com/boards/viewtopic.php?f=6&t=813

- Load a .TTF file from the hard disk with AddFontResourceEx with the private flag, or load an IncBin-ed .TTF file with that AddFontMemResourceEx also with the private flag.
This gives you an HFONT value.

- Use that HFONT value with MaxGUI's TWindowsFont.LoadFromHandle( HFONT ) to get a MaxGUI font.

- Use the MaxGUI font with that "global gadget font" convenience function.
Gadgets should have the custom font.

- If the HFONT value was created from memory with AddFontMem(...), then when the application ends it's necessary to remove it, for safety (even though the OS will automatically do that itself).
It's easy to do this by having that HFONT be a global value that is deleted in a function registered with OnEnd.

Someone's gonna have to write all that.


dw817(Posted 2016) [#20]
I have always learned by example, Grable. That's what makes me the good (or bad) programmer I am today.

In the past I wrote a database that kept track of all the churches in the world. I wrote it in QBasic.

The original programmer before had written his in C. It took 2-days to compile the data with his model. Me ? My program to replace his took 2-hours to calculate everything. It also had bells, whistles, lights, and a cool screensaver. :)

So, yes, I do know how to program. But I learn because of Example. That is my anvil and that is my crucible. There are many MANY QBasic example codes out there and were there before the Internet. It is from those I learned the bulk of my programming.

It is from those codes I learned what I did and started to build Scenario 1, an ambitious project, a full and complete RPG Maker. I was only interrupted from someone who said my coding was good and wanted me to advance, to start to program in GFA, a considerably more powerful language.

And with a little kicking and screaming and promises (that weren't broken), I did.

And I did go on to write Scenario 2 - completely in GFABasic, a Windows 3.1 Programming Language. And it still runs perfectly in Windows XP computers and before.

http://creatools.gameclassification.com/EN/creatools/481-Scenario-RPGMaker/index.html

I'll even send you the source code if you like - if you think you can learn something from it. It's a little over 20,000 lines long.

Thus ends =MY= rant. :)

Now, understand the code you write is just 'barely' on the edge of my understanding. You are saying that it needs to go by the actual Font Name ? This then is viewed when you press ENTER on the file ?

Let me see ... Font Filename, "destroy x.ttf" actual Font name, "Audimat Mono"

So change the code you submitted above thus ?
Global font:Tguifont=LoadGuiFont("Audimat Mono",20)
No, that didn't fix it either. There is still no font change.

No other lines have been changed from your code.

. . .

Kryzon, what programming language is that ?


grable(Posted 2016) [#21]
No, that didn't fix it either. There is still no font change.
Then your doing something wrong perhaps...

I got the font from here http://www.dafont.com/destroy-x.font
Drop the font in the same directory as the sample below.

And with some of the suggested code by Kryzon, to not have to unregister a font and handle incbin.
(i see no reason to use LoadFromHandle() as to get a handle one has to call CreateFont anyway, so it becomes less generic. Obviously if one is including a font one already knows its name)



Kryzon(Posted 2016) [#22]
I didn't know about IncbinPtr, that makes it simple.

In case you don't know the family name of the font, you'd do something like this I imagine (untested):
Global font:TWindowsFont = Null

Local HFONT:Int = RegisterGuiFont( "incbin::destroy x.ttf" )
If HFONT Then 
   font = New TWindowsFont
   font.LoadFromHandle( HFONT )
   setGlobalGadgetFont( zvideo, font )
EndIf



dw817(Posted 2016) [#23]
Hi Kryzon. Hmm ... I can but try. Still not working. Here is current model. Perhaps I'm doing something wrong in my code ? It's running. I have the RegisterGuiFont() function in place alongside your code - it just doesn't want to work.

Feel free to make a change in it if you think I did something in error.




grable(Posted 2016) [#24]
That wont work. RegisterGuiFont() returns only a boolean for success or failure.
You still have to load it via LoadGuiFont().
The reason for this is that the underlying functions does not return HFONT either. Id have to call CreateFont() manually, and that would lock style and size to it. So it wouldnt be usable as a generic font.

Did my example not work? If it didnt even after downloading that font, i have no idea whats wrong.
Thats when i start asking: What version of Windows your running, if its 32-bit or 64-bit and the like.
But since those APIs are supported since Windows 2000, and it doesnt complain about missing functions i assume you have newer.


dw817(Posted 2016) [#25]
Taking a break. Briefly, my OS, Windows 8.1, 64-bit quad. Speed ? REALLY FAST. :)
Resolution 1024x768.

System is stored: "c:\windows"


Kryzon(Posted 2016) [#26]
Ah that's right, the AddFontMem(...) seems to return some handle that you use to delete it later. Then using the HFONT path seems more complicated.

It's simpler to just go to the font file, right-click and select Preview to open Windows Font Viewer that will show you what's the family name.

Then use LoadGuiFont function from MaxGUI like grable originally showed in post #21. Post #21 is the answer.


dw817(Posted 2016) [#27]
Hmm ... And now the font in "\dump\" won't delete at all. I'm getting the message:

The action cannot be completed because the file is open in System.

This is after I have shut down BlitzMAX and checked for any stray EXEs with Process Explorer - and there aren't any.

Likely this can be fixed by shutting everything completely down and rebooting the computer. Ok, maybe I have my answer there. If it's possible to accidentally lock a file by registering it so no-one can delete it at all unless it is properly released and only by rebooting or code - I really should be messing in that kind of area. That is not my type of coding.

Further, I can't think of a single Windows interface I have ever seen that uses a custom font, you know where you have a text input field, radio buttons and the like. Perhaps they too thought it was too much work or too invasive to the OS as well. It's just a curiosity I thought I would explore.

Now as for putting images in, that shouldn't be a problem. As long as I don't have the image override any controls, it should work. I can put a nice unobtrusive logo in the top-right-hand corner and continue my control interface work.

Thanks for the help guyz. Now maybe if someone posted just an EXE, no source, to show what a custom font looks like on all the standard window controls, it might be further appreciated. Failing that, I'm good where I'm at. :)


grable(Posted 2016) [#28]
If it's possible to accidentally lock a file by registering it so no-one can delete it at all unless it is properly released and only by rebooting or code
That only happens if you terminate the application without releasing the resource, which is to be expected with that specific API.
And your own example did just that, by not having a way to cleanly exit the application.

There is no such limitation with the one using AddFontResourceEx() and FR_PRIVATE (as in post #21), as it will be released when the process terminates no matter what.
And if its incbined theres not even a file to lock ;)


dw817(Posted 2016) [#29]
I'm good, Grable. Still, maybe a screenshot or something of a custom font in a standard GUI interface might be interesting to look at.

If there is no easy way to have a custom font in a GUI interface without pulling out the kitchen sink from all the way in the bathroom, likely it was not meant to be.

Failing that, I can proceed without, and am already working on my libs, exchanging reference names and lookup for object IDs.


Kryzon(Posted 2016) [#30]
Go take a rest and try again tomorrow dude. I copy-pasted post #21 and it works, but I deleted the Incbin call and specified a path to a local TTF file in my harddisk.

It didn't work at first, so I renamed the TTF file to a name without spaces and it worked -- I'm not sure if this made a difference, I'm too tired to tell.




dw817(Posted 2016) [#31]
Good Morning ! Yes, it looks like it works. :D

However, every font that gets 'registered' also gets locked in. I think I asked earlier, is there an UnregisterGuiFont() routine ?

Here is the current model:



Oh, and before I reinvent something - has someone already made a gadget maker ? That is, a program that lets you use the mouse and keyboard to set down and arrange gadgets on a canvas of your choosing and then give you the option to convert that lot to raw BlitzMAX code afterwards ?

And - there is no way to change the menu font ?


grable(Posted 2016) [#32]
I dont know why yours is locking, i cant seem to reproduce it. Not even force terminating the application locks the file...
In any case, incbining the font would fix that.

To unregister a font call the function below, though there should not be a need for it. As the OS will unregister the font when the process terminates.

EDIT: Updated both functions.
Private
Extern "Win32"
	Const FR_PRIVATE:Int = $10
	Const FR_NOT_ENUM:Int = $20
	
	Function AddFontMemResourceEx:Int( p:Byte Ptr, sz:Int, r:Byte Ptr, count:Int Var)
	Function AddFontResourceExW:Int( fn$w, flags:Int, r:Byte Ptr)
	Function RemoveFontResourceExW:Int( url$w, flags:Int, r:Byte Ptr)
EndExtern
Public

Function RegisterGuiFont:Int( url:String)
	Local font:Int
	If url.StartsWith("incbin::") Then
		url = url["incbin::".Length ..]
		Local count:Int
		AddFontMemResourceEx( IncbinPtr(url), IncbinLen(url), Null, count)
		Return count > 0
	Else
		Return AddFontResourceExW( url, FR_PRIVATE, Null)
	EndIf
EndFunction

Function UnregisterGuiFont( url:String)
	If Not url.StartsWith("incbin::") Then
		RemoveFontResourceExW( url, FR_PRIVATE, Null)
	EndIf
EndFunction


From https://msdn.microsoft.com/en-us/library/windows/desktop/dd162923%28v=vs.85%29.aspx :
When you try to replace an existing font file that contains a font with outstanding references to it, you might get an error that indicates that the original font can't be deleted because it’s in use even after you call RemoveFontResourceEx. If your app requires that the font file be replaced, to reduce the resource count of the original font to zero, call RemoveFontResourceEx in a loop as shown in this example code. If you continue to get errors, this is an indication that the font file remains loaded in other sessions. Make sure the font isn't listed in the font registry and restart the system to ensure the font is unloaded from all sessions.

So it might be a previous session that didnt clean up properly.
Try copying the font to a new file and see if it gets locked even after normally exiting the application.


dw817(Posted 2016) [#33]
Hi Grable. Yep, that fixed it. And yes, the fonts were definitely getting stuck. I already did a reboot before I tried this new code. And - unfortunately these fonts really don't look very good compared to what the system chooses.

After all that. :) Well, I =CAN= see where this routine would come in handy for RPG text where you want to have a large and fancy font.

There's nothing to say I could actually (for the first time) make a true RPG Maker that makes use of standard Windows GUI controls to modify and edit the world file.

Here's another question. Is it possible to change the background color of a field you insert text to ?


grable(Posted 2016) [#34]
Is it possible to change the background color of a field you insert text to ?
SetGadgetColor and SetGadgetTextColor.
Press F1 twice on a MaxGI function and youl get a list of all of them.


degac(Posted 2016) [#35]
@Grable & other: many thanks for the solution posted!

I think I put on the online manual for future request... too handy in some occasion!


Kryzon(Posted 2016) [#36]
dw817, I suggest renaming the thread to something like "MaxGUI custom gadget font", so people know what it's about just by reading the title.
Objectivity comes to mind.


dw817(Posted 2016) [#37]
Ah sure, Kryzon. That's a better title. I think this question has been well answered. Thanks to all contributors !

Got the gadget page done, working on the internals now.



Not sure why it appears blurry here.