Skip to content

Commit 820d6ae

Browse files
committed
Dear ImGui v1.92.0 compatibility fixes
1 parent b2ebc4d commit 820d6ae

11 files changed

Lines changed: 417 additions & 165 deletions

dear_bindings.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Dear Bindings Version v0.13
1+
# Dear Bindings Version v0.14
22
# Generates C-language headers for Dear ImGui
33
# Developed by Ben Carter (e-mail: ben AT shironekolabs.com, github: @ShironekoBen)
44

@@ -185,7 +185,20 @@ def convert_header(
185185
"ImGui::DragBehaviorT",
186186
"ImGui::SliderBehaviorT",
187187
"ImGui::RoundScalarWithFormatT",
188-
"ImGui::CheckboxFlagsT"])
188+
"ImGui::CheckboxFlagsT",
189+
# This is a slightly awkward one - there are two problems here. One is that
190+
# the function stub generator goes back to the original parsed code when
191+
# trying to generate casts and ends up using the un-instantiated template
192+
# parameter T as the target for a cast. That is fixable, but the other issue
193+
# is that it takes a const& to the element to push, and in the sole existing
194+
# usage right now that's a ImFontBaked, which isn't an easy thing for the
195+
# structure marshalling to handle. Really it should probably be fudged to
196+
# take a pointer instead of const& or similar, as passing ImFontBaked by
197+
# value isn't really a sensible thing to be doing in the first place, but
198+
# for the moment I don't think there's actually any use-case for wanting to
199+
# add things to BakedPool from outside of ImGui itself, so I'm going to
200+
# adopt a wait-and-see stance on this for now.
201+
"ImStableVector::push_back"])
189202

