Hello everyone 👋, hope everyone is having a good day.
Hopefully I am allowed to ask technical questions here, if not
please tell me and I will remove this.
I am trying to create object oriented wrappers around
`bindbc.sfml`, this is because I don't like the C-style syntax of
CSFML.
The C-style syntax is not right -- in my opinion -- for an object
oriented language. Dealing with pointers all the time is also
unsafe.
This is not to say that CSFML isn't good -- it's great, and I've
made some apps using `bindbc-sfml`. I just want to extend it to
my liking with object oriented wrappers that can more closely
match the C++ SFML syntax.
For the wrappers, I created a `Shape` class. This `Shape` class
is seen in the original C++ SFML implementation:
```D
class Shape : Transformable, Drawable {
void setTexture(sfTexture* texture, bool resetRect) {
ptr.sfShape_setTexture(texture, resetRect);
}
void setTextureRect(IntRect rect) {
ptr.sfShape_setTextureRect(rect.to_sfIntRect());
}
void setFillColor(Color color) {
ptr.sfShape_setFillColor(color.to_sfColor());
}
void setOutlineColor(Color color) {
ptr.sfShape_setOutlineColor(color.to_sfColor());
}
void setOutlineThickness(float thickness) {
ptr.sfShape_setOutlineThickness(thickness);
}
const(sfTexture)* getTexture() {
return ptr.sfShape_getTexture();
}
IntRect getTextureRect() {
return ptr.sfShape_getTextureRect().toIntRect();
}
Color getFillColor() {
return ptr.sfShape_getFillColor().toColor();
}
Color getOutlineColor() {
return ptr.sfShape_getOutlineColor().toColor();
}
float getOutlineThickness() {
return ptr.sfShape_getOutlineThickness();
}
size_t getPointCount() nothrow {
return ptr.sfShape_getPointCount();
}
Vector2f getPoint(size_t index) nothrow {
return ptr.sfShape_getPoint(index).toVector2f_noThrow();
}
FloatRect getLocalBounds() {
return ptr.sfShape_getLocalBounds().toFloatRect();
}
FloatRect getGlobalBounds() {
return ptr.sfShape_getGlobalBounds().toFloatRect();
}
private sfShape* ptr;
}
```
The `sfShape` pointer isn't currently initialized, I'll get to
that issue soon.
As you can see, `Shape` extends the `Transformable` class and the
`Drawable` interface. This again roughly matches what's seen in
SFML. SFML.NET also did a similar wrapper for their CSFML C#
bindings. What's great about SFML.NET is that you don't even know
that you're using CSFML, this is because it feels just like C++
SFML.
Now, I will create a `RectangleShape` which will be a subclass of
the `Shape` class:
(Btw I took a lot of inspiration from SFML.NET when it comes to
these wrappers.)
```D
class RectangleShape : Shape {
this(Vector2f size) {
_size = size;
setSize(_size);
}
Vector2f getSize() {
return _size;
}
void setSize(Vector2f size) {
_size = size;
}
override {
size_t getPointCount() {
return 4;
}
Vector2f getPoint(size_t index) {
final switch (index) {
case 0:
return Vector2f(0, 0);
case 1:
return Vector2f(_size.x, 0);
case 2:
return Vector2f(_size.x, _size.y);
case 3:
return Vector2f(0, _size.y);
}
}
}
private Vector2f _size;
}
```
As you can see, the `Rectangle` class only overrides the
`getPointCount` and `getPoint` methods.
**These are the methods that the superclass - `Shape` - will use
to construct the shape object for it to actually be drawable.**
Now, let us add the following code to the `Shape` class so that
we can construct a `Shape` via these two methods, which we assume
that the child provides us a good implementation for:
```D
class Shape : Transformable, Drawable {
this() {
ptr = sfShape_create(&getPointCount, &getPoint,
cast(void*)this);
}
extern(C) private static ulong getPointCount(void* data)
nothrow {
return (cast(Shape)data).getPointCount();
}
extern(C) private static sfVector2f getPoint(size_t index,
void* data) nothrow {
return
(cast(Shape)data).getPoint(index).to_sfVector2f_noThrow();
}
```
I hear you asking, what's going on here?
We are providing two callbacks to the `getPointCount` and
`getPoint` methods via function pointers, and we're passing in
the current object to the `data` `void*` pointer. It's kind of
hard to understand, but if you read through it carefully you
should get a rough idea of what's going on.
Now, when we create a new instance of `Rectangle`, I will assume
that the constructor will be called, the `sf_shape` ptr will be
initialized correctly (as it will be utilizing the crucial
`getPoint` and `getPointCount` met