Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
_site
.sass-cache
.jekyll-cache
.jekyll-metadata
vendor
25 changes: 25 additions & 0 deletions docs/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
permalink: /404.html
layout: page
---

<style type="text/css" media="screen">
.container {
margin: 10px auto;
max-width: 600px;
text-align: center;
}
h1 {
margin: 30px 0;
font-size: 4em;
line-height: 1;
letter-spacing: -1px;
}
</style>

<div class="container">
<h1>404</h1>

<p><strong>Page not found :(</strong></p>
<p>The requested page could not be found.</p>
</div>
91 changes: 91 additions & 0 deletions docs/CustomControls/Defining a CustomControl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
title: Defining a CustomControl
parent: CustomControls
nav_order: 2
permalink: /CustomControls/Defining/
---
# Defining a CustomControl
A CustomControl is simply an ordinary twinBASIC class, with a few extra attributes and requirements.

{: .tip}
> It is highly advisable to look at and experiment with the sample project provided with twinBASIC before trying to implement your own CustomControl.

![Custom Control Sample Project](Images/ccSampleProject.png)

***
## CustomControl() attribute
![CustomControl attribute](Images/ccCustomControlAttribute.png)

This is a required attribute for all CustomControls. You must provide the relative path to an image file within your project that can be used to identify your control in the form designer toolbox. We recommend that you put the image file in the Miscellaneous folder in your project.

![CustomControl GridImage Folder](Images/ccGridButtonImage.png)

***
## ClassId() attribute
![CustomControl ClassId Attribute](Images/ccClassIdAttribute.png)

This is a required attribute for all CustomControls. You must provide a unique CLSID (GUID) in order for the form engine to work with your control.

{: .tip }
> If you enter `[ ClassId () ]` twinBASIC helps you out - just press the 'insert a randomly generated GUID' text:

![CustomControl ClassId auto-generate](Images/ccClassIdInsert.png)

***
## COMCreatable() attribute
![CustomControl COMCreatable attribute](Images/ccCOMCreatable.png)

This is an optional attribute, but it is usually advisable to set this attribute to False, as you don't need to instantiate CustomControls from external COM environments.

***
## Must implement ICustomControl
![CustomControl ICustomControl interface](Images/ccICustomControl.png)

All CustomControls *must* implement CustomControls.ICustomControl. This interface currently has 3 methods that you must implement:

``` vb
Sub Initialize(ByVal Context As CustomControlContext)
```

This method is called when your control is attached to a form. You must store the provided Context object in a class field as it offers a `Repaint()` method for informing the form engine that something in your control has changed and needs to be repainted.

``` vb
Sub Destroy()
```

This method is called when your control is detached from a form. This allows an opportunity to break circular references so that your object instance can be destructed properly. The implementation for this can often be left empty provided you don't create circular references in objects.

``` vb
Sub Paint(ByVal Canvas As Canvas)
```

This is the most interesting part for a CustomControl. As such, it gets its own section, see [Painting / drawing to your control](../Painting/)

***
## Minimum set of properties
As twinBASIC doesn't yet support inheritance, you must expose a set of common properties (class fields) for all CustomControls:

``` vb
Public Name As String
Public Left As CustomControls.PixelCount
Public Top As CustomControls.PixelCount
Public Width As CustomControls.PixelCount
Public Height As CustomControls.PixelCount
Public Anchors As Anchors = New Anchors
Public Dock As CustomControls.DockMode
Public Visible As Boolean
```

The form designer and the form engine work with these properties, so it is important to include them in your CustomControl class.

Note that the form designer works with pixel values which are not DPI-scaled. So the Left/Top/Width/Height properties of your control do not reflect DPI scaling. For example, if your control has a width of 50 pixels, then at DPI 150%, then the actual drawing width is 75 pixels ( see [Painting / drawing to your control](CustomControls/Painting/)).

***
## Must have a serialization constructor
CustomControls *must* offer a serialization constructor:

``` vb
Public Sub New(Serializer As SerializationInfo)
```

The passed in Serializer object offers a `Deserialize()` method that you call in order to load the properties that have been set for your control via the form designer. See [Property Sheet and Object Serialization](CustomControls/Properties/) for further information.
Binary file added docs/CustomControls/Images/ccEvents.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/CustomControls/Images/ccMyFieldArray.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/CustomControls/Images/ccMyFieldClass.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/CustomControls/Images/ccMyFieldJson1a.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions docs/CustomControls/Introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
title: Introduction
parent: CustomControls
nav_order: 1
permalink: /CustomControls/Introduction/
---

### Introduction

twinBASIC now offers experimental support for CustomControls. CustomControls are implemented using the BASIC language, allowing implementers to design controls directly from the twinBASIC environment.

A few highlights;
- completely custom drawn controls, with no external or third-party dependencies (tiny footprint)
- support 32-bit RGBA for full alpha-transparency
- support high-DPI modes (per-monitor), requiring little thought whilst designing new controls
- full debugging support via the usual twinBASIC integrated debugger
- designed for efficiency so that supporting complex controls with hundreds of elements (e.g. a DataGrid with 100's of cells) is easily possible
- designed for flexibility, allowing for curved corners, multiple borders, background gradients and much more
- the form engine supports anchoring and docking without any considerations needed for CustomControl implementers
- simple property sheet synchronization via the built-in form designer
10 changes: 10 additions & 0 deletions docs/CustomControls/Notes about the form designer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: Notes About the Form Designer
parent: CustomControls
nav_order: 5
permalink: /CustomControls/Notes/
---

# Notes About the Form Designer

For the painting of controls in the form designer, CustomControl instances are instantiated and then release immediately after painting has finished.
99 changes: 99 additions & 0 deletions docs/CustomControls/Painting-drawing to your control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: Painting / Drawing to Your Control
parent: CustomControls
nav_order: 4
permalink: /CustomControls/Painting/
---

# Painting / Drawing to Your Control

### The ICustomControl.Paint method
This is by far the most important method of a CustomControl. It tells the form engine exactly how you want it to render your control.

{: .tip }
> It is highly advisable to look at and experiment with the sample project provided with twinBASIC before trying to implement your own CustomControl.

``` vb
Private Sub OnPaint(ByVal Canvas As CustomControls.Canvas) _
Implements ICustomControl.Paint
```

You are passed a Canvas object that offers the following methods:

```
Canvas.Width As Long [Property-Get]
Canvas.Height As Long [Property-Get]
Canvas.Dpi As Long [Property-Get]
Canvas.DpiScaleFactor As Double [Property-Get]
Canvas.AddElement(Descriptor As ElementDescriptor)
```

`Canvas.Width` and `Canvas.Height` are the absolute pixel sizes that your control is drawing to. Unlike your controls Width/Height properties that are not DPI-scaled, the `Canvas.Width` and `Canvas.Height` values **are** DPI-scaled.

The `Canvas.Dpi` property represents the DPI setting in Windows. If no DPI scaling is in effect, this value is 96. For example, if you have scaling set at 150% on your monitor, then the `Canvas.Dpi` property will be 144.

The `Canvas.DpiScaleFactor` property gives a floating point value representing the DPI scaling percentage. A value of 1 indicates no scaling. For example, if you have scaling set at 150% on your monitor, then the `Canvas.DpiScaleFactor` property will be 1.5.

The `Canvas.AddElement` method is used for adding elements to your control. An *element* is considered to be something that the form-engine will render for you. For example, you might have a grid control that displays 100 cells at a time. Each of those cells would be an *element*. Elements can overlap each over (allowing for opacity/transparency). The form engine draws them in the order that you call AddElement, meaning that the last element added will have the highest z-order.

***
### AddElement(ElementDescriptor)
The AddElement method takes a single argument; an ElementDescriptor. ElementDescriptor is a UDT that defines exactly how the element will be drawn and how it reacts to events like mouse clicks.

``` vb
Public Type ElementDescriptor
OnClick As LongPtr ' event function callback pointer
OnDblClick As LongPtr ' event function callback pointer
OnMouseDown As LongPtr ' event function callback pointer
OnMouseUp As LongPtr ' event function callback pointer
OnMouseEnter As LongPtr ' event function callback pointer
OnMouseLeave As LongPtr ' event function callback pointer
OnMouseMove As LongPtr ' event function callback pointer
OnScrollH As LongPtr ' event function callback pointer
OnScrollV As LongPtr ' event function callback pointer
Left As Long ' pixel offset (control relative, DPI scaled)
Top As Long ' pixel offset (control relative, DPI scaled)
Width As Long ' pixel width (DPI scaled)
Height As Long ' pixel width (DPI scaled)
Cursor As MousePointerConstants ' cursor/pointer icon
TrackingIdX As LongLong ' for tracking this element, passed to events
TrackingIdY As LongLong ' for tracking this element, passed to events
Text As String ' the text to render
TextRenderingOptions As TextRendering ' options to customize text rendering (object)
BackgroundFill As Fill ' options to customize back fill rendering (object)
Corners As Corners ' options to customize corner rendering (object)
Borders As Borders ' options to customize border rendering (object)
End Type
```

***
### Tips
- Each time your OnPaint method is called, you start with a blank canvas.

- Left/Top/Width/Height can legitimately be outside of the canvas area. For example, negative Left/Top, or a Width/Height past the Canvas.Width/Canvas.Height has no ill-effects. The form engine will clip everything appropriately for you, allowing for much simpler designing of your control.

- You should put thought into making the Paint routine efficient. Try not to instantiate COM objects, and when drawing multiple similar elements, try to re-use ElementDescriptors by setting up common properties outside of loops (see WaynesGrid for examples of this)

- TrackingIdX and TrackingIdY are important when you have multiple elements within a control. The two values, when combined, should uniquely represent the element, and must be maintained if your Paint routine is called again. This is needed for supporting events. For example, in a grid control, each cell would have a TrackingIdX / TrackingIdY value associated with it, given the X/Y co-ordinates of the cell.

- Currently, only mouse events are provided, but focus events are coming soon, as well as keyboard events.

- You can use class-based event handlers by simply using the `AddressOf MyEvent` which is now possible to use even on class members. You can see this used frequently in the samples, such as WaynesGrid. All mouse events have the following format:

``` vb
Class MyCustomControl
'...
Private Sub MyClickEvent(ByRef EventInfo As MouseEvent)
MsgBox "You clicked me!"
End Sub

Private Sub OnPaint(ByVal Canvas As CustomControls.Canvas) _
Implements ICustomControl.Paint
Dim MyDescriptor As ElementDescriptor
MyDescriptor.OnClick = AddressOf MyClickEvent
End Sub
```

EventInfo (MouseEvent) provides mouse information such as the relative X/Y position of the mouse, plus the TrackingX/Y values discussed earlier.

- When you call Canvas.AddElement, your element goes into a render pipeline. It is **not** immediately painted to the screen. The render pipeline is compared to the previous render pipeline that was provided by you in the last OnPaint call, and the tB form engine will only redraw areas of the control that have changed. This allows for efficient painting of controls whilst not needing to be concerned about the finer details of how to do partial repainting.
82 changes: 82 additions & 0 deletions docs/CustomControls/Property sheet and object serialization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: Property Sheet and Object Serialization
parent: CustomControls
nav_order: 3
permalink: /CustomControls/Properties/
---
# Property Sheet and Object Serialization
The form designer property sheet will pickup any **_public_** custom properties (fields) that you expose via your CustomControl class. For example, adding a field `Public MyField As Long` will then automatically show up in the control property sheet in the form designer:

![CustomControl MyField propertySheet](Images/ccMyFieldPropertySheet1a.png)

This is then persisted to your project as properties inside your form JSON structure:

![CustomControl MyField JSON](Images/ccMyFieldJson1a.png)

The key to making this work is your serialization constructor, which might look something like this:

``` vb
Public Sub New(Serializer As SerializationInfo)
If Not Serializer.Deserialize(Me) Then
InitializeDefaultValues ' you implement this
End If
End Sub
```

If `Deserialize(Me)` returns `True`, then your class properties were synchronized with the properties set via the form designer. If it returns `False` then the control has just been added to the form, and this gives you an opportunity to setup any suitable default values for your custom public properties. The form designer notices default values you set within the serialization constructor, so that your property sheet is kept in-sync.

***
## Default Values
An alternative method for setting up default values is to inline them into the class field definition:

![CustomControl MyField = 42](Images/ccMyFieldPropertySheet1b.png)

The `Deserialize(Me)` call inside your serialization constructor will overwrite the property value if the control is being synchronized from the persisted property sheet data.

***
## Enumerations
Enumerations that you define in your twinBASIC project are supported. Simply expose a class field with the enumeration:

![CustomControl enumeration property sheet example](Images/ccMyEnumFieldPropertySheet.png)

Note: Enumerations are persisted to the form JSON structure as strings, so bare this in mind when making changes/updates to a CustomControl so that you don't introduce breaking changes by renaming an enumeration value.

***
## Objects
Class objects that you define in your twinBASIC project are supported. You ***must*** supply a ClassId attribute for any exposed object, so that the serialization can identify it.

![CustomControl class property sheet example](Images/ccMyFieldClass.png)

***
## Arrays
Arrays are supported. The form designer allows for adding new elements, removing elements, and re-ordering of elements (via drag/drop).

![CustomControl array property sheet example](Images/ccMyFieldArray.png)

***
## Property Get / Let
Custom property procedures are supported. You will find that using Property Get / Let procedures is required if you want property changes to trigger repainting of your control.

![CustomControl custom property example](Images/ccMyFieldCustomProperty.png)

Note that _**private**_ fields and properties do not form part of the serialization, and so will not appear on the property sheet.

***
## Avoid Variants
The serialization does not support Variants or generic Objects. Always use strongly-typed datatypes.

***
## Events
Events that you define in your class will be exposed in the Events property sheet:

![CustomControl attribute](Images/ccEvents.png)

At the moment, the form-designer doesn't yet support code-behind-forms, so this feature is not yet complete.

***
## Tips
- If you make changes to your CustomControl class, such as exposing new properties or changing how a control is drawn, these changes will get reflected immediately to any open form designers. Form designers will show a 'resync' button when you return to them, once pressed the changes will be apparent.

- The serialization happens via JSON when running in the IDE, but via a binary format when running in a compiled DLL/EXE. The `SerializationInfo` object that is passed to your serialization constructor is a different implementation when running in the IDE, but this should be transparent to you as a CustomControl implementer.

- When making changes or updates to a CustomControl always consider backwards compatibility. For example, if you rename an exposed property, the old property values stored via the property sheet won't be deserialized to your new property.
7 changes: 7 additions & 0 deletions docs/CustomControls/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: CustomControls
nav_order: 3
permalink: /CustomControls/
---

# CustomControls
Loading