190203
mod_remove_functions.apply(dom_root, ["ImGui::GetInputTextState",
191204
"ImGui::DebugNodeInputTextState"])
@@ -306,12 +319,14 @@ def convert_header(
306319
mod_mark_by_value_structs.apply(dom_root, by_value_structs=[
307320
'ImVec1',
308321
'ImVec2',
322+
'ImVec2i',
309323
'ImVec2ih',
310324
'ImVec4',
311325
'ImColor',
312326
'ImStr',
313327
'ImRect',
314-
'ImGuiListClipperRange'
328+
'ImGuiListClipperRange',
329+
'ImTextureRef'
315330
])
316331
mod_mark_internal_members.apply(dom_root)
317332
mod_flatten_class_functions.apply(dom_root)

docs/Changelog.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
--- v0.14
2+
3+
* Fixes for compatibility with Dear ImGui v1.92.0.
4+
* Refactored template support so that ImStableVector<> doesn't cause errors (at present ImStableVector::push_back()
5+
is not supported as there are complications with that function, but I don't think there's actually a use-case for
6+
modifying ImStableVector contents from outside ImGui right now anyway). (#98)
7+
* Marked ImVec2i and ImTextureRef as by-value structure types.
8+
* Parsing support for loose member function bodies.
9+
110
--- v0.13
211

312
* Cosmetic fix to the naming of the PlatformIO thunk helper functions, and made the comment more explicit about why

src/code_dom/element.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,14 @@ def is_descendant_of(self, parent):
290290
return False
291291
return self.parent.is_descendant_of(parent)
292292

293+
# Tests if this element is a descendant of (or the same as) the type given
294+
def is_descendant_of_type(self, element_type):
295+
if isinstance(self, element_type):
296+
return True
297+
if self.parent is None:
298+
return False
299+
return self.parent.is_descendant_of_type(element_type)
300+
293301
# Walk this element and all children, calling a function on them
294302
def walk(self, func):
295303
func(self)

src/code_dom/fielddeclaration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
class DOMFieldDeclaration(code_dom.element.DOMElement):
77
def __init__(self):
88
super().__init__()
9-
self.field_type = None
9+
self.field_type = None # A DOMType giving the type of the field
1010
self.names = []
1111
self.is_static = False
1212
self.is_extern = False

src/code_dom/functiondeclaration.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ def __init__(self):
3535
# (see mod_generate_imstr_helpers for more details)
3636

3737
self.function_name_alignment = 0 # Column to align the function name to (see mod_align_function_names)
38-
self.is_unformatted_helper = False # Set if this is a variant of a function accepting a format string with
38+
self.is_unformatted_helper = False # Set if this is a variant of a function accepting a format string with
3939
# format string forced to '%s' and a single string argument
40+
self.is_loose_function_body = False # Set if this is a "loose" inline function body in the header file
4041

4142
# Parse tokens from the token stream given
4243
@staticmethod
@@ -109,6 +110,30 @@ def parse(context, stream):
109110
return None
110111
dom_element.tokens.append(name_token)
111112

113+
# We potentially actually might have a class name here, so if this is followed by :: then the real name
114+
# comes after it
115+
116+
if stream.get_token_of_type(["COLON"]) is not None:
117+
if stream.get_token_of_type(["COLON"]) is None:
118+
# Function name followed by a single colon isn't valid
119+
stream.rewind(checkpoint)
120+
return None
121+
122+
# At this point we know we have something of the form ClassName::FunctionName
123+
# For the time being, treat that as the actual function name (we are likely just going to throw this
124+
# away as it is guaranteed to be a function body with an actual declaration elsewhere)
125+
126+
actual_name_token = stream.get_token_of_type(["THING"])
127+
if actual_name_token is None:
128+
# ClassName:: with no actual function name isn't valid
129+
stream.rewind(checkpoint)
130+
return None
131+
dom_element.tokens.append(actual_name_token)
132+
133+
dom_element.is_loose_function_body = True
134+
name_prefix = name_token.value + "::" + name_prefix
135+
name_token = actual_name_token
136+
112137
if name_token.value == "operator":
113138
# If we got "operator" then we need to read the real name from the next tokens too
114139
# (tokens because of things like "operator[]" and "operator*=")

src/code_dom/template.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ def parse(context, stream):
7070
# template<> only has a single (non-comment-like) child, so stop here
7171
break
7272

73+
# Now we have parsed all our children, we want to mark any types we contain that are our own template parameters
74+
75+
template_params = dom_element.get_template_parameters()
76+
77+
for type_element in dom_element.list_all_children_of_type(code_dom.DOMType):
78+
for token in type_element.tokens:
79+
for param in template_params:
80+
if token.value == param.name:
81+
# Slightly hacky - set this on the token itself
82+
token.is_template_parameter = True
83+
7384
return dom_element
7485

7586
# Get the class/function this template is for
@@ -79,6 +90,33 @@ def get_templated_object(self):
7990
return child
8091
return None
8192

93+
class TemplateParameter:
94+
def __init__(self):
95+
super().__init__()
96+
self.is_typename = False # Is this a typename parameter?
97+
self.name = None # The name of this parameter
98+
99+
# Figure out the template parameters via some dubious parsing
100+
# Returns a list of DOMTemplate.TemplateParameter objects
101+
def get_template_parameters(self):
102+
template_parameters = []
103+
next_parameter_is_typename = False
104+
for token in self.template_parameter_tokens:
105+
if token.value == 'typename':
106+
next_parameter_is_typename = True
107+
elif token.value == 'int':
108+
next_parameter_is_typename = False
109+
elif token.type == 'COMMA':
110+
# Just ignore
111+
pass
112+
else:
113+
# This must be a template parameter name
114+
param = DOMTemplate.TemplateParameter()
115+
param.is_typename = next_parameter_is_typename
116+
param.name = token.value
117+
template_parameters.append(param)
118+
return template_parameters
119+
82120
# Write this element out as C code
83121
def write_to_c(self, file, indent=0, context=WriteContext()):
84122
self.write_preceding_comments(file, indent, context)

src/code_dom/type.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,21 @@ def is_constexpr(self):
111111
return True
112112
return False
113113

114+
# Returns true if this type is a pointer
115+
# Only considers the "outermost" type
116+
def is_pointer(self):
117+
if len(self.tokens) > 0:
118+
if self.tokens[-1].type == 'ASTERISK':
119+
return True
120+
return False
121+
122+
# Return true if there are any (unresolved) template parameters in this type
123+
def contains_template_parameters(self):
124+
for tok in self.tokens:
125+
if hasattr(tok, 'is_template_parameter') and tok.is_template_parameter:
126+
return True
127+
return False
128+
114129
# Gets the "primary" type name involved (i.e. without any prefixes or suffixes)
115130
# This is mostly useful for trying to construct overload disambiguation suffixes
116131
def get_primary_type_name(self):

src/generators/gen_struct_converters.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from src.code_dom.common import write_c_line, WriteContext
55

66

7-
# Recursively generate code to copy all the members of a struct and any contained by-valuestructs
8-
def generate_field_copies(file, indent, known_by_value_structs, struct, prefix):
7+
# Recursively generate code to copy all the members of a struct and any contained by-value structs
8+
def generate_field_copies(file, indent, known_by_value_structs, struct, prefix, to_cpp):
99
# Emit code to copy each member
1010
for field in struct.list_directly_contained_children_of_type(code_dom.DOMFieldDeclaration):
1111
if field.field_type.to_c_string() in known_by_value_structs:
@@ -15,10 +15,23 @@ def generate_field_copies(file, indent, known_by_value_structs, struct, prefix):
1515
indent,
1616
known_by_value_structs,
1717
known_by_value_structs[field.field_type.to_c_string()],
18-
prefix + name + ".")
18+
prefix + name + ".",
19+
to_cpp)
1920
else:
2021
for name in field.names:
21-
write_c_line(file, indent, WriteContext(), "dest." + prefix + name + " = src." + prefix + name + ";")
22+
if field.field_type.is_pointer():
23+
# Pointer-type fields need casting
24+
if to_cpp:
25+
cast_type = "::" + field.field_type.to_c_string()
26+
else:
27+
cast_type = "cimgui::" + field.field_type.to_c_string()
28+
29+
write_c_line(file, indent, WriteContext(),
30+
"dest." + prefix + name + " = reinterpret_cast<" + cast_type + ">(src." +
31+
prefix + name + ");")
32+
else:
33+
write_c_line(file, indent, WriteContext(), "dest." + prefix + name + " = src." + prefix + name +
34+
";")
2235

2336

2437
# Generate code to convert by-value types to/from their CPP version
@@ -55,7 +68,7 @@ def generate(dom_root, file, indent=0):
5568
write_c_line(file, indent, write_context, dest_type + " dest;")
5669

5770
# Emit code to copy each member
58-
generate_field_copies(file, indent, known_by_value_structs, struct, "")
71+
generate_field_copies(file, indent, known_by_value_structs, struct, "", to_cpp)
5972

6073
write_c_line(file, indent, write_context, "return dest;")
6174
indent -= 1

0 commit comments

Comments
 (0)