@tool @icon("res://addons/kenyoni/qr_code/icon.svg") extends TextureRect class_name QRCodeRect const QRCode := preload("res://addons/kenyoni/qr_code/qr_code.gd") const ShiftJIS := preload("res://addons/kenyoni/qr_code/shift_jis.gd") var mode: QRCode.Mode: set = set_mode, get = get_mode var error_correction: QRCode.ErrorCorrection: set = set_error_correction, get = get_error_correction ## Use Extended Channel Interpretation (ECI). var use_eci: bool: set = set_use_eci, get = get_use_eci ## Extended Channel Interpretation (ECI) Value. var eci_value: int: set = set_eci_value, get = get_eci_value var data: Variant = "": set = set_data, get = get_data ## Automatically update the QR Code when a property changes. var auto_update: bool = true: set = set_auto_update ## Use automatically the smallest version possible. var auto_version: bool = true: set = set_auto_version, get = get_auto_version var version: int = 1: set = set_version, get = get_version ## Use automatically the best mask pattern. var auto_mask_pattern: bool = true: set = set_auto_mask_pattern, get = get_auto_mask_pattern ## Used mask pattern. var mask_pattern: int = 0: set = set_mask_pattern, get = get_mask_pattern var light_module_color: Color = Color.WHITE: set = set_light_module_color var dark_module_color: Color = Color.BLACK: set = set_dark_module_color ## Automatically set the module pixel size based on the size. ## Do not use expand mode KEEP_SIZE when using it. ## Turn this off when the QR Code is resized often, as it impacts the performance quite heavily. var auto_module_size: bool = true: set = set_auto_module_size ## Use that many pixel for one module. ## If auto_module_size is set, this value might by only occasionally updated. In this case do not rely on it. var module_size: int = 1: set = set_module_size ## Use that many modules for the quiet zone. A value of 4 is recommended. var quiet_zone_size: int = 4: set = set_quiet_zone_size var _qr: QRCode = QRCode.new() var _update_fn: Callable = self.update var _cached_data: PackedByteArray = PackedByteArray() func set_mode(new_mode: QRCode.Mode) -> void: self._qr.mode = new_mode self.notify_property_list_changed() if self.auto_update: self.update() func get_mode() -> QRCode.Mode: return self._qr.mode func set_error_correction(new_error_correction: QRCode.ErrorCorrection) -> void: self._qr.error_correction = new_error_correction if self.auto_update: self.update() func get_error_correction() -> QRCode.ErrorCorrection: return self._qr.error_correction func set_use_eci(new_use_eci: bool) -> void: self._qr.use_eci = new_use_eci self.notify_property_list_changed() if self.auto_update: self.update() func get_use_eci() -> bool: return self._qr.use_eci func set_eci_value(new_eci_value: int) -> void: if self._qr.eci_value == new_eci_value: return self._qr.eci_value = new_eci_value self.notify_property_list_changed() if self.auto_update: self.update() func get_eci_value() -> int: return self._qr.eci_value func set_data(new_data: Variant) -> void: match self._qr.mode: QRCode.Mode.NUMERIC: self._qr.put_numeric(new_data) QRCode.Mode.ALPHANUMERIC: self._qr.put_alphanumeric(new_data) QRCode.Mode.BYTE: if typeof(new_data) == TYPE_PACKED_BYTE_ARRAY || ! self.use_eci: self._qr.put_byte(new_data) return match self.eci_value: QRCode.ECI.ISO_8859_1: self._qr.put_byte(new_data.to_ascii_buffer()) QRCode.ECI.SHIFT_JIS: self._qr.put_byte(ShiftJIS.to_shift_jis_2004_buffer(new_data)) QRCode.ECI.UTF_8: self._qr.put_byte(new_data.to_utf8_buffer()) QRCode.ECI.UTF_16: self._qr.put_byte(new_data.to_utf16_buffer()) QRCode.ECI.US_ASCII: self._qr.put_byte(new_data.to_ascii_buffer()) _: self._qr.put_byte(new_data) QRCode.Mode.KANJI: self._qr.put_kanji(new_data) if self.auto_update: self.update() func get_data() -> Variant: var input_data: Variant = self._qr.get_input_data() if self.mode == QRCode.Mode.BYTE && self.use_eci: match self.eci_value: QRCode.ECI.ISO_8859_1: return input_data.get_string_from_ascii() QRCode.ECI.SHIFT_JIS: return ShiftJIS.get_string_from_shift_jis_2004(input_data) QRCode.ECI.UTF_8: return input_data.get_string_from_utf8() QRCode.ECI.UTF_16: return input_data.get_string_from_utf16() QRCode.ECI.US_ASCII: return input_data.get_string_from_ascii() return self._qr.get_input_data() func set_auto_update(new_auto_update: bool) -> void: if auto_update == new_auto_update: return auto_update = new_auto_update self.notify_property_list_changed() if auto_update: self.update() func set_auto_version(new_auto_version: bool) -> void: if self._qr.auto_version == new_auto_version: return self._qr.auto_version = new_auto_version self.notify_property_list_changed() if self.auto_update: self.update() func get_auto_version() -> bool: return self._qr.auto_version func set_version(new_version: int) -> void: if self.auto_version || self._qr.version == new_version: return self._qr.version = new_version if self.auto_update: self.update() func get_version() -> int: return self._qr.version func set_auto_mask_pattern(new_auto_mask_pattern: bool) -> void: if self._qr.auto_mask_pattern == new_auto_mask_pattern: return self._qr.auto_mask_pattern = new_auto_mask_pattern self.notify_property_list_changed() if self.auto_update: self.update() func get_auto_mask_pattern() -> bool: return self._qr.auto_mask_pattern func set_mask_pattern(new_mask_pattern: int) -> void: if self.auto_mask_pattern || self._qr.mask_pattern == new_mask_pattern: return self._qr.mask_pattern = new_mask_pattern if self.auto_update: self.update() func get_mask_pattern() -> int: return self._qr.mask_pattern func set_light_module_color(new_light_module_color: Color) -> void: if light_module_color == new_light_module_color: return light_module_color = new_light_module_color if self.auto_update: self._update_texture() func set_dark_module_color(new_dark_module_color: Color) -> void: if dark_module_color == new_dark_module_color: return dark_module_color = new_dark_module_color if self.auto_update: self._update_texture() func set_auto_module_size(new_auto_module_size: bool) -> void: if auto_module_size == new_auto_module_size: return auto_module_size = new_auto_module_size self.notify_property_list_changed() self.update_configuration_warnings() if self.auto_update: self._update_texture() func set_module_size(new_module_size: int) -> void: if module_size == new_module_size: return module_size = new_module_size # if not auto it was set directly if ! self.auto_module_size && self.auto_update: self._update_texture() func set_quiet_zone_size(new_quiet_zone_size: int) -> void: if quiet_zone_size == new_quiet_zone_size: return quiet_zone_size = maxi(0, new_quiet_zone_size) if self.auto_module_size && self.auto_update: self._update_texture() elif self.auto_module_size: self._update_module_size() func _ready() -> void: if self.texture != null && self.auto_update: self.update() func update() -> void: self._cached_data = self._qr.encode() self._update_texture() func _set(property: StringName, value: Variant) -> bool: if property == "expand_mode": self.update_configuration_warnings() return false func _get_property_list() -> Array[Dictionary]: var data_prop: Dictionary = { "name": "data", "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, } match self.mode: QRCode.Mode.NUMERIC: data_prop["type"] = TYPE_STRING QRCode.Mode.ALPHANUMERIC, QRCode.Mode.KANJI: data_prop["type"] = TYPE_STRING data_prop["hint"] = PROPERTY_HINT_MULTILINE_TEXT QRCode.Mode.BYTE: # these encoding is natively supported if self.use_eci && self.eci_value in [QRCode.ECI.ISO_8859_1, QRCode.ECI.SHIFT_JIS, QRCode.ECI.UTF_8, QRCode.ECI.UTF_16, QRCode.ECI.US_ASCII]: data_prop["type"] = TYPE_STRING data_prop["hint"] = PROPERTY_HINT_MULTILINE_TEXT else: data_prop["type"] = TYPE_PACKED_BYTE_ARRAY return [ { "name": "_update_fn", "type": TYPE_CALLABLE, "usage": PROPERTY_USAGE_NONE if self.auto_update else PROPERTY_USAGE_EDITOR, "hint": PROPERTY_HINT_TOOL_BUTTON, "hint_string": "Update" }, { "name": "auto_update", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, }, { "name": "mode", "type": TYPE_INT, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, "hint": PROPERTY_HINT_ENUM, "hint_string": "Numeric:1,Alphanumeric:2,Byte:4,Kanji:8" }, { "name": "error_correction", "type": TYPE_INT, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, "hint": PROPERTY_HINT_ENUM, "hint_string": "Low:1,Medium:0,Quartile:3,High:2" }, { "name": "use_eci", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, }, { "name": "eci_value", "type": TYPE_INT, "usage": (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE) if self.use_eci else (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY), "hint": PROPERTY_HINT_ENUM, "hint_string": "Code Page 437:2,ISO 8859-1:3,ISO 8859-2:4,ISO 8859-3:5,ISO 8859-4:6,ISO 8859-5:7,ISO 8859-6:8,ISO 8859-7:9,ISO 8859-8:10,ISO 8859-9:11,ISO 8859-10:12,ISO 8859-11:13,ISO 8859-12:14,ISO 8859-13:15,ISO 8859-14:16,ISO 8859-15:17,ISO 8859-16:18,Shift JIS:20,Windows 1250:21,Windows 1251:22,Windows 1252:23,Windows 1256:24,UTF-16:25,UTF-8:26,US ASCII:27,BIG 5:28,GB 18030:29,EUC KR:30" }, data_prop, { "name": "auto_version", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, }, { "name": "version", "type": TYPE_INT, "usage": (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY) if self.auto_version else (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE), "hint": PROPERTY_HINT_RANGE, "hint_string": "1,40" }, { "name": "auto_mask_pattern", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, }, { "name": "mask_pattern", "type": TYPE_INT, "usage": (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY) if self.auto_mask_pattern else (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE), "hint": PROPERTY_HINT_RANGE, "hint_string": "0,7" }, { "name": "Appearance", "type": TYPE_NIL, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_GROUP, }, { "name": "light_module_color", "type": TYPE_COLOR, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, }, { "name": "dark_module_color", "type": TYPE_COLOR, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, }, { "name": "auto_module_size", "type": TYPE_BOOL, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, }, { "name": "module_size", "type": TYPE_INT, "usage": (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY) if self.auto_module_size else (PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE), "hint": PROPERTY_HINT_RANGE, "hint_string": "1,1,or_greater" }, { "name": "quiet_zone_size", "type": TYPE_INT, "usage": PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE, "hint_string": "0,1,or_greater", }, ] func _validate_property(property: Dictionary) -> void: if property.name == "texture": property.usage &= ~(PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE) func _property_can_revert(property: StringName) -> bool: return property in ["eci_value", "auto_version", "auto_mask_pattern", "light_module_color", "dark_module_color", "auto_module_size", "quiet_zone_size"] func _property_get_revert(property: StringName) -> Variant: match property: "eci_value": return QRCode.ECI.ISO_8859_1 "auto_version": return true "auto_mask_pattern": return true "light_module_color": return Color.WHITE "dark_module_color": return Color.BLACK "auto_module_size": return true "quiet_zone_size": return 4 _: return null func _get_configuration_warnings() -> PackedStringArray: if self.auto_module_size && self.expand_mode == EXPAND_KEEP_SIZE: return ["Do not use auto module px size AND keep size expand mode."] return [] func _notification(what: int) -> void: match what: NOTIFICATION_RESIZED: if self.auto_module_size && self.auto_update: self._update_texture() elif self.auto_module_size: self._update_module_size() func _update_module_size() -> void: self.module_size = mini(self.size.x, self.size.y) / (self._qr.get_dimension() + 2 * self.quiet_zone_size) func _update_texture() -> void: if self.auto_module_size: self._update_module_size() self.texture = ImageTexture.create_from_image(QRCode.generate_image(self._cached_data, self.module_size, self.light_module_color, self.dark_module_color, self.quiet_zone_size))