It means you type multiple lines of code all the time and if you forget to wrap things in the #if debugbuild then you have logging messages in your builds that maybe should have been turned off
Personally I have a module, named Debug, that has a method in it – Log
Then where ever I want a debugging message I can write
debug.log "some message"
which is also shorter
Debug.Log is implemented as (I have other overloads as well)
Sub Log(msg as string)
#if debugbuild
System.Log(System.LogLevelDebug, msg)
#endif
End Sub
This way I can write my debug.log messages and know that every last one will disappear in a built app.
In Xojo EndOfLine is two things One is a global method and the other a Class.
Because of how the compiler works it realizes when you mean each one.
In a line of code like
var eol as EndOfLine
EndOfLine can, in that context, ONLY be a TYPE name. So the compiler knows you mean the EndOfLine class.
But when you do
dim eol as EndOfLine = EndOfLine
it knows the FIRST one is the TYPE, the class, and the second HAS to be something that returns a value – in this case the method named EndOfLine. (And this design is something you can also use in your own code)
What this means is that since EndOfLine is also a CLASS it can be extended. And that makes it possible to add things like a Length function.
Public Function Length(extends e as EndOfLine) as integer
Dim s As String = e
Return s.length
End Function
And there you go. Now you can write code like
var position as integer = otherString.IndexOf(EndOfLine)
var leftChars as string = otherString.left(position)
var eol as EndOfLine = EndOfLine
var rest as string = otherString.Middle(position + eol.length)
And NOT have to convert the EndOfLine into a string to get the length
Or in this case linear gradients. If you want linear gradients to work well you really need to calculate the correct start and end points.
This is especially true if you dont want a gradient that simply runs from left to right or top to bottom (or their reverse directions)
While figuring out exactly what I needed to do I had several different suggestions from different people that I found dont work quite right.
If you simply compute the points using sin() and cos() of whatever angle you wont get the right answer. Depending on what radius you use you’ll either get clipping of the gradient inside the rectangle you want to fill or you’ll position the start & end points far enough outside that rectangle that if you have lots of gradient steps some wont show.
For example, if we set the start and end points so they are slightly angled across a square canvas with code like
Var linearBrush As New LinearGradientBrush
linearBrush.StartPoint = New Point(0, g.Height/2-20)
linearBrush.EndPoint = New Point(g.Width, g.Height/2+20)
linearBrush.GradientStops.Add(New Pair(0, Color.Red))
linearBrush.GradientStops.Add(New Pair(0.4, Color.Yellow))
linearBrush.GradientStops.Add(New Pair(0.7, Color.Magenta))
linearBrush.GradientStops.Add(New Pair(1.0, Color.Blue))
g.Brush = linearBrush
g.FillRect(0, 0, g.Width, g.Height)
we get a clipped gradient fill in the upper left corner
because the gradient “rectangle”, when rotated, doesnt overlap the entire rectangle we filled. But notice how the gradient starts at blue & ends at red very nicely. It shows all our steps quite completely.
When you start the gradient too far outside the rectangle you get some steps in the gradient not being as visible as they should be (this one IS much harder to see but notice how little blue there is)
I set the rotation to be 22.5 degree and then set the start & end points using sin & cos using a radius that is the hypotenuse of the right trangle formed from the rectangle center point to the lower right corner (any corner would do since this is a square – it get worse if you use a non-square rectangle) The red circle around the filled rectangle outlines where the start and end points might be place for any rotation. Note how far outside the rectangle they are and the more color steps there are the more get clipped out. The code is as follows
Const pi As Double = 3.1415926535897932384626433
Const degreesToRadians As Double = pi / 180
Const degrees As Double = 22.5
Var linearBrush As New LinearGradientBrush
Var hypot As Double = sqrt( (g.width - g.width/2)^2 + (g.height - g.height/2)^2)
Dim startx As Double = cos(degrees * degreesToRadians) * hypot
Dim starty As Double = sin(degrees * degreesToRadians) * hypot
linearBrush.StartPoint = New Point(g.width/2+startX, g.height/2+startY)
linearBrush.EndPoint = New Point(g.Width/2-startx, g.Height/2-startY)
linearBrush.GradientStops.Add(New Pair(0, Color.Red))
linearBrush.GradientStops.Add(New Pair(0.4, Color.Yellow))
linearBrush.GradientStops.Add(New Pair(0.7, Color.Magenta))
linearBrush.GradientStops.Add(New Pair(1.0, Color.Blue))
g.Brush = linearBrush
g.FillRect(0, 0, g.Width, g.Height)
You can see that in the two images about. Nove how much red & blue the first has compared to the second,. Yet the gradient is set up identically between them to trabsition between the same number of colors using the same number of steps.
The problem is that circle is much too large. Of course we could use a much smaller circle but if we set it so its inside the rectangle then we get the clipping problem back.
So whats the “right answer” ? So far it seems to me that the correct answer is to find a minimal bounding rectangle that is rotated however we want. And to make sure our gradient starts on the edges of that bounding box.
OK so what the kec does that mean ? 😛
Well – in action it looks like this
The white square is the rectangle we’re going to fill with the linear gradient. The light purple circle is the same circile that encloses the rectangle computed much like the one just above here. And the light blue line is where our gradient will run either left to right or right to left (we can invert those two trivially)
Where the gradient runs exactly left to right or top to bottom we can put the start & end points right on the white square. But as we start to rotate the gradient you’ll see some yellow and red lines show. Where the blue line crosses the yellow line is where we need to put the start & points of the gradient. And note those points are NOT on that circle drawn outside the rectangle. But those red & tyellow lines for the minimum enclosing rectangle that, if the gradient starts on its edges, then the entire rectangle in the white square will be filled. The result is this
I expect this mechanism can be extended to apply to any shape using a convex hull approach.
I’ll see about whether I can release this code or not.
Either way you have an explanation of how to compute the right start and end points for linear gradients and apply then to rectangular areas. And you’ll know why things might get clipped off or not fill quite right if you do something else.
Now I’m sure some of you will get to the end of this and go “Well Duh Norm”
Take this post purely as evidence my trig sucks 😛 I freely admit that. It does suck.
Some code looks enticing – but in the long run may cause you as many headaches as it solves.
Some is really innocuous looking like :
var position as integer = otherString.IndexOf(EndOfLine)
var leftChars as string = otherString.left(position)
var rest as string = otherString.Middle(position + 1)
There’s an assumption in this little bit of code and it may not be entirely obvious what it is.
Try it on a mac and its probably fine. And on Windows it makes a mess. The reason is the EndOfLine marker on Windows is of course two characters not 1 and so the assignment of rest is wrong and off by 1.
The fix is to simply use the Length of EndOfLine instead of 1. Note that autocomplete dislikes EndOfLine.Length and it wont compile forcing you to use a temporary variable for this. see this bug report and you appear to not be able to extend it since there’s a global method and class with the same name in the framework.
The following works but you end up declaring a variable to hold the EOL char(s).
var position as integer = otherString.IndexOf(EndOfLine)
var leftChars as string = otherString.left(position)
var eol as String = EndOfLine
var rest as string = otherString.Middle(position + eol.length)
Another that I’ve run into is TextInputStream.ReadLine. The issue here is that if you assume ReadLine reads “one line” then you can be in for a surprise since “one line” depends on what line endings are in use in the file.
If you’re on macOS reading lines from a file from Windows then the lines will include half of the Windows end of line marker (which is 2 bytes – &h0D + &h0A)
And TextinputStream has no mechanism built in to say “please translate line endings as you read” which would be really handy. So you either read all the text in (hoping its not GB of data) and use ReplaceLineEndings, or read “line by line” and clean them up. Or write a new Class that wraps a binary stream that does allow you to specify both an encoding and whether line endings should be converted to the platform specific ones. I chose the latter route. I’ll see about publishing my class on my Github
Any others you’ve run into that look simple and clear but are actually problematic ?
There’s a great debate about what versions of Xojo work on Big Sur.
IF you want to build DESKTOP apps you must use 2019r3.2 or later. Even an empty project fails to build properly.
They rely on a framework that Apple has removed and so the linking of them will fail. Linking Executable ld: file not found: /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation for architecture x86_64
Quite literally this framework does not exist – its got nothing to do with code signing etc
IF you are NOT using Xojo for desktop projects then you may be able to continue using old versions. I’ve successfully used 2017r3 and 2018r4 on Big Sur for Web projects.
I have to admit I have NOT tested console apps.
If you have it would be nice if you posted your results in the comments
Alternatively – why Xojo should put everything in a Xojo namespace and NOT make anything global
I have a few projects that are the results of many many years of coding.
Updating them to the very latest version of Xojo ends up with lots of conflicts since Xojo added a String module with their own global extends for EndsWith and many others that have existed in other peoples code for years.
And, since Xojo didnt add both an extends version AND a non-extends version I have to go adjust all my code because they decided to take over that name. EndsWith is just one example of this. All in all I have about 15000 items like this to go deal with.
And every time I update to a newer version of Xojo, since everything is always in a global namespace, ANY update of Xojo _may_result in many more conflicts like this.
And this taking over of names users may already have used is the problem with a framework that has everything only as extends and always global. Every update may take more of the names you have used for however long and force you to rename and update your code IF you want to keep using code that you may have no reason to want to update.
While I get the syntactic sugariness of the dotted methods names – its hose dotted names that cause this as they MUST be global.
If Xojo had named their module String AND made the methods in string protected then you could always unambiguously refer to yours AND theirs and never have confusion.
Thats not what they have chosen to do and so now we, the users, may end up having to adjust our already working code every time Xojo decides they want to take over another name for some extra functionality.
Way back when, when Apple still permitted 32 bit compiles, the landscape for what you had to do to build an app that would work on macOS was a lot simpler.
Now, not only do you have to build the right kind of executable you need to also do code signing, even if only ad-hoc signing, to run even a debug app on the very latest versions of macOS.
First off you cant use the native file systems of Windows or Linux to create a bundle. Those file system dont support what macOS needs to create a proper bundle. So you have to build into a zip file or some other container type that DOES.
As well, since you need to sign the build, you either sign it when you compile OR you try & somehow sign when you finally have the app ON a Mac or suitable VM. At best Xojo might make a remote debug run from Windows or Linux to macOS compile & link on Windows or Linux and then send the code to the remote stub. Then the remote stub might unpack this and apply an ad-hoc signature & start the application. You would still needs a Mac, or suitable VM, to sign code to be able to remote debug the app.
But this still leaves developers on Windows & linux unable to build & sign, or notarize, a Mac app that they could send to anyone else. I dont expect that signing a macOS build on Windows or Linux time will be possible since this would require Apples security framework be ported to another OS. I doubt this will ever occur. Trying to duplicate what Apple has implemented in their framework AND keeping up to date with it, without Apples source code, seems very difficult if not downright impossible.
And, to sign and submit an app to the App store or to notarize one you still need a Mac and do those tasks there.
It seems that if you want to develop FOR a Mac you are going to have to do that ON a Mac for the foreseeable future – if not forever.
Now, I want to be clear. This is NOT Xojo’s fault. And I dont expect they will be able to “fix” it. Apple has changed the rules a lot and made things more difficult. And, the side effects are what they are. Don’t blame Xojo for this one.
The upper bound of the array. Size must be a number or a constant.
Thats been altered now and the case noted above marked “fix” . Documenting a shortcoming isnt the same as fixing the shortcoming though.
In a 64 bit application Integer usually means a 64 bit integer but in reality arrays dont use Integer, they use Int32. This limitation isnt considered a bug as
The Integer type is Xojo compiler/framework magic. Behind the scenes, LastIndex (and Ubound) are Int32 because we still support 32 bit OSs. Should the day come when we no longer support 32 bit operating systems, we could then change arrays to Int64 but even that would then be a LOT of work involving updating the compiler, the frameworks, etc.
In most Xojo code and use cases we never have to worry about “compiler magic”1 etc and integer is an Int64 in 64 bit apps and an Int32 in 32 bit programs.
As users we shouldn’t have to know, or care about “magic” – it should just work like it does everywhere else.
1 In a language like C++ this “magic” amounts to something like
// your C++ compiler MAY define specific tags to tell if you're compiling 32 or 64 bit
#if 64Bit // with whatever specific code your C++ compiler needs to tell this is a 64 bit build
typedef int64_t integer ;
#elif 32Bit // with whatever specific code your C++ compiler needs to tell this is a 64 bit build
typedef int32_t integer ;
#endif
They even have code in their headers for plugins that handles this (this is from the 2020r2 Plugin SDK include file REALplugin.h)
UPDATE : Xojo fixed the documentation and marked the original report “fixed”. But fixing the docs doesnt fix the actual issue reported. I have an older feature request to make arrays 64 bit