Add new `CanvasItem` snapping option

This commit is contained in:
Adam Scott 2024-07-23 15:16:39 -04:00
parent 8e36f98ea5
commit 2b9942a044
No known key found for this signature in database
GPG Key ID: F6BA2A0302E21A77
8 changed files with 120 additions and 1 deletions

View File

@ -604,6 +604,9 @@
<member name="show_behind_parent" type="bool" setter="set_draw_behind_parent" getter="is_draw_behind_parent_enabled" default="false"> <member name="show_behind_parent" type="bool" setter="set_draw_behind_parent" getter="is_draw_behind_parent_enabled" default="false">
If [code]true[/code], the object draws behind its parent. If [code]true[/code], the object draws behind its parent.
</member> </member>
<member name="snap_to_pixel" type="int" setter="set_snap_to_pixel" getter="get_snap_to_pixel" enum="CanvasItem.SnapToPixel" default="0">
The snapping to pixel mode to use on this [CanvasItem].
</member>
<member name="texture_filter" type="int" setter="set_texture_filter" getter="get_texture_filter" enum="CanvasItem.TextureFilter" default="0"> <member name="texture_filter" type="int" setter="set_texture_filter" getter="get_texture_filter" enum="CanvasItem.TextureFilter" default="0">
The texture filtering mode to use on this [CanvasItem]. The texture filtering mode to use on this [CanvasItem].
</member> </member>
@ -736,5 +739,17 @@
<constant name="CLIP_CHILDREN_MAX" value="3" enum="ClipChildrenMode"> <constant name="CLIP_CHILDREN_MAX" value="3" enum="ClipChildrenMode">
Represents the size of the [enum ClipChildrenMode] enum. Represents the size of the [enum ClipChildrenMode] enum.
</constant> </constant>
<constant name="SNAP_TO_PIXEL_PARENT_NODE" value="0" enum="SnapToPixel">
The [CanvasItem] will inherit the snapping mode from its parent.
</constant>
<constant name="SNAP_TO_PIXEL_DISABLED" value="1" enum="SnapToPixel">
The [CanvasItem] will not snap.
</constant>
<constant name="SNAP_TO_PIXEL_ENABLED" value="2" enum="SnapToPixel">
The [CanvasItem] will snap.
</constant>
<constant name="SNAP_TO_PIXEL_MAX" value="3" enum="SnapToPixel">
Represents the size of the [enum SnapToPixel] enum.
</constant>
</constants> </constants>
</class> </class>

View File

