Over the past years iv’e reported a few compiler issues
Here’s the current list of open issues I’ve reported
55502 adding a constant to a class cant use an expression while one in code can 56376 compiler misidentifies syntax error on shared factory in super class 56378 factory pattern cant be implemented properly in Xojo esp when items are in a namespace 56765 compiler weird error message seems to be linked to wrong order of evaluation 57500 debugging a while loop in debugger appears to skip entire loop 58900 compiler incorrectly identifies unused local variables 59004 endexception constructor should probably be restricted in some fashion 59061 compiler gives a very ambiguous error message on select case statement 61901 compiler reports completely bogus error 61949 xojo does not warn of conversion on assignment 63438 compiler gets comparison confused with assignment 56264 make it so when a method returns an array you can immediately index into the return result 56272 optional parameters can only be “right most” parameters 60207 command line arguments for debug run cannot use a #constant
And there two I would love to see Xojo fix as its impossible for the usage to be ambiguous
56375 compiler incorrectly identifies “global” as syntax error In something like an enumeration its impossible for the tokens for Global, Protected, Private and Public to be ambiguous. An enumeration is written as
Enum myenumeration
value
value
End Enum
And even if the Enum is scoped as private its , at worst, written as.
Private Enum myenumeration
value
value
End Enum
It would be simiar for Public, Private and Global. No one of those value lines can be mistaken for the scope of anything else.
56411 compiler misidentifies “attributes” as syntax error in spots where it cannot be ambiguous
Attributes is handled similarly to the above case for scope. And has the same impossible to be ambiguous situation.
Both of these cases are most definitely fixable but so far Xojo has declined to do so.
Out of my list iof just compiler bugs – 1 has been fixed – 2 closed as By Design (noted above) – 1 closed as Not Reproducible (and I have no idea how to reproduce it either) – 11 are Reproducible – 3 feature requests Reviewed
If the applications are developed by the same Xojo version, and all the exe files of several applications are placed in the same folder, is it possible for distribution purposes to have only one Libs folder ? In such case, which would be the name of this unique Lib folder ?
There are some caveats that havent been mentioned
You DO need to recompile everything using the same version of Xojo. That way the Xojo framework, and supporting plugins libraries are all the same version.
The other thing you do need to be careful about is they need to all be the same kind of applications. You cant properly combine the libs for a desktop app and a console app into just one set as the XojoFramework for desktop and Console are not the same.
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.