From 1007a0a681f0743ac78bf609298bde08c02d74aa Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 3 Oct 2026 23:52:03 +0200 Subject: [PATCH 01/27] docs: Core page on how Line, Circle, PSet and Scale syntax becomes a call with a flags argument; linked from every drawing surface; bare Scale resets to twips --- builder/page-baseline.json | 2 +- builder/symbol-baseline.json | 1 + docs/Documentation/Permanent-Links.md | 2 +- docs/Reference/Core/Graphics-Methods.md | 195 ++++++++++++++++++ docs/Reference/Default/VB/Form/index.md | 10 +- docs/Reference/Default/VB/PictureBox/index.md | 8 + docs/Reference/Default/VB/Printer/index.md | 8 + .../Default/VB/PropertyPage/index.md | 10 +- docs/Reference/Default/VB/Report/index.md | 10 +- .../Reference/Default/VB/UserControl/index.md | 10 +- docs/Reference/Glossary.md | 2 +- docs/Reference/Statements.md | 2 + 12 files changed, 253 insertions(+), 7 deletions(-) create mode 100644 docs/Reference/Core/Graphics-Methods.md diff --git a/builder/page-baseline.json b/builder/page-baseline.json index a32e7a3c1..73b27bf71 100644 --- a/builder/page-baseline.json +++ b/builder/page-baseline.json @@ -1,5 +1,5 @@ { "src": "docs", - "pages": 921, + "pages": 922, "staticFiles": 250 } diff --git a/builder/symbol-baseline.json b/builder/symbol-baseline.json index f269aff8b..6da00f5ba 100644 --- a/builder/symbol-baseline.json +++ b/builder/symbol-baseline.json @@ -105,6 +105,7 @@ "/tB/Core/Get", "/tB/Core/GoSub-Return", "/tB/Core/GoTo", + "/tB/Core/Graphics-Methods", "/tB/Core/Handles", "/tB/Core/If-Then-Else", "/tB/Core/Imp", diff --git a/docs/Documentation/Permanent-Links.md b/docs/Documentation/Permanent-Links.md index bde6deb14..f5cfd958d 100644 --- a/docs/Documentation/Permanent-Links.md +++ b/docs/Documentation/Permanent-Links.md @@ -60,7 +60,7 @@ A name can have several entries, and many do: `Add` is a method of a dozen class - [Date](../../tB/Core/Date), [Declare](../../tB/Core/Declare), [Deftype](../../tB/Core/Deftype), [DeleteSetting](../../tB/Core/DeleteSetting), [Dim](../../tB/Core/Dim), [Do-Loop](../../tB/Core/Do-Loop) - [End](../../tB/Core/End), [Enum](../../tB/Core/Enum), [Erase](../../tB/Core/Erase), [Error](../../tB/Core/Error), [Event](../../tB/Core/Event), [Exit](../../tB/Core/Exit) - [FileCopy](../../tB/Core/FileCopy), [For-Next](../../tB/Core/For-Next), [For-Each-Next](../../tB/Core/For-Each-Next), [Function](../../tB/Core/Function) -- [Get](../../tB/Core/Get), [GetSetting](../../tB/Core/GetSetting), [GoSub-Return](../../tB/Core/GoSub-Return), [GoTo](../../tB/Core/GoTo) +- [Get](../../tB/Core/Get), [GetSetting](../../tB/Core/GetSetting), [GoSub-Return](../../tB/Core/GoSub-Return), [GoTo](../../tB/Core/GoTo), [Graphics-Methods](../../tB/Core/Graphics-Methods) for `Line`, `Circle`, `PSet` and `Scale` - [If-Then-Else](../../tB/Core/If-Then-Else), [Implements](../../tB/Core/Implements), [Input](../../tB/Core/Input), [Interface](../../tB/Core/Interface), [Is](../../tB/Core/Is) - [Kill](../../tB/Core/Kill) - [LBound](../../tB/Core/LBound), [Let](../../tB/Core/Let), [Line-Input](../../tB/Core/Line-Input), [Load](../../tB/Core/Load), [Lock](../../tB/Core/Lock), [LSet](../../tB/Core/LSet) diff --git a/docs/Reference/Core/Graphics-Methods.md b/docs/Reference/Core/Graphics-Methods.md new file mode 100644 index 000000000..aae189a12 --- /dev/null +++ b/docs/Reference/Core/Graphics-Methods.md @@ -0,0 +1,195 @@ +--- +title: Line, Circle, PSet, Scale +parent: Statements +permalink: /tB/Core/Graphics-Methods +--- +# Line, Circle, PSet, Scale +{: .no_toc } + +Turns the drawing syntax of **Line**, **Circle**, **PSet** and **Scale** into an ordinary method call with a flags argument. + +Drawing surfaces such as [**Form**](../Packages/VB/Form/), [**PictureBox**](../Packages/VB/PictureBox/) and [**Printer**](../Packages/VB/Printer/) take these four methods in a form that is not an ordinary argument list: the points are written `(x, y)`, two points are joined by `-`, **Step** makes a point relative, and **B** or **BF** turns a line into a box. The compiler recognises the method name, reads that syntax, and calls the method with a *Flags* value first that records which parts were written. The method receives plain numbers. + +Syntax: + +- > *object*.**Line** [ [ **Step** ] ( *X1*, *Y1* ) ] -[ **Step** ] ( *X2*, *Y2* ) [, [ *Color* ] [, **B** [ **F** ] ] ] +- > *object*.**Circle** [ **Step** ] ( *X*, *Y* ), *Radius* [, [ *Color* ] [, [ *Start* ] [, [ *End* ] [, *Aspect* ] ] ] ] +- > *object*.**PSet** [ **Step** ] ( *X*, *Y* ) [, *Color* ] +- > *object*.**Scale** [ ( *X1*, *Y1* )-( *X2*, *Y2* ) ] + +## The call the compiler makes + +Each form becomes a call with *Flags* as the first argument, followed by every remaining argument in a fixed order. A part that is left out is passed as `0`, with its flag clear, so the method can tell an omitted *Color* from a *Color* of `0`. + +| Method | Parameters, in order | +|--------|----------------------| +| **Line** | *Flags*, *X1*, *Y1*, *X2*, *Y2*, *Color* | +| **Circle** | *Flags*, *X*, *Y*, *Radius*, *Color*, *Start*, *End*, *Aspect* | +| **PSet** | *Flags*, *X*, *Y*, *Color* | +| **Scale** | *Flags*, *X1*, *Y1*, *X2*, *Y2* | + +The coordinates, *Radius*, angles and *Aspect* are **Single** values, *Color* is a **Long**, and *Flags* is a **Long** bit mask. + +### The flags + +| Value | Name in the VB package | Set when | +|------:|------------------------|----------| +| 1 | StepOnFirstCoords | **Step** precedes the first point (**Line**, **PSet**, **Circle**) | +| 2 | ColorSpecified | *Color* is written | +| 4 | FirstCoordsSpecified | the first point of **Line** or **Scale** is written | +| 8 | StepOnSecondCoords | **Step** precedes the second point of **Line** | +| 16 | Box | **B** is written | +| 32 | BoxFilled | **BF** is written | +| 64 | StartSpecified | *Start* is written | +| 128 | EndSpecified | *End* is written | +| 256 | AspectSpecified | *Aspect* is written | + +The names belong to a private enumeration in the VB package, so code that implements these methods writes the numbers or declares its own constants. **BF** sets only 32, not 16 and 32 together. + +## Line + +The first point is optional. When it is left out, *X1* and *Y1* are passed as `0` and flag 4 is clear, which is how a method knows to start from the current position. **Step** before the first point sets flag 1, and **Step** before the second point sets flag 8. + +| Statement | Flags | Arguments after *Flags* | +|-----------|------:|-------------------------| +| `(1, 2)-(3, 4)` | 4 | `1, 2, 3, 4, 0` | +| `(1, 2)-(3, 4), 5` | 6 | `1, 2, 3, 4, 5` | +| `(1, 2)-(3, 4), 5, B` | 22 | `1, 2, 3, 4, 5` | +| `(1, 2)-(3, 4), 5, BF` | 38 | `1, 2, 3, 4, 5` | +| `(1, 2)-(3, 4), , B` | 20 | `1, 2, 3, 4, 0` | +| `Step(1, 2)-(3, 4)` | 5 | `1, 2, 3, 4, 0` | +| `(1, 2)-Step(3, 4)` | 12 | `1, 2, 3, 4, 0` | +| `Step(1, 2)-Step(3, 4), 5, BF` | 47 | `1, 2, 3, 4, 5` | +| `-(3, 4)` | 0 | `0, 0, 3, 4, 0` | +| `-Step(3, 4)` | 8 | `0, 0, 3, 4, 0` | +| `-(3, 4), 7, B` | 18 | `0, 0, 3, 4, 7` | + +**B** and **BF** are case-insensitive. They are recognised only in the slot after the colour: a local variable named `B` or `F` does not change them, and in the colour slot itself `, B` is an ordinary expression that supplies the colour. + +## Circle + +**Circle** never sets flag 4, because its centre is not optional. *Start*, *End* and *Aspect* each set their flag when written, even for a value of `0`, so a method can tell a *Start* of `0` from an omitted one. The values pass through unchanged, negative angles included. + +| Statement | Flags | Arguments after *Flags* | +|-----------|------:|-------------------------| +| `(1, 2), 3` | 0 | `1, 2, 3, 0, 0, 0, 0` | +| `Step(1, 2), 3` | 1 | `1, 2, 3, 0, 0, 0, 0` | +| `(1, 2), 3, 4` | 2 | `1, 2, 3, 4, 0, 0, 0` | +| `(1, 2), 3, 4, 0.5` | 66 | `1, 2, 3, 4, 0.5, 0, 0` | +| `(1, 2), 3, 4, 0.5, 1.5` | 194 | `1, 2, 3, 4, 0.5, 1.5, 0` | +| `(1, 2), 3, , , 1.5` | 128 | `1, 2, 3, 0, 0, 1.5, 0` | +| `(1, 2), 3, , , , 2.5` | 256 | `1, 2, 3, 0, 0, 0, 2.5` | +| `(1, 2), 3, , 0` | 64 | `1, 2, 3, 0, 0, 0, 0` | +| `Step(1, 2), 3, 4, -0.5, -1.5, 2.5` | 451 | `1, 2, 3, 4, -0.5, -1.5, 2.5` | + +## PSet + +**PSet** never sets flag 4. + +| Statement | Flags | Arguments after *Flags* | +|-----------|------:|-------------------------| +| `(1, 2)` | 0 | `1, 2, 0` | +| `(1, 2), 5` | 2 | `1, 2, 5` | +| `Step(1, 2)` | 1 | `1, 2, 0` | +| `Step(1, 2), 5` | 3 | `1, 2, 5` | + +## Scale + +**Scale** takes either two points or nothing. **Step** is not accepted: `o.Scale Step(1, 2)-(3, 4)` is error TB5079. + +| Statement | Flags | Arguments after *Flags* | +|-----------|------:|-------------------------| +| `(1, 2)-(3, 4)` | 4 | `1, 2, 3, 4` | +| *(no arguments)* | 0 | `0, 0, 0, 0` | + +## Any object can take the syntax + +The compiler reads the syntax from the method name alone: **Line**, **Circle**, **PSet** or **Scale**. It does not look at the type of the object. All of these use it: + +- a method of a class declared in the project, called through a variable of that class; +- a variable declared **As Object** or **As Variant**, where the call is late-bound; +- a variable of an **Interface** type; +- the object of a **With** block, `Me.Line`, and a bare `Line` inside the class; +- a **Public Sub** in a module, called bare or as `ModuleName.Line`. + +A method of any other name gets ordinary syntax errors: `o.Foo (1, 2)-(3, 4)` does not compile. + +The method must accept the whole argument list: six arguments for **Line**, eight for **Circle**, four for **PSet** and five for **Scale**. A method with fewer parameters is error TB5103, *Too many arguments*. The parameter types and **ByRef** or **ByVal** are not checked against a fixed signature; the usual conversions apply. A method with extra trailing **Optional** parameters is accepted, and they take their defaults. + +> [!IMPORTANT] +> The default value of an **Optional** parameter is not used for a part that was left out. The compiler passes `0`, so a parameter declared `Optional ByVal Color As Long = vbBlack` receives `0` whether or not *Color* was written. Test *Flags* to find out what was written. + +The plain call forms are not rewritten. `o.Line 99, 1, 2, 3, 4, 5` and `Call o.Line(99, 1, 2, 3, 4, 5)` pass their arguments through unchanged. A trailing comma is accepted after the second point of **Line** and after the point of **PSet**, but not after the radius of **Circle**, where it is error TB5075. + +> [!NOTE] +> VB6 reserves some of these names. It accepts a **Public Sub Line** in a module and a qualified call such as `Module1.Line (1, 2)-(3, 4), 5, B`, and passes the same flags as above to it. It refuses a bare `Line (1, 2)-(3, 4)` in a module, with the error *Method not valid without suitable object*, and it does not accept a procedure named **Circle**, **PSet** or **Scale** at all. In twinBASIC a procedure may have any of the four names, and a bare call in a module reaches the module's procedure. + +## What the built-in surfaces do with the flags + +The six drawing classes of the VB package implement these methods. [**Printer**](../Packages/VB/Printer/) declares its own version, with a *Flags* parameter of type **Long**, and passes each call on to the same implementation; the other five inherit one shared declaration. They act on the flags like this: + +- With flag 2 clear, the line, outline or point is drawn in [**ForeColor**](../Packages/VB/Form/#forecolor). +- **Line** without flag 4 starts at (**CurrentX**, **CurrentY**). **Step** on the first point adds those values to it. **Step** on the second point is relative to the first point, or to (**CurrentX**, **CurrentY**) when there is no first point. Afterwards (**CurrentX**, **CurrentY**) is the end point. +- **B** draws a rectangle outline using [**FillColor**](../Packages/VB/Form/#fillcolor) and [**FillStyle**](../Packages/VB/Form/#fillstyle) for the interior. **BF** draws a solid rectangle in the line colour. +- **PSet** with **Step** adds (**CurrentX**, **CurrentY**) to the point. Afterwards (**CurrentX**, **CurrentY**) is the point. +- **Circle** with **Step** adds (**CurrentX**, **CurrentY**) to the centre, and afterwards (**CurrentX**, **CurrentY**) is the centre. Without *Start* and *End* it draws a full ellipse. A negative *Start* or *End* also draws the radius line to that end of the arc, which makes a pie. A *Start* or *End* above 2 times pi raises error 5. With flag 256 clear, *Aspect* is ignored. + +For example, after `Form1.PSet (100, 100)` followed by `Form1.PSet Step(3, 4)`, the position is (103, 104). A call through an **As Object** variable draws in the same way. + +The full description of each method on each class is in that class's own section: [**Line**](../Packages/VB/Form/#line), [**Circle**](../Packages/VB/Form/#circle), [**PSet**](../Packages/VB/Form/#pset) and [**Scale**](../Packages/VB/Form/#scale) on **Form**, and the same sections on the other five. + +## Example + +This example is a class that is not a drawing surface. **Plotter** implements **Line**, **Circle** and **PSet** as ordinary methods that print what they receive, so the program shows the *Flags* value and the arguments that each form of the syntax produces. + +```tb check_build projname=graphics-plotter slot=file +Class Plotter + Public Sub Line(ByVal Flags As Long, ByVal X1 As Single, ByVal Y1 As Single, _ + ByVal X2 As Single, ByVal Y2 As Single, ByVal Color As Long) + Debug.Print "Line F=" & Flags & " (" & X1 & "," & Y1 & ")-(" & X2 & "," & Y2 & ") C=" & Color + End Sub + + Public Sub Circle(ByVal Flags As Long, ByVal X As Single, ByVal Y As Single, ByVal Radius As Single, _ + ByVal Color As Long, ByVal Start As Single, ByVal ArcEnd As Single, ByVal Aspect As Single) + Debug.Print "Circle F=" & Flags & " (" & X & "," & Y & ") R=" & Radius & " C=" & Color & _ + " S=" & Start & " E=" & ArcEnd & " A=" & Aspect + End Sub + + Public Sub PSet(ByVal Flags As Long, ByVal X As Single, ByVal Y As Single, ByVal Color As Long) + Debug.Print "PSet F=" & Flags & " (" & X & "," & Y & ") C=" & Color + End Sub +End Class +``` + +```tb check_run projname=graphics-plotter +Dim p As New Plotter + +p.Line (1, 2)-(3, 4) +p.Line (1, 2)-(3, 4), 5, BF +p.Line Step(1, 2)-Step(3, 4), , B +p.Line -(3, 4), 7 +p.Line 99, 1, 2, 3, 4, 5 + +p.Circle (1, 2), 3 +p.Circle Step(1, 2), 3, 4, 0.5, 1.5, 2.5 + +p.PSet (1, 2) +p.PSet Step(1, 2), 5 +' Output: +' Line F=4 (1,2)-(3,4) C=0 +' Line F=38 (1,2)-(3,4) C=5 +' Line F=29 (1,2)-(3,4) C=0 +' Line F=2 (0,0)-(3,4) C=7 +' Line F=99 (1,2)-(3,4) C=5 +' Circle F=0 (1,2) R=3 C=0 S=0 E=0 A=0 +' Circle F=451 (1,2) R=3 C=4 S=0.5 E=1.5 A=2.5 +' PSet F=0 (1,2) C=0 +' PSet F=3 (1,2) C=5 +``` + +## See Also + +- [**Form**](../Packages/VB/Form/#line) class -- the **Line**, **Circle**, **PSet** and **Scale** methods +- [**PictureBox**](../Packages/VB/PictureBox/#line), [**Printer**](../Packages/VB/Printer/#line), [**UserControl**](../Packages/VB/UserControl/#line), [**PropertyPage**](../Packages/VB/PropertyPage/#line) and [**Report**](../Packages/VB/Report/#line) classes -- the other drawing surfaces +- [**Print** statement](Print) -- the other language-level syntax a drawing surface accepts +- [**CurrentX**](../Packages/VB/Form/#currentx), [**CurrentY**](../Packages/VB/Form/#currenty) properties -- the drawing position these methods read and set diff --git a/docs/Reference/Default/VB/Form/index.md b/docs/Reference/Default/VB/Form/index.md index 215d95326..67296743b 100644 --- a/docs/Reference/Default/VB/Form/index.md +++ b/docs/Reference/Default/VB/Form/index.md @@ -510,6 +510,8 @@ Draws a circle, ellipse, or arc on the form using [**ForeColor**](#forecolor) fo Syntax: *object*.**Circle** [ **Step** ] ( *X*, *Y* ), *Radius* [, [ *Color* ] [, [ *Start* ] [, [ *End* ] [, *Aspect* ] ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#circle). + *X*, *Y* : *required* The centre, in [**ScaleMode**](#scalemode) units. **Step** makes the centre relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). @@ -553,6 +555,8 @@ Draws a line, or a rectangle, on the form using [**ForeColor**](#forecolor) (or Syntax: *object*.**Line** [ [ **Step** ] ( *X1*, *Y1* ) ] -[ **Step** ] ( *X2*, *Y2* ) [, [ *Color* ] [, **B** [ **F** ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#line). + *X1*, *Y1* : *optional* The start point, in [**ScaleMode**](#scalemode) units. **Step** makes the point relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). When omitted, drawing begins from the current pen position. @@ -677,6 +681,8 @@ Sets a single pixel on the form to a specified colour. Syntax: *object*.**PSet** [ **Step** ] ( *X*, *Y* ) [, *Color* ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#pset). + *X*, *Y* : *required* The pixel position, in [**ScaleMode**](#scalemode) units. **Step** makes the position relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). @@ -693,10 +699,12 @@ Syntax: *object*.**Refresh** ### Scale {: .no_toc } -Sets the form's logical drawing rectangle in a single call by assigning [**ScaleLeft**](#scaleleft), [**ScaleTop**](#scaletop), [**ScaleWidth**](#scalewidth), and [**ScaleHeight**](#scaleheight). Switches [**ScaleMode**](#scalemode) to **vbUser**. Calling **Scale** with no arguments resets the rectangle to a 1-to-1 mapping with the client area in pixels. +Sets the form's logical drawing rectangle in a single call by assigning [**ScaleLeft**](#scaleleft), [**ScaleTop**](#scaletop), [**ScaleWidth**](#scalewidth), and [**ScaleHeight**](#scaleheight). Switches [**ScaleMode**](#scalemode) to **vbUser**. Calling **Scale** with no arguments resets [**ScaleMode**](#scalemode) to **vbTwips**, with [**ScaleLeft**](#scaleleft) and [**ScaleTop**](#scaletop) at 0. Syntax: *object*.**Scale** [ ( *X1*, *Y1* )-( *X2*, *Y2* ) ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#scale). + *X1*, *Y1* : *optional* The logical coordinate at the top-left corner. diff --git a/docs/Reference/Default/VB/PictureBox/index.md b/docs/Reference/Default/VB/PictureBox/index.md index 8dbdd7622..f1a2b93ed 100644 --- a/docs/Reference/Default/VB/PictureBox/index.md +++ b/docs/Reference/Default/VB/PictureBox/index.md @@ -453,6 +453,8 @@ Draws a circle, ellipse, or elliptical arc on the drawing surface. Syntax: *object*.**Circle** [ **Step** ] ( *X*, *Y* ), *Radius* [, *Color* [, *Start* [, *End* [, *Aspect* ] ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#circle). + *X*, *Y* : *required* Coordinates of the centre in [**ScaleMode**](#scalemode) units. **Single**. When prefixed with **Step**, they are interpreted relative to [**CurrentX**](#currentx) / [**CurrentY**](#currenty). @@ -494,6 +496,8 @@ Draws a straight line, a rectangle outline, or a filled rectangle. Syntax: *object*.**Line** [ [ **Step** ] ( *X1*, *Y1* ) ] **-** [ **Step** ] ( *X2*, *Y2* ) [, *Color* [, **B** [**F**] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#line). + *X1*, *Y1* : *optional* Start coordinates. **Single**. If omitted, the line starts at [**CurrentX**](#currentx) / [**CurrentY**](#currenty). **Step** makes them relative to the current pen position. @@ -582,6 +586,8 @@ Sets a single pixel. Syntax: *object*.**PSet** [ **Step** ] ( *X*, *Y* ) [, *Color* ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#pset). + *X*, *Y* : *required* Coordinates in [**ScaleMode**](#scalemode) units. **Step** makes them relative to [**CurrentX**](#currentx) / [**CurrentY**](#currenty). @@ -604,6 +610,8 @@ Defines a user coordinate system for the surface. Calling **Scale** with no argu Syntax: *object*.**Scale** [ ( *X1*, *Y1* ) **-** ( *X2*, *Y2* ) ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#scale). + *X1*, *Y1* : *required* (with the second pair) The coordinate that maps to the top-left corner --- sets [**ScaleLeft**](#scaleleft) and [**ScaleTop**](#scaletop). diff --git a/docs/Reference/Default/VB/Printer/index.md b/docs/Reference/Default/VB/Printer/index.md index 098a1a3a5..84119b959 100644 --- a/docs/Reference/Default/VB/Printer/index.md +++ b/docs/Reference/Default/VB/Printer/index.md @@ -348,6 +348,8 @@ Draws a circle, ellipse, or elliptical arc on the current page. Syntax: *object*.**Circle** [ **Step** ] ( *X*, *Y* ), *Radius* [, *Color* [, *Start* [, *End* [, *Aspect* ] ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#circle). + *X*, *Y* : *required* Coordinates of the centre in [**ScaleMode**](#scalemode) units. **Single**. **Step** makes them relative to [**CurrentX**](#currentx) / [**CurrentY**](#currenty). @@ -386,6 +388,8 @@ Draws a straight line, a rectangle outline, or a filled rectangle. Syntax: *object*.**Line** [ [ **Step** ] ( *X1*, *Y1* ) ] **-** [ **Step** ] ( *X2*, *Y2* ) [, *Color* [, **B** [**F**] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#line). + *X1*, *Y1* : *optional* Start coordinates. **Single**. If omitted, the line starts at [**CurrentX**](#currentx) / [**CurrentY**](#currenty). **Step** makes them relative to the pen. @@ -450,6 +454,8 @@ Sets a single pixel on the current page. Syntax: *object*.**PSet** [ **Step** ] ( *X*, *Y* ) [, *Color* ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#pset). + *X*, *Y* : *required* Coordinates in [**ScaleMode**](#scalemode) units. **Single**. **Step** makes them relative to [**CurrentX**](#currentx) / [**CurrentY**](#currenty). @@ -465,6 +471,8 @@ Defines a user coordinate system for the page. Calling **Scale** with no argumen Syntax: *object*.**Scale** [ ( *X1*, *Y1* ) **-** ( *X2*, *Y2* ) ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#scale). + *X1*, *Y1* : *required* (with the second pair) The coordinate that maps to the top-left corner --- sets [**ScaleLeft**](#scaleleft) and [**ScaleTop**](#scaletop). diff --git a/docs/Reference/Default/VB/PropertyPage/index.md b/docs/Reference/Default/VB/PropertyPage/index.md index 100a2e84d..99f42bb8d 100644 --- a/docs/Reference/Default/VB/PropertyPage/index.md +++ b/docs/Reference/Default/VB/PropertyPage/index.md @@ -390,6 +390,8 @@ Draws a circle, ellipse, or arc on the page using [**ForeColor**](#forecolor) fo Syntax: *object*.**Circle** [ **Step** ] ( *X*, *Y* ), *Radius* [, [ *Color* ] [, [ *Start* ] [, [ *End* ] [, *Aspect* ] ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#circle). + *X*, *Y* : *required* The centre, in [**ScaleMode**](#scalemode) units. **Step** makes the centre relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). @@ -419,6 +421,8 @@ Draws a line, or a rectangle, on the page using [**ForeColor**](#forecolor) (or Syntax: *object*.**Line** [ [ **Step** ] ( *X1*, *Y1* ) ] -[ **Step** ] ( *X2*, *Y2* ) [, [ *Color* ] [, **B** [ **F** ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#line). + *X1*, *Y1* : *optional* The start point, in [**ScaleMode**](#scalemode) units. **Step** makes the point relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). When omitted, drawing begins from the current pen position. @@ -490,6 +494,8 @@ Sets a single pixel on the page to a specified colour. Syntax: *object*.**PSet** [ **Step** ] ( *X*, *Y* ) [, *Color* ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#pset). + *X*, *Y* : *required* The pixel position, in [**ScaleMode**](#scalemode) units. **Step** makes the position relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). @@ -506,10 +512,12 @@ Syntax: *object*.**Refresh** ### Scale {: .no_toc } -Sets the page's logical drawing rectangle in a single call by assigning [**ScaleLeft**](#scaleleft), [**ScaleTop**](#scaletop), [**ScaleWidth**](#scalewidth), and [**ScaleHeight**](#scaleheight). Switches [**ScaleMode**](#scalemode) to **vbUser**. Calling **Scale** with no arguments resets the rectangle to a 1-to-1 mapping with the client area in pixels. +Sets the page's logical drawing rectangle in a single call by assigning [**ScaleLeft**](#scaleleft), [**ScaleTop**](#scaletop), [**ScaleWidth**](#scalewidth), and [**ScaleHeight**](#scaleheight). Switches [**ScaleMode**](#scalemode) to **vbUser**. Calling **Scale** with no arguments resets [**ScaleMode**](#scalemode) to **vbTwips**, with [**ScaleLeft**](#scaleleft) and [**ScaleTop**](#scaletop) at 0. Syntax: *object*.**Scale** [ ( *X1*, *Y1* )-( *X2*, *Y2* ) ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#scale). + *X1*, *Y1* : *optional* The logical coordinate at the top-left corner. diff --git a/docs/Reference/Default/VB/Report/index.md b/docs/Reference/Default/VB/Report/index.md index 1c6c3c965..e1208bb34 100644 --- a/docs/Reference/Default/VB/Report/index.md +++ b/docs/Reference/Default/VB/Report/index.md @@ -552,6 +552,8 @@ Draws a circle, ellipse, or arc on the current drawing surface (see [**hDC**](#h Syntax: *object*.**Circle** [ **Step** ] ( *X*, *Y* ), *Radius* [, [ *Color* ] [, [ *Start* ] [, [ *End* ] [, *Aspect* ] ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#circle). + *X*, *Y* : *required* The centre, in [**ScaleMode**](#scalemode) units. **Step** makes the centre relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). @@ -595,6 +597,8 @@ Draws a line, or a rectangle, on the current drawing surface (see [**hDC**](#hdc Syntax: *object*.**Line** [ [ **Step** ] ( *X1*, *Y1* ) ] -[ **Step** ] ( *X2*, *Y2* ) [, [ *Color* ] [, **B** [ **F** ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#line). + *X1*, *Y1* : *optional* The start point, in [**ScaleMode**](#scalemode) units. When omitted, drawing begins from the current pen position. @@ -686,6 +690,8 @@ Sets a single pixel on the current drawing surface to a specified colour. Syntax: *object*.**PSet** [ **Step** ] ( *X*, *Y* ) [, *Color* ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#pset). + *X*, *Y* : *required* The pixel position, in [**ScaleMode**](#scalemode) units. **Step** makes the position relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). @@ -702,10 +708,12 @@ Syntax: *object*.**Refresh** ### Scale {: .no_toc } -Sets the report window's logical drawing rectangle in a single call by assigning [**ScaleLeft**](#scaleleft), [**ScaleTop**](#scaletop), [**ScaleWidth**](#scalewidth), and [**ScaleHeight**](#scaleheight). Switches [**ScaleMode**](#scalemode) to **vbUser**. Calling **Scale** with no arguments resets the rectangle to a 1-to-1 mapping with the client area in pixels. +Sets the report window's logical drawing rectangle in a single call by assigning [**ScaleLeft**](#scaleleft), [**ScaleTop**](#scaletop), [**ScaleWidth**](#scalewidth), and [**ScaleHeight**](#scaleheight). Switches [**ScaleMode**](#scalemode) to **vbUser**. Calling **Scale** with no arguments resets [**ScaleMode**](#scalemode) to **vbTwips**, with [**ScaleLeft**](#scaleleft) and [**ScaleTop**](#scaletop) at 0. Syntax: *object*.**Scale** [ ( *X1*, *Y1* )-( *X2*, *Y2* ) ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#scale). + *X1*, *Y1* : *optional* The logical coordinate at the top-left corner. diff --git a/docs/Reference/Default/VB/UserControl/index.md b/docs/Reference/Default/VB/UserControl/index.md index 24a08ad1f..556a875e1 100644 --- a/docs/Reference/Default/VB/UserControl/index.md +++ b/docs/Reference/Default/VB/UserControl/index.md @@ -597,6 +597,8 @@ Draws a circle, ellipse, or arc on the control using [**ForeColor**](#forecolor) Syntax: *object*.**Circle** [ **Step** ] ( *X*, *Y* ), *Radius* [, [ *Color* ] [, [ *Start* ] [, [ *End* ] [, *Aspect* ] ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#circle). + *X*, *Y* : *required* The centre, in [**ScaleMode**](#scalemode) units. **Step** makes the centre relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). @@ -639,6 +641,8 @@ Draws a line, or a rectangle, on the control using [**ForeColor**](#forecolor) ( Syntax: *object*.**Line** [ [ **Step** ] ( *X1*, *Y1* ) ] -[ **Step** ] ( *X2*, *Y2* ) [, [ *Color* ] [, **B** [ **F** ] ] ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#line). + *X1*, *Y1* : *optional* The start point, in [**ScaleMode**](#scalemode) units. **Step** makes the point relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). When omitted, drawing begins from the current pen position. @@ -728,6 +732,8 @@ Sets a single pixel on the control to a specified colour. Syntax: *object*.**PSet** [ **Step** ] ( *X*, *Y* ) [, *Color* ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#pset). + *X*, *Y* : *required* The pixel position, in [**ScaleMode**](#scalemode) units. **Step** makes the position relative to ([**CurrentX**](#currentx), [**CurrentY**](#currenty)). @@ -744,10 +750,12 @@ Syntax: *object*.**Refresh** ### Scale {: .no_toc } -Sets the control's logical drawing rectangle in a single call by assigning [**ScaleLeft**](#scaleleft), [**ScaleTop**](#scaletop), [**ScaleWidth**](#scalewidth), and [**ScaleHeight**](#scaleheight). Switches [**ScaleMode**](#scalemode) to **vbUser**. Calling **Scale** with no arguments resets the rectangle to a 1-to-1 mapping with the client area in pixels. +Sets the control's logical drawing rectangle in a single call by assigning [**ScaleLeft**](#scaleleft), [**ScaleTop**](#scaletop), [**ScaleWidth**](#scalewidth), and [**ScaleHeight**](#scaleheight). Switches [**ScaleMode**](#scalemode) to **vbUser**. Calling **Scale** with no arguments resets [**ScaleMode**](#scalemode) to **vbTwips**, with [**ScaleLeft**](#scaleleft) and [**ScaleTop**](#scaletop) at 0. Syntax: *object*.**Scale** [ ( *X1*, *Y1* )-( *X2*, *Y2* ) ] +The compiler turns this syntax into a call with a flags argument; see [Line, Circle, PSet, Scale](../../../Core/Graphics-Methods#scale). + *X1*, *Y1* : *optional* The logical coordinate at the top-left corner. diff --git a/docs/Reference/Glossary.md b/docs/Reference/Glossary.md index eb4fcbc07..de18ea837 100644 --- a/docs/Reference/Glossary.md +++ b/docs/Reference/Glossary.md @@ -334,7 +334,7 @@ A [procedure](#procedure) that must be explicitly called by another procedure. I ## graphics method -A method that operates on an object such as a **Form**, **PictureBox**, or **Printer**, and performs run-time drawing operations such as animation or simulation. Graphics methods include **Circle**, **Cls**, **Line**, **PaintPicture**, **Point**, **Print**, and **PSet**. +A method that operates on an object such as a **Form**, **PictureBox**, or **Printer**, and performs run-time drawing operations such as animation or simulation. Graphics methods include **Circle**, **Cls**, **Line**, **PaintPicture**, **Point**, **Print**, and **PSet**. The drawing syntax of **Circle**, **Line**, **PSet** and **Scale** is described on the [Line, Circle, PSet, Scale](../tB/Core/Graphics-Methods) page. ## host application diff --git a/docs/Reference/Statements.md b/docs/Reference/Statements.md index b7c3b6582..494421500 100644 --- a/docs/Reference/Statements.md +++ b/docs/Reference/Statements.md @@ -72,6 +72,8 @@ These statements are built into the language itself. They are understood by the * [Let](../tB/Core/Let) -- assigns the value of an expression to a variable or property +* [Line, Circle, PSet, Scale](../tB/Core/Graphics-Methods) -- the drawing syntax of these four methods, which the compiler turns into a call with a flags argument + * [Line Input #](../tB/Core/Line-Input) -- reads a single line from an open sequential file into a string variable * [Load](../tB/Core/Load) -- loads an object (typically a form) into memory without showing it From 01b367380474178c80fb1cb27dd6b75e9b44325f Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sun, 4 Oct 2026 00:08:03 +0200 Subject: [PATCH 02/27] BUGS: Clear All Breakpoints undone by a compiler restart; a watch on a UDT fails with a codegen error; evaluating after a break in DoEvents crashes (995, 987); reproducers and ide-test lanes --- BUGS-TO-REPORT.md | 128 ++++++++++++++++++ .../break-in-doevents-evaluate-crash.twinproj | Bin 0 -> 2463 bytes .../repro.json | 4 + .../src/Settings | 61 +++++++++ .../src/Sources/Startup.twin | 12 ++ .../clear-all-breakpoints-restart.twinproj | Bin 0 -> 2556 bytes bugs/clear-all-breakpoints-restart/repro.json | 4 + .../src/Settings | 61 +++++++++ .../src/Sources/Startup.twin | 13 ++ bugs/udt-watch-codegen-error/repro.json | 4 + bugs/udt-watch-codegen-error/src/Settings | 61 +++++++++ .../src/Sources/Startup.twin | 20 +++ .../udt-watch-codegen-error.twinproj | Bin 0 -> 2643 bytes test/ide/break-into.test.mjs | 128 ++++++++++++++++++ test/ide/breakpoints.test.mjs | 124 +++++++++++++++++ test/ide/lanes.mjs | 3 + test/ide/probes/break-into/Settings | 61 +++++++++ .../probes/break-into/Sources/Startup.twin | 12 ++ test/ide/probes/breakpoints/Settings | 61 +++++++++ .../probes/breakpoints/Sources/Startup.twin | 12 ++ test/ide/probes/watches/Settings | 61 +++++++++ test/ide/probes/watches/Sources/Startup.twin | 20 +++ test/ide/watches.test.mjs | 106 +++++++++++++++ 23 files changed, 956 insertions(+) create mode 100644 bugs/break-in-doevents-evaluate-crash/break-in-doevents-evaluate-crash.twinproj create mode 100644 bugs/break-in-doevents-evaluate-crash/repro.json create mode 100644 bugs/break-in-doevents-evaluate-crash/src/Settings create mode 100644 bugs/break-in-doevents-evaluate-crash/src/Sources/Startup.twin create mode 100644 bugs/clear-all-breakpoints-restart/clear-all-breakpoints-restart.twinproj create mode 100644 bugs/clear-all-breakpoints-restart/repro.json create mode 100644 bugs/clear-all-breakpoints-restart/src/Settings create mode 100644 bugs/clear-all-breakpoints-restart/src/Sources/Startup.twin create mode 100644 bugs/udt-watch-codegen-error/repro.json create mode 100644 bugs/udt-watch-codegen-error/src/Settings create mode 100644 bugs/udt-watch-codegen-error/src/Sources/Startup.twin create mode 100644 bugs/udt-watch-codegen-error/udt-watch-codegen-error.twinproj create mode 100644 test/ide/break-into.test.mjs create mode 100644 test/ide/breakpoints.test.mjs create mode 100644 test/ide/probes/break-into/Settings create mode 100644 test/ide/probes/break-into/Sources/Startup.twin create mode 100644 test/ide/probes/breakpoints/Settings create mode 100644 test/ide/probes/breakpoints/Sources/Startup.twin create mode 100644 test/ide/probes/watches/Settings create mode 100644 test/ide/probes/watches/Sources/Startup.twin create mode 100644 test/ide/watches.test.mjs diff --git a/BUGS-TO-REPORT.md b/BUGS-TO-REPORT.md index e90bf4170..3b0582755 100644 --- a/BUGS-TO-REPORT.md +++ b/BUGS-TO-REPORT.md @@ -1245,3 +1245,131 @@ VB6 has no comparison: a project cannot declare an `Interface`, and the interfac +--- + +## Clear All Breakpoints is undone by restarting the compiler, unless the project was saved in between + +**Describe the bug** +Breakpoints removed with **Debug > Clear All Breakpoints** come back when the compiler restarts. After **Restart the compiler** on the toolbar the margin shows them again, as they were just before they were cleared, and the next run stops at them. A breakpoint removed with F9 stays removed, and saving the project between the two keeps the effect of **Clear All Breakpoints**. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `clear-all-breakpoints-restart.twinproj` (attached as `clear-all-breakpoints-restart.zip`) and open `Startup.twin`. `Main` prints three lines: + ``` + Public Sub Main() + Debug.Print "main start" + Debug.Print "the breakpoint's line" ' BREAK + Debug.Print "main end" + End Sub + ``` +2. Put the cursor on the line marked `BREAK` and press F9. A breakpoint appears in the margin. +3. Choose **Debug > Clear All Breakpoints** (Ctrl+Shift+F9). The breakpoint goes. +4. Click **Restart the compiler** on the toolbar, and wait for the compile to end. The breakpoint is back in the margin. +5. Press F5. The run stops at the breakpoint, having printed only `main start`. + +**Expected behavior** +Breakpoints cleared with **Clear All Breakpoints** stay cleared, as a breakpoint removed with F9 does: after the restart the margin shows none, and F5 prints all three lines. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 995 + +**Additional context** +Also on BETA 987, identically. + +What does not reproduce it: removing the breakpoint with F9 instead, which stays removed across the restart; and saving the project (Ctrl+S) after **Clear All Breakpoints** and before the restart, which keeps them cleared. A breakpoint saved with the project survives a restart, as it should. Not tried: whether the IDE's other ways of starting a new compiler, such as switching between win32 and win64, do the same. + +Where it seems to come from, in BETA 995's `ide/main.js`: F9 records each change in `g_SessionFilesystemTransactions`, which the IDE replays into a new compiler and which a save empties; **Clear All Breakpoints** (`tbDebug_BreakpointsClear`) clears the breakpoints in the compiler and records nothing there, so the replay sets them again. + +Severity: breakpoints a person has removed stop the program again after a restart, with nothing to connect the two. + + + +--- + +## A watch on a variable of a user-defined type fails with a codegen error, and the Debug Console reports a linker error at every stop + +**Describe the bug** +While the debugger is stopped, a watch on a variable of a user-defined type cannot be evaluated. **Watches** shows `(compile error: codegen error; check for compilation errors)`, of type `ERROR`, and the Debug Console prints `[LINKER] compilation (codegen) error detected in 'Startup.{temp_procedure}' at line #1`. The linker error comes again at every later stop while the watch exists, and `? p` in the Debug Console fails the same way. **Variables** shows the same variable and its fields without trouble, and a watch on one of its fields works. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `udt-watch-codegen-error.twinproj` (attached as `udt-watch-codegen-error.zip`). `Startup.twin` declares a type, and `Main` passes the line marked `BREAK` twice: + ``` + Private Type Point + X As Long + Y As Long + End Type + + Public Sub Main() + Dim p As Point + Dim i As Long + p.X = 7 + For i = 1 To 2 + Debug.Print "pass " & i ' BREAK + Next + End Sub + ``` +2. Put a breakpoint on the line marked `BREAK` (F9) and press F5. The run stops there, and **Variables** shows `p` as `{user defined type, 8 bytes}`, with `X` and `Y`. +3. Add a watch on `p` (**Debug > Add Watch...**, or the plus sign in **Watches**). The watch shows `(compile error: codegen error; check for compilation errors)`, of type `ERROR`, and the Debug Console prints `[LINKER] compilation (codegen) error detected in 'Startup.{temp_procedure}' at line #1`. +4. Press F5. The run stops at the breakpoint again, and the Debug Console prints the linker error again. +5. Type `? p` in the Debug Console and press Enter. It fails with the same error, and the Debug Console prints the same linker line. + +**Expected behavior** +The watch shows `p` as **Variables** does, `{user defined type, 8 bytes}` opening to its fields, as VBA's Watch window shows a variable of a user-defined type. An expression that cannot be shown, such as `? p`, which has no single value to print, is refused with a message about the expression. A linker error about generated code reads as though the project had failed to build, which it has not. + +**Desktop:** + - OS: Windows 10 Pro 22H2 (build 19045) + - twinBASIC compiler version: BETA 995 + +**Additional context** +Also on BETA 987, identically. + +What does not reproduce it: a watch on a field, `p.X`, which shows `7`, of type `Variant [Long]`; and **Variables**, which shows `p` and its fields. That type suggests a watch is evaluated as a `Variant`, which cannot hold a value of a user-defined type. Seen with a `Private Type` declared in the module; other declarations of the type were not tried. + +Severity: a watch on a structure is unusable, and while it exists every stop adds a linker error to the Debug Console. + + + +--- + +## After Break Into Code stops in DoEvents, evaluating anything ends the program with an access violation + +**Describe the bug** +**Break Into Code** (Ctrl+Break), pressed while a program is in `DoEvents`, stops inside the VB package rather than in the program: the IDE opens the package's `IdleMessageLoopBreak.twin` at `IdleMessageLoopBreakpoint`, which **Call Stack** shows above the program's procedure. Evaluating anything at that stop ends the program with a native access violation: a line in the Debug Console, even `? 1`, or any watch, which the IDE evaluates at every stop. The IDE then stays in break mode, with the yellow arrow on the package's line and **Stop** and the step commands enabled, although the program has ended. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open `break-in-doevents-evaluate-crash.twinproj` (attached as `break-in-doevents-evaluate-crash.zip`). `Main` loops on `DoEvents`: + ``` + Public Sub Main() + Do + DoEvents + Loop + End Sub + ``` +2. Press F5, then Ctrl+Break (**Run > Break**). The IDE opens `IdleMessageLoopBreak.twin` under `Packages/VB/Sources/SUPPORT`, stopped in `IdleMessageLoopBreakpoint`, and **Call Stack** shows `IdleMessageLoopBreakpoint` above `Main`. +3. Type `? 1` in the Debug Console and press Enter. The program ends, and the Debug Console prints: + ``` + (runtime error -2147467259: NATIVE EXCEPTION: ACCESS_VIOLATION) + 0001