@ -328,6 +328,7 @@ void CanvasItem::_notification(int p_what) {
_update_texture_filter_changed(false); _update_texture_filter_changed(false);
_update_texture_repeat_changed(false); _update_texture_repeat_changed(false);
_update_snap_to_pixel_changed(false);
if (!block_transform_notify && !xform_change.in_list()) { if (!block_transform_notify && !xform_change.in_list()) {
get_tree()->xform_change_list.add(&xform_change); get_tree()->xform_change_list.add(&xform_change);
@ -1270,6 +1271,9 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode); ClassDB::bind_method(D_METHOD("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode);
ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode); ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode);
ClassDB::bind_method(D_METHOD("set_snap_to_pixel", "snap"), &CanvasItem::set_snap_to_pixel);
ClassDB::bind_method(D_METHOD("get_snap_to_pixel"), &CanvasItem::get_snap_to_pixel);
GDVIRTUAL_BIND(_draw); GDVIRTUAL_BIND(_draw);
ADD_GROUP("Visibility", ""); ADD_GROUP("Visibility", "");
@ -1294,6 +1298,10 @@ void CanvasItem::_bind_methods() {
ADD_GROUP("Material", ""); ADD_GROUP("Material", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial"), "set_material", "get_material");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material");
ADD_GROUP("Snap", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_to_pixel", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled"), "set_snap_to_pixel", "get_snap_to_pixel");
// ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled"); // ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),"set_transform_notify","is_transform_notify_enabled");
ADD_SIGNAL(MethodInfo("draw")); ADD_SIGNAL(MethodInfo("draw"));
@ -1328,6 +1336,11 @@ void CanvasItem::_bind_methods() {
BIND_ENUM_CONSTANT(CLIP_CHILDREN_ONLY); BIND_ENUM_CONSTANT(CLIP_CHILDREN_ONLY);
BIND_ENUM_CONSTANT(CLIP_CHILDREN_AND_DRAW); BIND_ENUM_CONSTANT(CLIP_CHILDREN_AND_DRAW);
BIND_ENUM_CONSTANT(CLIP_CHILDREN_MAX); BIND_ENUM_CONSTANT(CLIP_CHILDREN_MAX);
BIND_ENUM_CONSTANT(SNAP_TO_PIXEL_PARENT_NODE);
BIND_ENUM_CONSTANT(SNAP_TO_PIXEL_DISABLED);
BIND_ENUM_CONSTANT(SNAP_TO_PIXEL_ENABLED);
BIND_ENUM_CONSTANT(SNAP_TO_PIXEL_MAX);
} }
Transform2D CanvasItem::get_canvas_transform() const { Transform2D CanvasItem::get_canvas_transform() const {
@ -1565,6 +1578,62 @@ CanvasItem::TextureRepeat CanvasItem::get_texture_repeat_in_tree() const {
return (TextureRepeat)texture_repeat_cache; return (TextureRepeat)texture_repeat_cache;
} }
void CanvasItem::_refresh_snap_to_pixel_cache() const {
if (!is_inside_tree()) {
return;
}
if (snap_to_pixel == SNAP_TO_PIXEL_PARENT_NODE) {
CanvasItem *parent_item = get_parent_item();
if (parent_item) {
snap_to_pixel_cache = parent_item->snap_to_pixel_cache;
} else {
snap_to_pixel_cache = RS::CANVAS_ITEM_SNAP_TO_PIXEL_DEFAULT;
}
} else {
snap_to_pixel_cache = RS::CanvasItemSnapToPixel(snap_to_pixel);
}
}
void CanvasItem::_update_self_snap_to_pixel(RS::CanvasItemSnapToPixel p_snap) {
RS::get_singleton()->canvas_item_set_snap_to_pixel(get_canvas_item(), p_snap);
queue_redraw();
}
void CanvasItem::_update_snap_to_pixel_changed(bool p_propagate) {
if (!is_inside_tree()) {
return;
}
_refresh_snap_to_pixel_cache();
_update_self_snap_to_pixel(snap_to_pixel_cache);
if (p_propagate) {
for (CanvasItem *E : children_items) {
if (!E->top_level && E->snap_to_pixel == SNAP_TO_PIXEL_PARENT_NODE) {
E->_update_snap_to_pixel_changed(true);
}
}
}
}
void CanvasItem::set_snap_to_pixel(SnapToPixel p_snap_to_pixel) {
ERR_THREAD_GUARD;
ERR_FAIL_COND(p_snap_to_pixel >= SNAP_TO_PIXEL_MAX);
if (snap_to_pixel == p_snap_to_pixel) {
return;
}
snap_to_pixel = p_snap_to_pixel;
_update_snap_to_pixel_changed(true);
notify_property_list_changed();
}
CanvasItem::SnapToPixel CanvasItem::get_snap_to_pixel() const {
ERR_READ_THREAD_GUARD_V(SNAP_TO_PIXEL_DISABLED);
return snap_to_pixel;
}
CanvasItem::CanvasItem() : CanvasItem::CanvasItem() :
xform_change(this) { xform_change(this) {
canvas_item = RenderingServer::get_singleton()->canvas_item_create(); canvas_item = RenderingServer::get_singleton()->canvas_item_create();

View File

@ -73,6 +73,13 @@ public:
CLIP_CHILDREN_MAX, CLIP_CHILDREN_MAX,
}; };
enum SnapToPixel {
SNAP_TO_PIXEL_PARENT_NODE,
SNAP_TO_PIXEL_DISABLED,
SNAP_TO_PIXEL_ENABLED,
SNAP_TO_PIXEL_MAX,
};
private: private:
mutable SelfList<Node> mutable SelfList<Node>
xform_change; xform_change;
@ -112,8 +119,10 @@ private:
mutable RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; mutable RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
mutable RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; mutable RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
mutable RS::CanvasItemSnapToPixel snap_to_pixel_cache = RS::CANVAS_ITEM_SNAP_TO_PIXEL_DISABLED;
TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE; TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE;
TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE; TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE;
SnapToPixel snap_to_pixel = SNAP_TO_PIXEL_PARENT_NODE;
Ref<Material> material; Ref<Material> material;
@ -148,12 +157,15 @@ private:
void _update_texture_repeat_changed(bool p_propagate); void _update_texture_repeat_changed(bool p_propagate);
void _refresh_texture_filter_cache() const; void _refresh_texture_filter_cache() const;
void _update_texture_filter_changed(bool p_propagate); void _update_texture_filter_changed(bool p_propagate);
void _refresh_snap_to_pixel_cache() const;
void _update_snap_to_pixel_changed(bool p_propagate);
void _notify_transform_deferred(); void _notify_transform_deferred();
protected: protected:
virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat); virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat);
virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter); virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter);
virtual void _update_self_snap_to_pixel(RS::CanvasItemSnapToPixel p_snap);
_FORCE_INLINE_ void _notify_transform() { _FORCE_INLINE_ void _notify_transform() {
_notify_transform(this); _notify_transform(this);
@ -380,6 +392,9 @@ public:
int get_canvas_layer() const; int get_canvas_layer() const;
CanvasLayer *get_canvas_layer_node() const; CanvasLayer *get_canvas_layer_node() const;
virtual void set_snap_to_pixel(SnapToPixel p_snap_to_pixel);
SnapToPixel get_snap_to_pixel() const;
CanvasItem(); CanvasItem();
~CanvasItem(); ~CanvasItem();
}; };
@ -387,6 +402,7 @@ public:
VARIANT_ENUM_CAST(CanvasItem::TextureFilter) VARIANT_ENUM_CAST(CanvasItem::TextureFilter)
VARIANT_ENUM_CAST(CanvasItem::TextureRepeat) VARIANT_ENUM_CAST(CanvasItem::TextureRepeat)
VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode) VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode)
VARIANT_ENUM_CAST(CanvasItem::SnapToPixel)
class CanvasTexture : public Texture2D { class CanvasTexture : public Texture2D {
GDCLASS(CanvasTexture, Texture2D); GDCLASS(CanvasTexture, Texture2D);

View File

@ -283,7 +283,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
rect.position -= repeat_size / scale * (repeat_times / 2); rect.position -= repeat_size / scale * (repeat_times / 2);
} }
if (snapping_2d_transforms_to_pixel) { if ((p_canvas_item->snap_to_pixel == RS::CANVAS_ITEM_SNAP_TO_PIXEL_DEFAULT && snapping_2d_transforms_to_pixel) || (p_canvas_item->snap_to_pixel == RS::CANVAS_ITEM_SNAP_TO_PIXEL_ENABLED)) {
final_xform.columns[2] = (final_xform.columns[2] + Point2(0.5, 0.5)).floor(); final_xform.columns[2] = (final_xform.columns[2] + Point2(0.5, 0.5)).floor();
parent_xform.columns[2] = (parent_xform.columns[2] + Point2(0.5, 0.5)).floor(); parent_xform.columns[2] = (parent_xform.columns[2] + Point2(0.5, 0.5)).floor();
} }
@ -2311,6 +2311,12 @@ void RendererCanvasCull::canvas_item_set_default_texture_repeat(RID p_item, RS::
ci->texture_repeat = p_repeat; ci->texture_repeat = p_repeat;
} }
void RendererCanvasCull::canvas_item_set_snap_to_pixel(RID p_item, RS::CanvasItemSnapToPixel p_snap) {
Item *ci = canvas_item_owner.get_or_null(p_item);
ERR_FAIL_NULL(ci);
ci->snap_to_pixel = p_snap;
}
void RendererCanvasCull::update_visibility_notifiers() { void RendererCanvasCull::update_visibility_notifiers() {
SelfList<Item::VisibilityNotifierData> *E = visibility_notifier_list.first(); SelfList<Item::VisibilityNotifierData> *E = visibility_notifier_list.first();
while (E) { while (E) {

View File

@ -339,6 +339,7 @@ public:
void canvas_item_set_default_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter); void canvas_item_set_default_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter);
void canvas_item_set_default_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat); void canvas_item_set_default_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat);
void canvas_item_set_snap_to_pixel(RID p_item, RS::CanvasItemSnapToPixel p_snap);
void update_visibility_notifiers(); void update_visibility_notifiers();

