From 011b668b2bfbff39b6770b2ded949f8fd26ac994 Mon Sep 17 00:00:00 2001 From: Mighty Mochi Games <84472936+mightymochi@users.noreply.github.com> Date: Mon, 21 Apr 2025 21:19:28 -0700 Subject: [PATCH] Add files via upload --- Penpot2Godot/DesignerFrame.gd | 438 ++++++++++++++ Penpot2Godot/designcontrols.gd | 470 +++++++++++++++ Penpot2Godot/errorTexture.png | Bin 0 -> 13118 bytes Penpot2Godot/penpot_process.gd | 546 ++++++++++++++++++ Penpot2Godot/shader/polygonimage.gdshader | 45 ++ .../shader/vertextApplyShader.gdshader | 82 +++ 6 files changed, 1581 insertions(+) create mode 100644 Penpot2Godot/DesignerFrame.gd create mode 100644 Penpot2Godot/designcontrols.gd create mode 100644 Penpot2Godot/errorTexture.png create mode 100644 Penpot2Godot/penpot_process.gd create mode 100644 Penpot2Godot/shader/polygonimage.gdshader create mode 100644 Penpot2Godot/shader/vertextApplyShader.gdshader diff --git a/Penpot2Godot/DesignerFrame.gd b/Penpot2Godot/DesignerFrame.gd new file mode 100644 index 0000000..63885b2 --- /dev/null +++ b/Penpot2Godot/DesignerFrame.gd @@ -0,0 +1,438 @@ +@tool +class_name DesignerFrame +extends ScrollContainer + +var script_dir:String = get_script().get_path().get_base_dir() +var frameShaderPath:String = script_dir + "/shader/vertextApplyShader.gdshader" +var is_scene_ready:bool = false +var the_name:String +var the_id:String +var isInAutoContainer:bool = false +var disable_size_update:bool = false +@export_category("Frame Controls") +## Select the node that will be the content container for the ScrollContainer. +@export var inner_container: NodePath : set = update_props +## This differs from the default Godot anchors in that changing the anchors does not change the position or size of the frame/node. +@export_enum("Left","Center","Right","Left and Right") var horizontalAnchor: String = "Left" : set = set_anchor_horizontal +## This differs from the default Godot anchors in that changing the anchors does not change the position or size of the frame/node. +@export_enum("Top","Center","Bottom","Top and Bottom") var verticalAnchor: String = "Top" : set = set_anchor_vertical +@export_group("Size") +## Determines how the node will scale when in an auto layout. +@export_enum("FIXED", "FILL", "HUG") var widthSizeMode: String = "FIXED" : set = set_wSizing +## Determines how the node will scale when in an auto layout. +@export_enum("FIXED", "FILL", "HUG") var heightSizeMode: String = "FIXED" : set = set_hSizing +##If you want to manually control dimensions in auto-layouts, you do so by setting minimum size. +@export var minSize:Vector2 = Vector2(1.0,1.0) : set = set_minimum_size +@export var maxSize:Vector2 = Vector2(10000.0,10000.0) : + set(value): + maxSize = value +@export_range(-180.0, 180.0, 0.1) var frameRotation:float : + set(value): + if is_scene_ready: + self.set_rotation_degrees(value) + frameRotation = value +## Centers the rotation/transform pivot point. This will be maintain even when changing the node size. +@export var center_rotation:bool = false : set = center_the_rotation +## Note that in Godot, clipping will not rotate with the frame/node. +@export var clipFrameContents:bool = false : set = set_clipping +@export_enum("None","Horizontal","Vertical","Both") var scrollingMode: String = "None" : set = set_scrolling +@export_group("Style and Padding") +## If you find changing one node is changing others, click this to break the style link. This will duplicate the Stylebox and shader to break it free of others. +@export var breakStyleLinks:bool : set = break_the_style_link +@export_subgroup("Fill Color") +@export var fill_color:Color = Color(0.6,0.6,0.6,1.0): set = set_background_fill +## When placing an image with transparency, this option determines if the fill color appears behind it. +@export var use_solid_fill:bool : set = set_use_solid +@export var fill_gradient:GradientTexture2D: set = set_background_gradient +## If there is a fill image, you can set whether it appears in front of or behind that image. +@export var gradient_behind_image:bool : set = set_gradientBehindImage +@export_subgroup("Fill Image") +@export var fill_texture: Texture : set = set_background_image +##Expands the image when using edge anti-aliasing. +@export_range(0.001, 1.0, 0.001) var edge_fill: float = 0.001 : set = update_edge_tolerance +## Godot will repeat the edge of pixels if there is not transparency and image does not fill the frame. +@export_enum("Fill", "Fit", "Stretch", "Keep Size") var textureSizeMode: String = "Fill" : set = set_image_sizing +@export var flip_x:bool : set = set_image_flipx +@export var flip_y:bool : set = set_image_flipy +@export_range(0.0, 6.0, 0.01) var zoom: float = 1.0 : set = update_fill_zoom +@export var tile_texture:bool : set = update_text_tile +@export var size_stretch:Vector2 = Vector2(1.0,1.0) : set = update_size_stretch +@export var position_offset:Vector2 = Vector2(0.0,0.0) : set = update_position_offset +@export var tint_color:Color = Color(1.0,1.0,1.0,1.0) : set = set_tint_color +@export_subgroup("Border Stroke") +@export var border_line_weight_all:int : set = set_borders +## An array of 4 border weights. Top, Right, Bottom, Left. +@export var border_weights:Array : set = add_border +@export var border_color:Color : set = add_border_color +## Smooths the border line. This can create color artifacts if using fill textures. See Edge Fill. +@export var anti_alias_border:bool = false : set = set_border_alias +@export_enum("inner", "center", "outer") var border_align: String = "inside" : set = set_border_align +@export_subgroup("Corners") +@export var corner_radius_all:int : set = change_all_corners +## An array of 4 corner radius. Top, Right, Bottom, Left. +@export var corner_radius:Array : set = round_the_corners +@export_subgroup("Padding") +@export var padding_all:int : set = padding_override +## An array of 4 paddings. Top, Right, Bottom, Left. +@export var padding:Array : set = update_padding +@export_subgroup("Shadow") +@export var shadow_color:Color = Color(0.0,0.0,0.0,0.5): set = update_shadow_color +@export var shadow_size:int : set = update_shadow_size +@export var shadow_offest:Vector2 = Vector2(0.0,0.0) : set = update_shadow_offest +@export_group("Auto Layout") +## Creates or changes the direction and type of auto layout. This will swap the inner container node with the appropriate Godot control node. +@export_enum("NONE", "VERTICAL", "HORIZONTAL","GRID") var layoutMode: String = "NONE" : set = set_layout_mode +@export_enum("NO_WRAP", "WRAP") var layoutWrap: String = "NO_WRAP" : set = set_layout_wrap +## Changes the child node positioning. This is different from default Godot as it is changing each child's positioning when needed vs just modifying the parent container. +@export_enum("Left", "Center", "Right") var hLayoutAlign: String = "Left" : set = set_h_alignm +## Changes the child node positioning. This is different from default Godot as it is changing each child's positioning when needed vs just modifying the parent container. +@export_enum("Top", "Center", "Bottom") var vLayoutAlign: String = "Top" : set = set_v_alignm +@export var spacing:int = 0 : set = set_gap_space +## In some containers you can change the horizontal and vertical spacing. +@export var secondary_spacing:int = 0 : set = set_vgap_space +## This option will expand the space of children to automatically fit the space. Only works in Vertical and Horizontal NO_WRAP. +@export var autoSpace:bool = false : set = space_override +## The the number of columns in Grid mode. +@export var grid_columns:int = 1 : set = set_grid_col + +var styleBox: StyleBoxFlat + +func _ready(): + self.resized.connect(_on_control_resized) + if !has_theme_stylebox_override("panel"): + var newstylebox = StyleBoxFlat.new() + add_theme_stylebox_override("panel", newstylebox) + styleBox = get_theme_stylebox("panel") + else: + styleBox = get_theme_stylebox("panel").duplicate() + #if Engine.is_editor_hint(): + is_scene_ready = true + +func _validate_property(property : Dictionary) -> void: + if property.name == "textureSizeMode" and fill_texture == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "flip_x" and fill_texture == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "flip_y" and fill_texture == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "zoom" and fill_texture == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "tile_texture" and fill_texture == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "size_stretch" and fill_texture == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "position_offset" and fill_texture == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "tint_color" and fill_texture == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "gradient_behind_image" and fill_gradient == null: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "edge_fill" and anti_alias_border == false: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "layoutMode" and inner_container == NodePath(""): + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "layoutWrap" and inner_container == NodePath(""): + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "hLayoutAlign" and inner_container == NodePath(""): + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "vLayoutAlign" and inner_container == NodePath(""): + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "spacing" and inner_container == NodePath(""): + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "secondary_spacing" and inner_container == NodePath(""): + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "autoSpace" and inner_container == NodePath(""): + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "grid_columns" and (inner_container == NodePath("") || layoutMode != "GRID"): + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "horizontalAnchor" and isInAutoContainer == true: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "verticalAnchor" and isInAutoContainer == true: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "framePosition" and isInAutoContainer == true: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "frameRotation" and isInAutoContainer == true: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "center_rotation" and isInAutoContainer == true: + property.usage |= PROPERTY_USAGE_READ_ONLY + if property.name == "frameSize" and isInAutoContainer == true: + property.usage |= PROPERTY_USAGE_READ_ONLY + +func _on_control_resized(): + UIDesignTools.restrictMaxSize(self,maxSize) + UIDesignTools.centerOnResize(self,center_rotation) + if UIDesignTools.shaderNameMatches(self,frameShaderPath): + self.material.set_shader_parameter("node_size", Vector2(self.size)) + #if self.heightSizeMode == "HUG": + #self.size_flags_vertical = SIZE_SHRINK_CENTER + #self.size.y = 0.0 + #if self.widthSizeMode == "HUG": + #self.size_flags_horizontal = SIZE_SHRINK_CENTER + #self.size.x = 0.0 + + +func break_the_style_link(theValue): + UIDesignTools.break_the_style_link(self,frameShaderPath) + +func update_props(theNode): + inner_container = theNode + notify_property_list_changed() + +func center_the_rotation(theVar): + center_rotation = theVar + UIDesignTools.center_the_rotation_s(self, is_scene_ready, center_rotation) + +func update_shadow_color(theVar): + shadow_color = theVar + if is_scene_ready: + styleBox.set("shadow_color", shadow_color) + add_theme_stylebox_override("panel", styleBox) + +func update_shadow_size(theVar): + shadow_size = theVar + if is_scene_ready: + styleBox.set("shadow_size", shadow_size) + add_theme_stylebox_override("panel", styleBox) + +func update_shadow_offest(theVar): + shadow_offest = theVar + if is_scene_ready: + styleBox.set("shadow_offset", shadow_offest) + add_theme_stylebox_override("panel", styleBox) + +func set_tint_color(theVar): + tint_color = theVar + if fill_texture != null && UIDesignTools.shaderNameMatches(self,frameShaderPath) && is_scene_ready: + self.material.set_shader_parameter("tint_color", tint_color) + +func update_position_offset(theVar): + position_offset = theVar + if fill_texture != null && UIDesignTools.shaderNameMatches(self,frameShaderPath) && is_scene_ready: + self.material.set_shader_parameter("offset", position_offset) + +func update_size_stretch(theVar): + size_stretch = theVar + if fill_texture != null && UIDesignTools.shaderNameMatches(self,frameShaderPath) && is_scene_ready: + self.material.set_shader_parameter("stretch", size_stretch) + +func update_text_tile(theVar): + tile_texture = theVar + if fill_texture != null && UIDesignTools.shaderNameMatches(self,frameShaderPath) && is_scene_ready: + self.material.set_shader_parameter("tile_texture", tile_texture) + +func update_fill_zoom(theVar): + zoom = theVar + if fill_texture != null && UIDesignTools.shaderNameMatches(self,frameShaderPath) && is_scene_ready: + self.material.set_shader_parameter("texture_scale", zoom) + +func set_image_flipx(theVar): + flip_x = theVar + if fill_texture != null && UIDesignTools.shaderNameMatches(self,frameShaderPath) && is_scene_ready: + self.material.set_shader_parameter("flip_x", flip_x) + +func set_image_flipy(theVar): + flip_y = theVar + if fill_texture != null && UIDesignTools.shaderNameMatches(self,frameShaderPath) && is_scene_ready: + self.material.set_shader_parameter("flip_y", flip_y) + +func update_edge_tolerance(newTol): + edge_fill = newTol + if fill_texture != null && UIDesignTools.shaderNameMatches(self,frameShaderPath) && is_scene_ready: + self.material.set_shader_parameter("tolerance", newTol) + +func set_image_sizing(theSizing): + textureSizeMode = theSizing + UIDesignTools.set_image_sizing(self,textureSizeMode,frameShaderPath) + +func set_use_solid(theVar): + use_solid_fill = theVar + if UIDesignTools.shaderNameMatches(self,frameShaderPath): + self.material.set_shader_parameter("use_solid", use_solid_fill) + +func set_gradientBehindImage(theVar): + gradient_behind_image = theVar + if UIDesignTools.shaderNameMatches(self,frameShaderPath): + self.material.set_shader_parameter("gradient_behind", gradient_behind_image) + +func set_background_gradient(_theGradient): + fill_gradient = _theGradient + UIDesignTools.set_background_gradient(self,is_scene_ready,fill_gradient,frameShaderPath) + +func set_background_image(_theTexture): + fill_texture = _theTexture + UIDesignTools.set_background_image(self,is_scene_ready,fill_texture,frameShaderPath) + +func set_wSizing(_theSize): + widthSizeMode = _theSize + UIDesignTools.set_wSizing(self,is_scene_ready,widthSizeMode) + +func set_hSizing(_theSize): + heightSizeMode = _theSize + UIDesignTools.set_hSizing(self,is_scene_ready,heightSizeMode) + +func set_scrolling(_theScroll): + scrollingMode = _theScroll + UIDesignTools.set_scrolling(self,is_scene_ready,scrollingMode) + +func set_max_size(_theSize): + maxSize = _theSize + +func set_minimum_size(_theSize): + minSize = _theSize + if is_scene_ready: + self.custom_minimum_size = minSize + +func set_anchor_horizontal(_theAnchorString): + horizontalAnchor = _theAnchorString + UIDesignTools.set_anchor_horizontal(self,is_scene_ready,horizontalAnchor) + +func set_anchor_vertical(_theAnchorString): + verticalAnchor = _theAnchorString + UIDesignTools.set_anchor_vertical(self,is_scene_ready,verticalAnchor) + +func set_clipping(_theVar): + clipFrameContents = _theVar + self.clip_contents = clipFrameContents + +func set_layout_wrap(_theVar): + layoutWrap = _theVar + if is_scene_ready: + UIDesignTools.change_layout(self,inner_container) + +func set_layout_mode(_theVar): + layoutMode = _theVar + if layoutMode == "NONE": + layoutWrap = "NO_WRAP" + if is_scene_ready: + UIDesignTools.change_layout(self,inner_container) + +func set_h_alignm(_theVar): + hLayoutAlign = _theVar + if is_scene_ready: + UIDesignTools.container_align_adjust(self,inner_container) + UIDesignTools.children_halign_adjust(self,inner_container) + UIDesignTools.update_gap_space(self,inner_container) + + +func set_v_alignm(_theVar): + vLayoutAlign = _theVar + if is_scene_ready: + UIDesignTools.container_align_adjust(self,inner_container) + UIDesignTools.children_valign_adjust(self,inner_container) + UIDesignTools.update_gap_space(self,inner_container) + +func round_the_corners(_theRound): + corner_radius = _theRound + if is_scene_ready: + if corner_radius != []: + styleBox.set("corner_radius_top_left", corner_radius[0]) + styleBox.set("corner_radius_top_right", corner_radius[1]) + styleBox.set("corner_radius_bottom_right", corner_radius[2]) + styleBox.set("corner_radius_bottom_left", corner_radius[3]) + add_theme_stylebox_override("panel", styleBox) + +func change_all_corners(_theRound): + corner_radius_all = _theRound + if is_scene_ready: + corner_radius = [corner_radius_all,corner_radius_all,corner_radius_all,corner_radius_all] + +func set_background_fill(_theFill): + fill_color = _theFill + if is_scene_ready: + if UIDesignTools.shaderNameMatches(self,frameShaderPath): + styleBox.set("bg_color", Color(0.6,0.6,0.6,1.0)) + self.material.set_shader_parameter("new_bg_color", fill_color) + else: + styleBox.set("bg_color", Color(fill_color)) + add_theme_stylebox_override("panel", styleBox) + +func add_border(_theBorder): + border_weights = _theBorder + if is_scene_ready: + if border_weights != []: + styleBox.set("border_width_top", border_weights[0]) + styleBox.set("border_width_right", border_weights[1]) + styleBox.set("border_width_bottom", border_weights[2]) + styleBox.set("border_width_left", border_weights[3]) + add_theme_stylebox_override("panel", styleBox) + update_padding(padding) + +func set_border_align(theVar): + border_align = theVar + if is_scene_ready: + match border_align: + "inner": + styleBox.set("expand_margin_left", 0) + styleBox.set("expand_margin_top", 0) + styleBox.set("expand_margin_right", 0) + styleBox.set("expand_margin_bottom", 0) + "center": + clipFrameContents = false + styleBox.set("expand_margin_left", border_weights[3] / 2) + styleBox.set("expand_margin_top", border_weights[0] / 2) + styleBox.set("expand_margin_right", border_weights[1] / 2) + styleBox.set("expand_margin_bottom", border_weights[2] / 2) + "outer": + clipFrameContents = false + styleBox.set("expand_margin_left", border_weights[3]) + styleBox.set("expand_margin_top", border_weights[0]) + styleBox.set("expand_margin_right", border_weights[1]) + styleBox.set("expand_margin_bottom", border_weights[2]) + add_theme_stylebox_override("panel", styleBox) + notify_property_list_changed() + +func set_border_alias(theVar): + anti_alias_border = theVar + if is_scene_ready: + styleBox.set("anti_aliasing", theVar) + add_theme_stylebox_override("panel", styleBox) + if theVar == false: + edge_fill = 0.001 + notify_property_list_changed() + +func set_borders(_borderSize): + border_line_weight_all = _borderSize + if is_scene_ready: + add_border([border_line_weight_all,border_line_weight_all,border_line_weight_all,border_line_weight_all]) + +func add_border_color(_theBorderColor): + border_color = _theBorderColor + if is_scene_ready: + styleBox.set("border_color", border_color) + add_theme_stylebox_override("panel", styleBox) + +func update_padding(_thePadding): + padding = _thePadding + if is_scene_ready: + if padding == []: + padding = [0,0,0,0] + styleBox.set("content_margin_top", padding[0]) + styleBox.set("content_margin_right", padding[1]) + styleBox.set("content_margin_bottom", padding[2]) + styleBox.set("content_margin_left", padding[3]) + add_theme_stylebox_override("panel", styleBox) + +func padding_override(_thePadding): + padding_all = _thePadding + if is_scene_ready: + padding = [padding_all,padding_all,padding_all,padding_all] + +func set_grid_col(_newCol): + grid_columns = _newCol + if layoutMode == "GRID" && is_scene_ready: + get_node(inner_container).columns = grid_columns + +func set_gap_space(_newGapSpace): + spacing = _newGapSpace + if is_scene_ready: + UIDesignTools.update_gap_space(self,inner_container) + +func set_vgap_space(_newGapSpace): + secondary_spacing = _newGapSpace + if is_scene_ready: + UIDesignTools.update_gap_space(self,inner_container) + +func space_override(_theVar): + autoSpace = _theVar + if is_scene_ready: + UIDesignTools.update_gap_space(self,inner_container) diff --git a/Penpot2Godot/designcontrols.gd b/Penpot2Godot/designcontrols.gd new file mode 100644 index 0000000..a694b29 --- /dev/null +++ b/Penpot2Godot/designcontrols.gd @@ -0,0 +1,470 @@ +extends Control +class_name UIDesignTools + +#var script_dir:String = get_script().get_path().get_base_dir() +#const frameShaderPath:String = "res://shader/vertextApplyShader.gdshader" + +static func center_the_rotation_s(theNode:Control, is_scene_ready:bool, center_rotation:bool)->void: + if is_scene_ready: + var cur_pos = theNode.position + var cur_offset = theNode.pivot_offset + var global_position_before = theNode.global_position + if (center_rotation == true): + theNode.pivot_offset.x = theNode.size.x / 2 + theNode.pivot_offset.y = theNode.size.y / 2 + var new_pivot_offset = theNode.pivot_offset + var local_offset_change = (new_pivot_offset - cur_offset).rotated(theNode.rotation) + theNode.position -= local_offset_change + theNode.global_position = global_position_before + else: + theNode.pivot_offset.x = 0.0 + theNode.pivot_offset.y = 0.0 + var new_pivot_offset = theNode.pivot_offset + var local_offset_change = (new_pivot_offset - cur_offset).rotated(theNode.rotation) + theNode.position += local_offset_change + theNode.global_position = global_position_before + +static func centerOnResize(theNode:Control, center_rotation:bool)->void: + if center_rotation: + theNode.pivot_offset.x = theNode.size.x / 2 + theNode.pivot_offset.y = theNode.size.y / 2 + +static func restrictMaxSize(theNode:Control, maxSize:Vector2)->void: + if theNode.size.x > maxSize.x && maxSize.x != null : + theNode.size.x = maxSize.x + if theNode.size.y > maxSize.y && maxSize.y != null : + theNode.size.y = maxSize.y + +static func shaderNameMatches(theNode:Control,frameShaderPath:String)->bool: + var shader_material = theNode.material + if shader_material and shader_material is ShaderMaterial: + var shader = shader_material.shader + if shader: + var shader_path = shader.resource_path + if shader_path == frameShaderPath: + return true + else: + return false + else: + return false + else: + return false + +static func set_background_gradient(theNode:Control,is_scene_ready:bool,fill_gradient,frameShaderPath:String)->void: + if is_scene_ready: + if fill_gradient == null: + pass + if shaderNameMatches(theNode,frameShaderPath): + theNode.material.set_shader_parameter("use_gradient", false) + if theNode.fill_texture == null: + theNode.material = null + theNode.styleBox.set("bg_color", theNode.fill_color) + theNode.add_theme_stylebox_override("panel", theNode.styleBox) + theNode.use_solid_fill = true + theNode.notify_property_list_changed() + elif !shaderNameMatches(theNode,frameShaderPath): + var imageShader = load(frameShaderPath) + var shader_material = ShaderMaterial.new() + shader_material.shader = imageShader + theNode.material = shader_material + bgGradientActivate(theNode,fill_gradient) + elif shaderNameMatches(theNode,frameShaderPath): + bgGradientActivate(theNode,fill_gradient) + +static func bgGradientActivate(theNode:Control,fill_gradient)->void: + theNode.material.set_shader_parameter("gradient_texture", fill_gradient) + theNode.material.set_shader_parameter("use_gradient", true) + theNode.styleBox.set("bg_color", Color(0.6,0.6,0.6,1.0)) + theNode.material.set_shader_parameter("new_bg_color", theNode.fill_color) + theNode.add_theme_stylebox_override("panel", theNode.styleBox) + theNode.use_solid_fill = false + theNode.notify_property_list_changed() + +static func set_background_image(theNode:Control,is_scene_ready:bool,fill_texture:Texture,frameShaderPath:String)->void: + if is_scene_ready: + if fill_texture == null: + if shaderNameMatches(theNode,frameShaderPath): + theNode.material.set_shader_parameter("use_image", false) + if theNode.fill_gradient == null: + theNode.material = null + theNode.styleBox.set("bg_color", theNode.fill_color) + theNode.add_theme_stylebox_override("panel", theNode.styleBox) + resetTextureDefaults(theNode) + theNode.notify_property_list_changed() + elif !shaderNameMatches(theNode,frameShaderPath): + var imageShader = load(frameShaderPath) + var shader_material = ShaderMaterial.new() + shader_material.shader = imageShader + theNode.material = shader_material + bgImageActivate(theNode,fill_texture) + elif shaderNameMatches(theNode,frameShaderPath): + bgImageActivate(theNode,fill_texture) + +static func bgImageActivate(theNode:Control,fill_texture:Texture)->void: + theNode.material.set_shader_parameter("image_texture", fill_texture) + theNode.material.set_shader_parameter("use_image", true) + theNode.material.set_shader_parameter("node_size", Vector2(theNode.size)) + theNode.material.set_shader_parameter("texture_size", fill_texture.get_size()) + theNode.styleBox.set("bg_color", Color(0.6,0.6,0.6,1.0)) + theNode.material.set_shader_parameter("new_bg_color", theNode.fill_color) + theNode.add_theme_stylebox_override("panel", theNode.styleBox) + theNode.use_solid_fill = theNode.use_solid_fill + theNode.notify_property_list_changed() + +static func resetTextureDefaults(theNode:Control)->void: + theNode.textureSizeMode = "Fill" + theNode.flip_x = false + theNode.flip_y = false + theNode.zoom = 1.0 + theNode.tile_texture = false + theNode.size_stretch = Vector2(1.0,1.0) + theNode.position_offset = Vector2(0.0,0.0) + theNode.tint_color = Color(1.0,1.0,1.0,1.0) + +static func set_image_sizing(theNode:Control,textureSizeMode:String,frameShaderPath:String)->void: + if theNode.fill_texture != null && shaderNameMatches(theNode,frameShaderPath): + match textureSizeMode: + "Fill": + theNode.zoom = 1.0 + theNode.size_stretch = Vector2(1.0,1.0) + theNode.position_offset = Vector2(0.0,0.0) + theNode.material.set_shader_parameter("keep_aspect", true) + theNode.material.set_shader_parameter("fill_rect", true) + theNode.material.set_shader_parameter("manual_scale", false) + "Fit": + theNode.zoom = 1.0 + theNode.size_stretch = Vector2(1.0,1.0) + theNode.position_offset = Vector2(0.0,0.0) + theNode.material.set_shader_parameter("keep_aspect", true) + theNode.material.set_shader_parameter("fill_rect", false) + theNode.material.set_shader_parameter("manual_scale", false) + "Stretch": + theNode.zoom = 1.0 + theNode.size_stretch = Vector2(1.0,1.0) + theNode.position_offset = Vector2(0.0,0.0) + theNode.material.set_shader_parameter("keep_aspect", false) + theNode.material.set_shader_parameter("fill_rect", false) + theNode.material.set_shader_parameter("manual_scale", false) + "Keep Size": + theNode.material.set_shader_parameter("keep_aspect", true) + theNode.material.set_shader_parameter("fill_rect", false) + theNode.material.set_shader_parameter("manual_scale", true) + +static func break_the_style_link(theNode:Control,frameShaderPath:String)->void: + theNode.styleBox = theNode.get_theme_stylebox("panel").duplicate() + if shaderNameMatches(theNode,frameShaderPath): + theNode.material = theNode.material.duplicate() + if theNode.fill_gradient != null: + theNode.fill_gradient = theNode.fill_gradient.duplicate() + +static func set_wSizing(theNode:Control,is_scene_ready:bool,widthSizeMode:String)->void: + if is_scene_ready: + match widthSizeMode: + "HUG", "auto": + theNode.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + if theNode is ScrollContainer: + theNode.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + theNode.minSize.x = 0.0 + theNode.size.x = 0.0 + "FIXED", "fix": + theNode.minSize.x = theNode.size.x + theNode.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + if theNode is ScrollContainer: + set_scrolling(theNode,is_scene_ready,theNode.scrollingMode) + "FILL", "fill": + theNode.size_flags_horizontal = Control.SIZE_EXPAND_FILL + +static func set_hSizing(theNode:Control,is_scene_ready:bool,heightSizeMode:String)->void: + if is_scene_ready: + match heightSizeMode: + "HUG", "auto": + theNode.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + if theNode is ScrollContainer: + theNode.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED + theNode.minSize.y = 0.0 + theNode.size.y = 0.0 + "FIXED", "fix": + theNode.minSize.y = theNode.size.y + theNode.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + if theNode is ScrollContainer: + set_scrolling(theNode,is_scene_ready,theNode.scrollingMode) + "FILL", "fill": + theNode.size_flags_vertical = Control.SIZE_EXPAND_FILL + +static func set_scrolling(theNode:Control,is_scene_ready:bool,scrollingMode:String): + if is_scene_ready: + match scrollingMode: + "None": + if theNode.widthSizeMode != "HUG": + theNode.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER + if theNode.heightSizeMode != "HUG": + theNode.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER + "Horizontal": + if theNode.widthSizeMode != "HUG": + theNode.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO + if theNode.heightSizeMode != "HUG": + theNode.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER + "Vertical": + if theNode.widthSizeMode != "HUG": + theNode.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_SHOW_NEVER + if theNode.heightSizeMode != "HUG": + theNode.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO + "Both": + if theNode.widthSizeMode != "HUG": + theNode.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO + if theNode.heightSizeMode != "HUG": + theNode.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO + +static func set_anchor_horizontal(theNode:Control,is_scene_ready:bool,horizontalAnchor:String): + if is_scene_ready: + var my_width = theNode.size.x + var myHpos = theNode.position.x + match horizontalAnchor: + "Left": + theNode.anchor_left = 0.0 + theNode.anchor_right = 0.0 + theNode.position.x = myHpos + theNode.size.x = my_width + "Center": + theNode.anchor_left = 0.5 + theNode.anchor_right = 0.5 + theNode.position.x = myHpos + theNode.size.x = my_width + "Right": + theNode.anchor_left = 1.0 + theNode.anchor_right = 1.0 + theNode.position.x = myHpos + theNode.size.x = my_width + "Left and Right": + theNode.anchor_left = 0.0 + theNode.anchor_right = 1.0 + theNode.position.x = myHpos + theNode.size.x = my_width + theNode.grow_horizontal = Control.GROW_DIRECTION_BOTH + "Scale": + pass + #need object bounds + +static func set_anchor_vertical(theNode:Control,is_scene_ready:bool,verticalAnchor:String): + if is_scene_ready: + var my_height = theNode.size.y + var myVpos = theNode.position.y + match verticalAnchor: + "Top": + theNode.anchor_top = 0.0 + theNode.anchor_bottom = 0.0 + theNode.position.y = myVpos + theNode.size.y = my_height + "Center": + theNode.anchor_top = 0.5 + theNode.anchor_bottom = 0.5 + theNode.position.y = myVpos + theNode.size.y = my_height + "Bottom": + theNode.anchor_top = 1.0 + theNode.anchor_bottom = 1.0 + theNode.position.y = myVpos + theNode.size.y = my_height + "Top and Bottom": + theNode.anchor_top = 0.0 + theNode.anchor_bottom = 1.0 + theNode.position.y = myVpos + theNode.size.y = my_height + theNode.grow_vertical = Control.GROW_DIRECTION_BOTH + "Scale": + pass + +static func change_layout(theNode:Control,inner_container:NodePath)->void: + if !theNode.is_scene_ready: + return + if theNode.layoutWrap == "NO_WRAP": + match theNode.layoutMode: + "NONE": + var ControlClass = Control.new() + ControlClass.name = theNode.get_node(inner_container).name + ControlClass.size_flags_vertical = Control.SIZE_EXPAND_FILL + ControlClass.size_flags_horizontal = Control.SIZE_EXPAND_FILL + theNode.get_node(inner_container).replace_by(ControlClass, true) + "VERTICAL": + var VBoxClass = VBoxContainer.new() + VBoxClass.name = theNode.get_node(inner_container).name + VBoxClass.size_flags_vertical = Control.SIZE_EXPAND_FILL + VBoxClass.size_flags_horizontal = Control.SIZE_EXPAND_FILL + theNode.get_node(inner_container).replace_by(VBoxClass, true) + "HORIZONTAL": + var HBoxClass = HBoxContainer.new() + HBoxClass.name = theNode.get_node(inner_container).name + HBoxClass.size_flags_vertical = Control.SIZE_EXPAND_FILL + HBoxClass.size_flags_horizontal = Control.SIZE_EXPAND_FILL + theNode.get_node(inner_container).replace_by(HBoxClass, true) + "GRID": + var GBoxClass = GridContainer.new() + GBoxClass.name = theNode.get_node(inner_container).name + GBoxClass.size_flags_vertical = Control.SIZE_EXPAND_FILL + GBoxClass.size_flags_horizontal = Control.SIZE_EXPAND_FILL + theNode.get_node(inner_container).replace_by(GBoxClass, true) + set_grid_col(theNode,theNode.grid_columns,inner_container) + change_child_layouts(theNode,inner_container) + elif theNode.layoutWrap == "WRAP": + match theNode.layoutMode: + "NONE": + theNode.layoutMode = "HORIZONTAL" + "VERTICAL": + var VFlowClass = VFlowContainer.new() + VFlowClass.name = theNode.get_node(inner_container).name + VFlowClass.size_flags_vertical = Control.SIZE_EXPAND_FILL + VFlowClass.size_flags_horizontal = Control.SIZE_EXPAND_FILL + theNode.get_node(inner_container).replace_by(VFlowClass, true) + "HORIZONTAL": + var HFlowClass = HFlowContainer.new() + HFlowClass.name = theNode.get_node(inner_container).name + HFlowClass.size_flags_vertical = Control.SIZE_EXPAND_FILL + HFlowClass.size_flags_horizontal = Control.SIZE_EXPAND_FILL + theNode.get_node(inner_container).replace_by(HFlowClass, true) + "GRID": + var GBoxClass = GridContainer.new() + GBoxClass.name = theNode.get_node(inner_container).name + GBoxClass.size_flags_vertical = Control.SIZE_EXPAND_FILL + GBoxClass.size_flags_horizontal = Control.SIZE_EXPAND_FILL + theNode.get_node(inner_container).replace_by(GBoxClass, true) + set_grid_col(theNode,theNode.grid_columns,inner_container) + container_align_adjust(theNode,inner_container) + update_gap_space(theNode,inner_container) + theNode.notify_property_list_changed() + +static func change_child_layouts(theNode:Control,inner_container:NodePath)->void: + for c_node in theNode.get_node(inner_container).get_children(): + if c_node.get("layoutMode") != null: + pass + #print(layoutMode + " : " + c_node.name) + elif c_node.get("layout_mode") != null: + match theNode.layoutMode: + "NONE": + #c_node.layout_mode = 1 + c_node.size = c_node.custom_minimum_size + c_node.anchor_left = 0.0 + c_node.anchor_right = 0.0 + c_node.anchor_top = 0.0 + c_node.anchor_bottom = 0.0 + "VERTICAL": + c_node.layout_mode = 2 + "HORIZONTAL": + c_node.layout_mode = 2 + "GRID": + c_node.layout_mode = 2 + container_align_adjust(theNode,inner_container) + children_valign_adjust(theNode,inner_container) + children_halign_adjust(theNode,inner_container) + update_gap_space(theNode,inner_container) + +static func set_grid_col(theNode:Control,grid_columns:int,inner_container:NodePath): + if theNode.layoutMode == "GRID": + theNode.get_node(inner_container).columns = grid_columns + +static func update_gap_space(theNode:Control,inner_container:NodePath)->void: + if !theNode.is_scene_ready: + return + if (theNode.layoutWrap == "NO_WRAP" && theNode.layoutMode == "HORIZONTAL") || (theNode.layoutWrap == "NO_WRAP" && theNode.layoutMode == "VERTICAL"): + if theNode.autoSpace: + theNode.get_node(inner_container).add_theme_constant_override("separation", 0) + var the_total:int = theNode.get_node(inner_container).get_child_count() + var the_count:int = 0 + for c_node in theNode.get_node(inner_container).get_children(): + the_count += 1 + if the_count < the_total: + match theNode.layoutMode: + "VERTICAL": + c_node.size_flags_vertical = Control.SIZE_EXPAND + "HORIZONTAL": + c_node.size_flags_horizontal = Control.SIZE_EXPAND + else: + children_valign_adjust(theNode,inner_container) + children_halign_adjust(theNode,inner_container) + theNode.get_node(inner_container).add_theme_constant_override("separation", theNode.spacing) + elif theNode.layoutWrap == "WRAP" || theNode.layoutMode == "GRID": + theNode.get_node(inner_container).add_theme_constant_override("h_separation", theNode.spacing) + theNode.get_node(inner_container).add_theme_constant_override("v_separation", theNode.secondary_spacing) + +static func container_align_adjust(theNode:Control,inner_container:NodePath)->void: + if !theNode.is_scene_ready: + return + var the_container = theNode.get_node(inner_container) + match theNode.layoutMode: + "VERTICAL": + if the_container.is_class("VFlowContainer"): + match theNode.hLayoutAlign: + "Left": + the_container.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN | Control.SIZE_EXPAND + "Center": + the_container.size_flags_horizontal = Control.SIZE_SHRINK_CENTER | Control.SIZE_EXPAND + "Right": + the_container.size_flags_horizontal = Control.SIZE_SHRINK_END | Control.SIZE_EXPAND + match theNode.vLayoutAlign: + "Top": + the_container.alignment = BoxContainer.AlignmentMode.ALIGNMENT_BEGIN + "Center": + the_container.alignment = BoxContainer.AlignmentMode.ALIGNMENT_CENTER + "Bottom": + the_container.alignment = BoxContainer.AlignmentMode.ALIGNMENT_END + "HORIZONTAL": + if the_container.is_class("HFlowContainer"): + match theNode.vLayoutAlign: + "Top": + the_container.size_flags_vertical = Control.SIZE_SHRINK_BEGIN | Control.SIZE_EXPAND + "Center": + the_container.size_flags_vertical = Control.SIZE_SHRINK_CENTER | Control.SIZE_EXPAND + "Bottom": + the_container.size_flags_vertical = Control.SIZE_SHRINK_END | Control.SIZE_EXPAND + match theNode.hLayoutAlign: + "Left": + the_container.alignment = BoxContainer.AlignmentMode.ALIGNMENT_BEGIN + "Center": + the_container.alignment = BoxContainer.AlignmentMode.ALIGNMENT_CENTER + "Right": + the_container.alignment = BoxContainer.AlignmentMode.ALIGNMENT_END + +static func children_halign_adjust(theNode:Control,inner_container:NodePath)->void: + if !theNode.is_scene_ready: + return + for c_node in theNode.get_node(inner_container).get_children(): + if c_node.size_flags_horizontal != Control.SIZE_FILL && c_node.size_flags_horizontal != Control.SIZE_EXPAND_FILL: + match theNode.layoutMode: + "VERTICAL": + match theNode.hLayoutAlign: + "Left": + c_node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + "Center": + c_node.size_flags_horizontal = Control.SIZE_SHRINK_CENTER + "Right": + c_node.size_flags_horizontal = Control.SIZE_SHRINK_END + "HORIZONTAL": + match theNode.vLayoutAlign: + "Top": + c_node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + "Center": + c_node.size_flags_horizontal = Control.SIZE_SHRINK_CENTER + "Bottom": + c_node.size_flags_horizontal = Control.SIZE_SHRINK_END + +static func children_valign_adjust(theNode:Control,inner_container:NodePath)->void: + if !theNode.is_scene_ready: + return + for c_node in theNode.get_node(inner_container).get_children(): + if c_node.size_flags_vertical != Control.SIZE_FILL && c_node.size_flags_vertical != Control.SIZE_EXPAND_FILL: + match theNode.layoutMode: + "VERTICAL": + match theNode.hLayoutAlign: + "Left": + c_node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + "Center": + c_node.size_flags_vertical = Control.SIZE_SHRINK_CENTER + "Right": + c_node.size_flags_vertical = Control.SIZE_SHRINK_END + "HORIZONTAL": + match theNode.vLayoutAlign: + "Top": + c_node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + "Center": + c_node.size_flags_vertical = Control.SIZE_SHRINK_CENTER + "Bottom": + c_node.size_flags_vertical = Control.SIZE_SHRINK_END diff --git a/Penpot2Godot/errorTexture.png b/Penpot2Godot/errorTexture.png new file mode 100644 index 0000000000000000000000000000000000000000..ac325b6115aa5cc9ca261590721d8957c4dfdc3a GIT binary patch literal 13118 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 F{{Tm~DR%$> literal 0 HcmV?d00001 diff --git a/Penpot2Godot/penpot_process.gd b/Penpot2Godot/penpot_process.gd new file mode 100644 index 0000000..1c61e77 --- /dev/null +++ b/Penpot2Godot/penpot_process.gd @@ -0,0 +1,546 @@ +@tool +extends Control + +var script_dir:String = get_script().get_path().get_base_dir() + +@export_global_file("*.penpot") var penpot_file +@export_global_dir var unzip_target +@export_tool_button("Extract Penpot") var unzip_button = extract_all_from_zip +@export_dir var fonts_folder +@export_dir var images_folder : + set(value): + images_folder = value + if images_folder == null || images_folder == "" || images_folder == " ": + autoPlaceImages = false + else: + autoPlaceImages = true + notify_property_list_changed() +## Select or deselect if you want the importer to automatically import images. +@export var autoPlaceImages:bool = false +@export_tool_button("Process Penpot") var processBtn = dir_contents_trigger +@export var SelectPage : String : set = changePageSelect +@export var SelectBoard : String : + set(value): + SelectBoard = value + if SelectBoard != "" && SelectBoard != "Select Board" && tool_ready: + board_id_to_proc = strip_to_id(SelectBoard) + notify_property_list_changed() +@export_tool_button("Import Board") var importBoardBtn = process_board_from_button + +var page_hints:String +var frame_hints:String +var pen_file_id:String +var tool_ready:bool = false +var board_id_to_proc:String +var name_dictionary:Dictionary +var parent_dictionary:Dictionary +var lineage_dictionary:Dictionary +var auto_layout_check:Dictionary +var main_parent_pos:Vector2 = Vector2(0.0,0.0) + +func _ready() -> void: + pass + SelectBoard = "" + SelectPage = "" + tool_ready = true + +func _validate_property(property : Dictionary) -> void: + if property.name == &"SelectPage": + property.hint = PROPERTY_HINT_ENUM + property.hint_string = page_hints + if property.name == &"SelectBoard": + property.hint = PROPERTY_HINT_ENUM + property.hint_string = frame_hints + + +func process_board_from_button(): + process_object(board_id_to_proc,true) + +func process_object(the_board_id,position_override:bool): + pass + var the_page_id = strip_to_id(SelectPage) + var path = unzip_target + "/files/" + pen_file_id + "/pages/" + the_page_id + "/" + the_board_id + ".json" + var data_file = FileAccess.open(path, FileAccess.READ) + var data_parsed = JSON.parse_string(data_file.get_as_text()) + var parent_cycle:String = "" + var parent_lineage:String = "" + #print(data_parsed["name"] + ": " + data_parsed["id"]) + name_dictionary[data_parsed["id"]] = data_parsed["name"] + parent_dictionary[data_parsed["id"]] = data_parsed["parentId"] + parent_cycle = data_parsed["parentId"] + while parent_cycle != "00000000-0000-0000-0000-000000000000": + parent_lineage = parent_cycle + "/InnerContainer/" + parent_lineage + parent_cycle = parent_dictionary[parent_cycle] + if parent_lineage != "": + parent_lineage = parent_lineage.erase(parent_lineage.length()-1,1) + lineage_dictionary[data_parsed["id"]] = parent_lineage + var make_min_size:bool = false + var process_forward:bool = true + if data_parsed.has("layout") && (data_parsed["layout"] == "grid" || data_parsed["layout"] == "flex"): + auto_layout_check[data_parsed["id"]] = true + process_forward = false + if auto_layout_check.has(data_parsed["parentId"]): + make_min_size = auto_layout_check[data_parsed["parentId"]] + match data_parsed["type"]: + "frame", "rect": + render_frame(data_parsed,parent_lineage,position_override,make_min_size) + if data_parsed.has("shapes"): + var shape_array = data_parsed["shapes"] + if shape_array != null && shape_array.size() > 0: + if process_forward: + for the_count in shape_array: + process_object(the_count,false) + else: + var the_count = shape_array.size() + while the_count > 0: + the_count -= 1 + process_object(shape_array[the_count],false) + "text": + render_text_frame(data_parsed,parent_lineage,position_override,make_min_size) + "path": + print("PATH") + +func render_frame(data_parsed,the_parent,position_override:bool,set_min_sizes:bool): + pass + var newFrame + newFrame = DesignerFrame.new() + #newFrame.name = data_parsed["name"]+" xIDx"+ data_parsed["id"]+"x" + newFrame.name = data_parsed["id"] + if the_parent != null && the_parent != "": + var parent = get_node(the_parent) + parent.add_child(newFrame) + else: + add_child(newFrame) + newFrame.set_owner(get_tree().get_edited_scene_root()) + newFrame.use_solid_fill = false + newFrame.scrollingMode = "None" + var newControl = Control.new() + newControl.name = "InnerContainer" + newFrame.add_child(newControl) + newFrame.inner_container = newControl.get_path() + newFrame.get_node(newFrame.inner_container).set_owner(get_tree().get_edited_scene_root()) + newFrame.get_node(newFrame.inner_container).size_flags_vertical = Control.SIZE_EXPAND_FILL + newFrame.get_node(newFrame.inner_container).size_flags_horizontal = Control.SIZE_EXPAND_FILL + newFrame.the_id = data_parsed["id"] + newFrame.set_deferred("center_rotation", true) + if data_parsed.has("layoutItemHSizing"): + newFrame.widthSizeMode = data_parsed["layoutItemHSizing"] + if data_parsed.has("layoutItemVSizing"): + newFrame.heightSizeMode = data_parsed["layoutItemVSizing"] + if set_min_sizes: + newFrame.minSize.x = data_parsed["width"] + newFrame.minSize.y = data_parsed["height"] + newFrame.set_deferred("size", Vector2(data_parsed["width"],data_parsed["height"])) + if position_override: + newFrame.set_deferred("global_position", Vector2(0.0,0.0)) + main_parent_pos = Vector2(data_parsed["x"],data_parsed["y"]) + else: + newFrame.set_deferred("global_position", Vector2(data_parsed["x"] - main_parent_pos.x, data_parsed["y"] - main_parent_pos.y)) + newFrame.set_deferred("rotation_degrees", data_parsed["rotation"] * 1) + if data_parsed.has("constraintsH"): + match data_parsed["constraintsH"]: + "left": + newFrame.set_deferred("horizontalAnchor","Left") + "right": + newFrame.set_deferred("horizontalAnchor","Right") + "leftright": + newFrame.set_deferred("horizontalAnchor","Left and Right") + "center": + newFrame.set_deferred("horizontalAnchor","Center") + "scale": + newFrame.set_deferred("horizontalAnchor","Scale") + if data_parsed.has("constraintsV"): + match data_parsed["constraintsV"]: + "top": + newFrame.set_deferred("verticalAnchor","Top") + "bottom": + newFrame.set_deferred("verticalAnchor","Bottom") + "topbottom": + newFrame.set_deferred("verticalAnchor","Top and Bottom") + "center": + newFrame.set_deferred("verticalAnchor","Center") + "scale": + newFrame.set_deferred("verticalAnchor","Scale") + if data_parsed.has("fills"): + newFrame.fill_color = Color(0,0,0,0) + for cur_fill in data_parsed["fills"]: + if cur_fill.has("fillColor") && cur_fill.has("fillOpacity"): + newFrame.fill_color = Color(Color.html(cur_fill["fillColor"]),cur_fill["fillOpacity"]) + newFrame.use_solid_fill = true + if cur_fill.has("fillImage"): + var image_texture_id = cur_fill["fillImage"]["id"] + var image_json = unzip_target + "/files/" + pen_file_id + "/media/" + image_texture_id +".json" + var the_image_file = find_image_from_json(image_json) + var image_texture = load(unzip_target + "/objects/" + the_image_file) as Texture + newFrame.fill_texture = image_texture + if cur_fill.has("fillColorGradient"): + var gradient = Gradient.new() + for color_stop in cur_fill["fillColorGradient"]["stops"]: + gradient.add_point(color_stop["offset"], Color(Color.html(color_stop["color"]), color_stop["opacity"])) + gradient.remove_point(1) + gradient.remove_point(0) + var gradient_texture = GradientTexture2D.new() + gradient_texture.gradient = gradient + match cur_fill["fillColorGradient"]["type"]: + "linear": + gradient_texture.fill = GradientTexture2D.FILL_LINEAR + "radial": + gradient_texture.fill = GradientTexture2D.FILL_RADIAL + gradient_texture.width = 64 + gradient_texture.height = 64 + gradient_texture.fill_from = Vector2(cur_fill["fillColorGradient"]["startX"],cur_fill["fillColorGradient"]["startY"]) + gradient_texture.fill_to = Vector2(cur_fill["fillColorGradient"]["endX"],cur_fill["fillColorGradient"]["endY"]) + newFrame.fill_gradient = gradient_texture + if data_parsed.has("strokes"): + if data_parsed["strokes"].size() > 0: + newFrame.border_color = Color(Color.html(data_parsed["strokes"][0]["strokeColor"]),data_parsed["strokes"][0]["strokeOpacity"]) + newFrame.border_weights = [data_parsed["strokes"][0]["strokeWidth"],data_parsed["strokes"][0]["strokeWidth"],data_parsed["strokes"][0]["strokeWidth"],data_parsed["strokes"][0]["strokeWidth"]] + newFrame.border_align = data_parsed["strokes"][0]["strokeAlignment"] + newFrame.corner_radius = [data_parsed["r1"],data_parsed["r2"],data_parsed["r3"],data_parsed["r4"]] + if data_parsed.has("layoutPadding"): + newFrame.padding = [data_parsed["layoutPadding"]["p1"],data_parsed["layoutPadding"]["p2"],data_parsed["layoutPadding"]["p3"],data_parsed["layoutPadding"]["p4"]] + if data_parsed.has("layout"): + match data_parsed["layout"]: + "flex": + pass + match data_parsed["layoutFlexDir"]: + "row": + newFrame.layoutMode = "HORIZONTAL" + "column": + newFrame.layoutMode = "VERTICAL" + match data_parsed["layoutWrapType"]: + "nowrap": + newFrame.layoutWrap = "NO_WRAP" + "wrap": + newFrame.layoutWrap = "WRAP" + newFrame.spacing = data_parsed["layoutGap"]["columnGap"] + newFrame.secondary_spacing = data_parsed["layoutGap"]["rowGap"] + match data_parsed["layoutAlignContent"]: + "start": + newFrame.set_deferred("hLayoutAlign", "Left") + "center": + newFrame.set_deferred("hLayoutAlign", "Center") + "end": + newFrame.set_deferred("hLayoutAlign", "Right") + match data_parsed["layoutJustifyContent"]: + "start": + newFrame.set_deferred("vLayoutAlign", "Top") + "end": + newFrame.set_deferred("vLayoutAlign", "Bottom") + "center": + newFrame.set_deferred("vLayoutAlign", "Center") + "grid": + newFrame.layoutMode = "GRID" + newFrame.spacing = data_parsed["layoutGap"]["columnGap"] + newFrame.secondary_spacing = data_parsed["layoutGap"]["rowGap"] + newFrame.grid_columns = data_parsed["layoutGridColumns"].size() + if data_parsed.has("showContent"): + newFrame.clipFrameContents = !data_parsed["showContent"] + else: + newFrame.clipFrameContents = false + +func find_image_from_json(path): + var data_file = FileAccess.open(path, FileAccess.READ) + var data_parsed = JSON.parse_string(data_file.get_as_text()) + var file_type:String + var file_name:String + if data_parsed.has("mtype"): + file_type = String(data_parsed["mtype"]).right(3) + if data_parsed.has("mediaId"): + file_name = data_parsed["mediaId"] + var full_file_name:String = file_name + "." + file_type + return full_file_name + +func render_text_frame(data_parsed,the_parent,position_override:bool,set_min_sizes:bool): + var newFrame = Label.new() + var newLabelSettings = LabelSettings.new() + + if fonts_folder != null and fonts_folder != "": + var dynamic_font = FontFile.new() + var font_name = str(data_parsed["positionData"][0]["fontFamily"]).replace(" ", "") + var font_weight = str(data_parsed["positionData"][0]["fontWeight"]).replace(" ", "") + var font_style = str(data_parsed["positionData"][0]["fontStyle"]).replace(" ", "") + match font_weight: + "100": + font_weight = "Thin" + "200": + font_weight = "ExtraLight" + "300": + font_weight = "Light" + "400": + font_weight = "Regular" + "500": + font_weight = "Medium" + "600": + font_weight = "SemiBold" + "700": + font_weight = "Bold" + "800": + font_weight = "ExtraBold" + "900": + font_weight = "Black" + match font_style: + "italic": + font_style = "Italic" + _: + font_style = "" + var font_style_transformed:String = font_weight + font_style + var font_location:String = fonts_folder + "/" + font_name + "-" + font_style_transformed + ".ttf" + if FileAccess.file_exists(font_location): + newLabelSettings.font = load(font_location) + if data_parsed.has("constraintsH"): + set_anchor_horizontal(data_parsed["constraintsH"],newFrame) + if data_parsed.has("constraintsV"): + set_anchor_vertical(data_parsed["constraintsV"],newFrame) + var font_size_change = data_parsed["positionData"][0]["fontSize"] + font_size_change = font_size_change.substr(0, font_size_change.length() - 2) + newLabelSettings.font_size = int(font_size_change) + if data_parsed.has("content") && data_parsed["content"].has("children") and data_parsed["content"]["children"].size() > 0 && data_parsed["content"]["children"][0].has("children") && data_parsed["content"]["children"][0]["children"].size() > 0 && data_parsed["content"]["children"][0]["children"][0].has("lineHeight"): + newLabelSettings.line_spacing = float(font_size_change) - (float(font_size_change) * float(data_parsed["content"]["children"][0]["children"][0]["lineHeight"])) + else: + newLabelSettings.line_spacing = 0 + if data_parsed["positionData"][0].has("fills"): + newLabelSettings.font_color = Color(Color.html(data_parsed["positionData"][0]["fills"][0]["fillColor"]),data_parsed["positionData"][0]["fills"][0]["fillOpacity"]) + newFrame.name = data_parsed["id"] #xIDx"+ make_safeName(p_id)+"x" + var parent = get_node(the_parent) + parent.add_child(newFrame) + newFrame.set_label_settings(newLabelSettings) + newFrame.set_owner(get_tree().get_edited_scene_root()) + newFrame.set_deferred("size", Vector2(data_parsed["width"],data_parsed["height"])) + newFrame.set_deferred("autowrap_mode", TextServer.AUTOWRAP_WORD_SMART) + #if data_parsed.has("content") && data_parsed["content"].has("children") and data_parsed["content"]["children"].size() > 0 && data_parsed["content"]["children"][0].has("children") && data_parsed["content"]["children"][0]["children"].size() > 0 && data_parsed["content"]["children"][0]["children"][0].has("children") && data_parsed["content"]["children"][0]["children"][0]["children"].size() > 0 && data_parsed["content"]["children"][0]["children"][0]["children"][0].has("text"): + #newFrame.set_deferred("text",data_parsed["content"]["children"][0]["children"][0]["children"][0]["text"]) + var temp_text:String + if data_parsed.has("positionData"): + for pdat in data_parsed["positionData"]: + temp_text += pdat["text"] + newFrame.set_deferred("text",temp_text) + #else: + #newFrame.text = data_parsed["positionData"][0]["text"] + if position_override: + newFrame.set_deferred("global_position", Vector2(0.0,0.0)) + main_parent_pos = Vector2(data_parsed["x"],data_parsed["y"]) + else: + newFrame.set_deferred("global_position", Vector2(data_parsed["x"] - main_parent_pos.x, data_parsed["y"] - main_parent_pos.y)) + newFrame.set_deferred("pivot_offset", Vector2(data_parsed["width"] / 2, data_parsed["height"] / 2)) + newFrame.set_deferred("rotation_degrees", data_parsed["rotation"] * 1) + if data_parsed.has("content") && data_parsed["content"].has("children") and data_parsed["content"]["children"].size() > 0 && data_parsed["content"]["children"][0].has("children") && data_parsed["content"]["children"][0]["children"].size() > 0 && data_parsed["content"]["children"][0]["children"][0].has("textAlign"): + update_textHAlign(data_parsed["content"]["children"][0]["children"][0]["textAlign"],newFrame) + if data_parsed.has("content") && data_parsed["content"].has("verticalAlign"): + update_textVAlign(data_parsed["content"]["verticalAlign"],newFrame) + newFrame.custom_minimum_size = Vector2(data_parsed["width"],data_parsed["height"]) + if data_parsed.has("layoutItemHSizing"): + set_textwSize(data_parsed["layoutItemHSizing"],newFrame,data_parsed["width"]) + if data_parsed.has("layoutItemVSizing"): + set_texthSize(data_parsed["layoutItemVSizing"],newFrame,data_parsed["height"]) + +func update_textHAlign(_theVar,theNode): + match _theVar: + "left": + theNode.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT + "center": + theNode.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + "right": + theNode.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + +func update_textVAlign(_theVar,theNode): + match _theVar: + "top": + theNode.vertical_alignment = VERTICAL_ALIGNMENT_TOP + "center": + theNode.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + "bottom": + theNode.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM + +func set_textwSize(widthSizeMode,theNode,theNodeWidth)->void: + match widthSizeMode: + "auto": + theNode.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + theNode.size.x = 0.0 + "fix": + theNode.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN + theNode.custom_minimum_size.x = theNodeWidth + "fill": + theNode.size_flags_horizontal = Control.SIZE_EXPAND_FILL + +func set_texthSize(heightSizeMode,theNode,theNodeHeight)->void: + match heightSizeMode: + "auto": + theNode.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + theNode.size.y = 0.0 + "fix": + theNode.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + theNode.custom_minimum_size.y = theNodeHeight + "fill": + theNode.size_flags_vertical = Control.SIZE_EXPAND_FILL + +func set_anchor_horizontal(horizontalAnchor:String,theNode)->void: + var my_width = theNode.size.x + var myHpos = theNode.position.x + match horizontalAnchor: + "left": + theNode.anchor_left = 0.0 + theNode.anchor_right = 0.0 + theNode.position.x = myHpos + theNode.size.x = my_width + "center": + theNode.anchor_left = 0.5 + theNode.anchor_right = 0.5 + theNode.position.x = myHpos + theNode.size.x = my_width + "right": + theNode.anchor_left = 1.0 + theNode.anchor_right = 1.0 + theNode.position.x = myHpos + theNode.size.x = my_width + "leftright": + theNode.anchor_left = 0.0 + theNode.anchor_right = 1.0 + theNode.position.x = myHpos + theNode.size.x = my_width + theNode.grow_horizontal = Control.GROW_DIRECTION_BOTH + "scale": + pass + #need object bounds + +func set_anchor_vertical(verticalAnchor:String,theNode)->void: + var my_height = theNode.size.y + var myVpos = theNode.position.y + match verticalAnchor: + "top": + theNode.anchor_top = 0.0 + theNode.anchor_bottom = 0.0 + theNode.position.y = myVpos + theNode.size.y = my_height + "center": + theNode.anchor_top = 0.5 + theNode.anchor_bottom = 0.5 + theNode.position.y = myVpos + theNode.size.y = my_height + "bottom": + theNode.anchor_top = 1.0 + theNode.anchor_bottom = 1.0 + theNode.position.y = myVpos + theNode.size.y = my_height + "topbottom": + theNode.anchor_top = 0.0 + theNode.anchor_bottom = 1.0 + theNode.position.y = myVpos + theNode.size.y = my_height + theNode.grow_vertical = Control.GROW_DIRECTION_BOTH + "scale": + pass + #need object bounds +# Extract all files from a ZIP archive, preserving the directories within. +# This acts like the "Extract all" functionality from most archive managers. +func extract_all_from_zip(): + var reader = ZIPReader.new() + if penpot_file == null: + print("no file") + return + reader.open(penpot_file) + # Destination directory for the extracted files (this folder must exist before extraction). + # Not all ZIP archives put everything in a single root folder, + # which means several files/folders may be created in `root_dir` after extraction! + if unzip_target == null: + print("no extract directory") + return + var root_dir = DirAccess.open(unzip_target) + + var files = reader.get_files() + for file_path in files: + # If the current entry is a directory. + if file_path.ends_with("/"): + root_dir.make_dir_recursive(file_path) + continue + # Write file contents, creating folders automatically when needed. + # Not all ZIP archives are strictly ordered, so we need to do this in case + # the file entry comes before the folder entry. + root_dir.make_dir_recursive(root_dir.get_current_dir().path_join(file_path).get_base_dir()) + var file = FileAccess.open(root_dir.get_current_dir().path_join(file_path), FileAccess.WRITE) + var buffer = reader.read_file(file_path) + file.store_buffer(buffer) + EditorInterface.get_resource_filesystem().scan() + print("Extraction Complete") + +func dir_contents_trigger(): + dir_contents(unzip_target) + +func dir_contents(path): + var dir = DirAccess.open(path) + if dir: + dir.list_dir_begin() + var file_name = dir.get_next() + while file_name != "": + if dir.current_is_dir(): + pass + #print("Found directory: " + file_name) + else: + #print("Found file: " + file_name) + if file_name == "manifest.json": + #Load the file. + var the_file_loc = path + "/" + file_name + var data_file = FileAccess.open(the_file_loc, FileAccess.READ) + #Parse the json file. + var data_parsed = JSON.parse_string(data_file.get_as_text()) + process_manifest(data_parsed) + return + file_name = dir.get_next() + else: + print("An error occurred when trying to access the path.") + +func process_manifest(json_data): + page_hints = "Select Page," + SelectPage = "Select Page" + pen_file_id = json_data["files"][0]["id"] + var path = unzip_target + "/files/" + pen_file_id + "/pages" + var dir = DirAccess.open(path) + if dir: + dir.list_dir_begin() + var file_name = dir.get_next() + while file_name != "": + if dir.current_is_dir(): + pass + #print("Found directory: " + file_name) + else: + if file_name.ends_with(".json"): + var page_json = path + "/" + file_name + get_page_id(page_json) + file_name = dir.get_next() + else: + print("An error occurred when trying to access the path.") + +func get_page_id(json_data): + var data_file = FileAccess.open(json_data, FileAccess.READ) + var data_parsed = JSON.parse_string(data_file.get_as_text()) + page_hints += data_parsed["name"]+" ----- xIDx"+data_parsed["id"]+"x," + notify_property_list_changed() + +func changePageSelect(_stuff): + SelectPage = _stuff + if SelectPage != "Select Page" && SelectPage != "": + var the_page_id = strip_to_id(SelectPage) + frame_hints = "Select Board," + SelectBoard = "Select Board" + if tool_ready: + loadPageShapes(the_page_id) + notify_property_list_changed() + +func loadPageShapes(page_id): + pass + var path = unzip_target + "/files/" + pen_file_id + "/pages/" + page_id + var data_file = FileAccess.open(path + "/00000000-0000-0000-0000-000000000000.json", FileAccess.READ) + var data_parsed = JSON.parse_string(data_file.get_as_text()) + var shape_array:Array = data_parsed["shapes"] + for the_id in shape_array.size(): + var shape_data_file = FileAccess.open(path + "/" + shape_array[the_id] + ".json", FileAccess.READ) + var shape_data_parsed = JSON.parse_string(shape_data_file.get_as_text()) + frame_hints += shape_data_parsed["name"]+" ----- xIDx"+shape_data_parsed["id"]+"x," + +func strip_to_id(input_string:String): + var start_pos = input_string.find("xIDx") + 4 + var end_pos = input_string.rfind("x") + if start_pos < end_pos: + return input_string.substr(start_pos, end_pos - start_pos) + else: + print("no id") diff --git a/Penpot2Godot/shader/polygonimage.gdshader b/Penpot2Godot/shader/polygonimage.gdshader new file mode 100644 index 0000000..d31d4e2 --- /dev/null +++ b/Penpot2Godot/shader/polygonimage.gdshader @@ -0,0 +1,45 @@ +shader_type canvas_item; + +uniform sampler2D image_texture : source_color, filter_linear, repeat_disable; +uniform sampler2D gradient_texture : source_color, filter_linear, repeat_disable; +uniform bool use_gradient = false; +uniform bool use_image = false; +uniform bool use_solid = true; +uniform bool gradient_behind = false; +varying vec4 vertex_color; +uniform vec4 new_bg_color : source_color = vec4(0.6, 0.6, 0.6, 1.0); +uniform vec4 tint_color : source_color = vec4(1.0, 1.0, 1.0, 1.0); +uniform vec2 texture_size; +uniform float texture_scale : hint_range(0.0, 6.0) = 1.0; +uniform vec2 stretch = vec2(1.0); +uniform vec2 offset = vec2(0.0); +uniform bool flip_x = false; +uniform bool flip_y = false; +uniform bool tile_texture = false; + +void fragment() { + float x_dir = flip_x ? -1.0 : 1.0; + float y_dir = flip_y ? -1.0 : 1.0; + vec2 uv = (UV / stretch - offset); + float texture_aspect = texture_size.x / texture_size.y; + uv -= 0.5; + uv *= vec2(x_dir, y_dir); + uv /= texture_scale; + uv += 0.5; + if (tile_texture) { + uv = fract(uv); + } + vec4 image = texture(image_texture, uv); + vec4 gradient = texture(gradient_texture,uv); + + if (use_solid) { COLOR = new_bg_color; } + if (gradient_behind && use_gradient) { + if (use_solid) { COLOR.rgb = mix(COLOR.rgb, gradient.rgb, gradient.a); } else { COLOR = gradient; } + } + if (use_image) { + if (use_solid || gradient_behind) { COLOR = mix(COLOR, tint_color * image, image.a); } else { COLOR = tint_color * image; } + } + if (!gradient_behind && use_gradient) { + if(use_image || use_solid) { COLOR.rgb = mix(COLOR.rgb, gradient.rgb, gradient.a); } else { COLOR = gradient; } + } +} \ No newline at end of file diff --git a/Penpot2Godot/shader/vertextApplyShader.gdshader b/Penpot2Godot/shader/vertextApplyShader.gdshader new file mode 100644 index 0000000..59d1a81 --- /dev/null +++ b/Penpot2Godot/shader/vertextApplyShader.gdshader @@ -0,0 +1,82 @@ +shader_type canvas_item; + +uniform sampler2D image_texture : source_color, filter_linear, repeat_disable; +uniform sampler2D gradient_texture : source_color, filter_linear, repeat_disable; +uniform bool use_gradient = false; +uniform bool use_image = false; +uniform bool use_solid = true; +uniform bool gradient_behind = false; +uniform vec4 target_color : source_color = vec4(0.6, 0.6, 0.6, 1.0); +uniform float tolerance : hint_range(0.001, 1.0,0.001) = 0.001; +uniform vec4 new_bg_color : source_color = vec4(0.6, 0.6, 0.6, 1.0); +uniform vec4 tint_color : source_color = vec4(1.0, 1.0, 1.0, 1.0); +uniform vec2 node_size; +uniform vec2 texture_size; +uniform float texture_scale : hint_range(0.0, 6.0) = 1.0; +uniform vec2 stretch = vec2(1.0); +uniform vec2 offset = vec2(0.0); +uniform bool flip_x = false; +uniform bool flip_y = false; +uniform bool keep_aspect = true; +uniform bool fill_rect = true; +uniform bool tile_texture = false; +uniform bool manual_scale = false; +uniform vec2 vert_offset = vec2(0.0, 0.0); + +varying vec4 vertex_color; +varying vec4 modulated_color; + +void vertex() { + vertex_color = COLOR; + VERTEX.xy += vert_offset; + modulated_color = vertex_color / target_color; +} + +void fragment() { + float x_dir = flip_x ? -1.0 : 1.0; + float y_dir = flip_y ? -1.0 : 1.0; + vec2 uv = (UV / stretch - offset); + float texture_aspect = texture_size.x / texture_size.y; + float viewport_aspect = node_size.x / node_size.y; + if (keep_aspect && !manual_scale) { + if (fill_rect) { + if (texture_aspect < viewport_aspect) { + uv.y = (uv.y - 0.5) * (texture_aspect / viewport_aspect) + 0.5; + } else { + uv.x = (uv.x - 0.5) * (viewport_aspect / texture_aspect) + 0.5; + } + } else { + if (texture_aspect > viewport_aspect) { + uv.y = (uv.y - 0.5) * (texture_aspect / viewport_aspect) + 0.5; + } else { + uv.x = (uv.x - 0.5) * (viewport_aspect / texture_aspect) + 0.5; + } + } + } + uv -= 0.5; + uv *= vec2(x_dir, y_dir); + if (manual_scale) { + uv /= (vec2(texture_size) / vec2(node_size)) * texture_scale; + } else { + uv /= texture_scale; + } + uv += 0.5; + if (tile_texture) { + uv = fract(uv); + } + vec4 image = texture(image_texture, uv); + vec4 gradient = texture(gradient_texture,UV); + if (distance(vertex_color.rgb, target_color.rgb) < tolerance) { + if (use_solid) { COLOR = new_bg_color; } + if (gradient_behind && use_gradient) { + if (use_solid) { COLOR.rgb = mix(COLOR.rgb, gradient.rgb, gradient.a); } else { COLOR = gradient; } + } + if (use_image) { + if (use_solid || gradient_behind) { COLOR = mix(COLOR, tint_color * image, image.a); } else { COLOR = tint_color * image; } + } + if (!gradient_behind && use_gradient) { + if(use_image || use_solid) { COLOR.rgb = mix(COLOR.rgb, gradient.rgb, gradient.a); } else { COLOR = gradient; } + } + COLOR *= modulated_color.a; + } +} \ No newline at end of file