View File

@ -458,6 +458,7 @@ public:
RS::CanvasItemTextureFilter texture_filter; RS::CanvasItemTextureFilter texture_filter;
RS::CanvasItemTextureRepeat texture_repeat; RS::CanvasItemTextureRepeat texture_repeat;
RS::CanvasItemSnapToPixel snap_to_pixel;
Item() { Item() {
commands = nullptr; commands = nullptr;

View File

@ -883,6 +883,8 @@ public:
FUNC2(canvas_item_set_draw_behind_parent, RID, bool) FUNC2(canvas_item_set_draw_behind_parent, RID, bool)
FUNC2(canvas_item_set_snap_to_pixel, RID, CanvasItemSnapToPixel)
FUNC6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool) FUNC6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool)
FUNC5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool) FUNC5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool)
FUNC5(canvas_item_add_multiline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool) FUNC5(canvas_item_add_multiline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool)

View File

@ -860,6 +860,13 @@ public:
CANVAS_ITEM_TEXTURE_REPEAT_MAX, CANVAS_ITEM_TEXTURE_REPEAT_MAX,
}; };
enum CanvasItemSnapToPixel {
CANVAS_ITEM_SNAP_TO_PIXEL_DEFAULT,
CANVAS_ITEM_SNAP_TO_PIXEL_DISABLED,
CANVAS_ITEM_SNAP_TO_PIXEL_ENABLED,
CANVAS_ITEM_SNAP_TO_PIXEL_MAX,
};
virtual RID viewport_create() = 0; virtual RID viewport_create() = 0;
enum ViewportScaling3DMode { enum ViewportScaling3DMode {
@ -1451,6 +1458,8 @@ public:
virtual void canvas_item_set_draw_behind_parent(RID p_item, bool p_enable) = 0; virtual void canvas_item_set_draw_behind_parent(RID p_item, bool p_enable) = 0;
virtual void canvas_item_set_snap_to_pixel(RID p_item, CanvasItemSnapToPixel p_snap) = 0;
enum NinePatchAxisMode { enum NinePatchAxisMode {
NINE_PATCH_STRETCH, NINE_PATCH_STRETCH,
NINE_PATCH_TILE, NINE_PATCH_TILE,