mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-12 16:53:07 +00:00
Compare commits
25 commits
fd0c19b8af
...
c9ad65841a
Author | SHA1 | Date | |
---|---|---|---|
|
c9ad65841a | ||
|
763783f2f1 | ||
|
e10b0d1b08 | ||
|
94735fc08b | ||
|
8077262b32 | ||
|
0d6b140dea | ||
|
dec698024c | ||
|
785d8cfc83 | ||
|
4c7d7da5e7 | ||
|
36329efaf6 | ||
|
7c1435e95f | ||
|
ad77d98f42 | ||
|
2600180736 | ||
|
5739a8b28e | ||
|
ce738f02c2 | ||
|
b0b1361722 | ||
|
5fa97988b5 | ||
|
5ee7566cb4 | ||
|
aba08cbc37 | ||
|
df8df9d72b | ||
|
fb826995ca | ||
|
d34c7739fb | ||
|
8990ff2816 | ||
|
6a60050bd3 | ||
|
8b12eac231 |
27
CHANGELOG.md
27
CHANGELOG.md
|
@ -4,6 +4,33 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). All the dates are in YYYY-MM-DD format.
|
||||
<br><br>
|
||||
|
||||
## [v1.0.5] - Unreleased
|
||||
This update has been brought to you by the contributions of:
|
||||
Fayez Akhtar ([@Variable-ind](https://github.com/Variable-ind))
|
||||
|
||||
Built using Godot 4.3
|
||||
|
||||
### Added
|
||||
- Add density to the square & circle brushes. 100% density means that the brush gets completely drawn. Anything less leaves gaps inside the brush, acting like a spray tool.
|
||||
- Selection expanding, shrinking and borders have been added as options in the Select menu.
|
||||
- Mouse buttons can now be used as menu shortcuts. [#1070](https://github.com/Orama-Interactive/Pixelorama/issues/1070)
|
||||
- Added confirm and cancel buttons in the selection tool options to confirm/cancel an active transformation.
|
||||
- OKHSL Lightness sorting in palettes has been implemented. [#1126](https://github.com/Orama-Interactive/Pixelorama/pull/1126)
|
||||
|
||||
### Changed
|
||||
- The brush size no longer changes by <kbd>Control</kbd> + Mouse Wheel when resizing the timeline cels or the palette swatches.
|
||||
- The Recorder panel now automatically records for the current project. This also allows for multiple projects to be recorded at the same time.
|
||||
|
||||
### Fixed
|
||||
- Panels no longer get scrolled when using the mouse wheel over a slider.
|
||||
- Fixed layer effect slider values being rounded to the nearest integer.
|
||||
- Fixed memory leak where the project remained referenced by a drawing tool, even when its tab was closed.
|
||||
- Fixed memory leak where the first project remained forever references in memory by the Recorder panel.
|
||||
- Slightly optimize circle brushes by only calling the ellipse algorithms once while drawing
|
||||
|
||||
### Removed
|
||||
- The Recorder panel has been removed from the Web version. It wasn't functional anyway in a way that was useful, and it's unsure if we can find a way to make it work.
|
||||
|
||||
## [v1.0.4] - 2024-10-25
|
||||
This update has been brought to you by the contributions of:
|
||||
Fayez Akhtar ([@Variable-ind](https://github.com/Variable-ind)), Mariano Semelman ([@msemelman](https://github.com/msemelman))
|
||||
|
|
|
@ -205,6 +205,43 @@ msgstr ""
|
|||
msgid "Invert"
|
||||
msgstr ""
|
||||
|
||||
msgid "Modify"
|
||||
msgstr ""
|
||||
|
||||
#. Found under the Select menu, in the Modify submenu. When selected, it shows a window that lets users expand the active selection.
|
||||
msgid "Expand"
|
||||
msgstr ""
|
||||
|
||||
#. Title of a window that lets users expand the active selection.
|
||||
msgid "Expand Selection"
|
||||
msgstr ""
|
||||
|
||||
#. Found under the Select menu, in the Modify submenu. When selected, it shows a window that lets users shrink the active selection.
|
||||
msgid "Shrink"
|
||||
msgstr ""
|
||||
|
||||
#. Title of a window that lets users shrink the active selection.
|
||||
msgid "Shrink Selection"
|
||||
msgstr ""
|
||||
|
||||
#. Found under the Select menu, in the Modify submenu. When selected, it shows a window that lets users create a border of the active selection.
|
||||
msgid "Border"
|
||||
msgstr ""
|
||||
|
||||
#. Title of a window that lets users create a border of the active selection.
|
||||
msgid "Border Selection"
|
||||
msgstr ""
|
||||
|
||||
#. Refers to a diamond-like shape.
|
||||
msgid "Diamond"
|
||||
msgstr ""
|
||||
|
||||
msgid "Circle"
|
||||
msgstr ""
|
||||
|
||||
msgid "Square"
|
||||
msgstr ""
|
||||
|
||||
msgid "Grayscale View"
|
||||
msgstr ""
|
||||
|
||||
|
@ -230,19 +267,16 @@ msgstr ""
|
|||
msgid "Tile Mode Offsets"
|
||||
msgstr ""
|
||||
|
||||
msgid "X-basis x:"
|
||||
#. Found under "Tile Mode Offsets". Basis is a linear algebra term. https://en.wikipedia.org/wiki/Basis_(linear_algebra)
|
||||
msgid "X-basis:"
|
||||
msgstr ""
|
||||
|
||||
msgid "X-basis y:"
|
||||
#. Found under "Tile Mode Offsets". Basis is a linear algebra term. https://en.wikipedia.org/wiki/Basis_(linear_algebra)
|
||||
msgid "Y-basis:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Y-basis x:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Y-basis y:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tile Mask"
|
||||
#. Found under "Tile Mode Offsets". It's a button that when pressed, enables masking for tile mode. Masking essentially limits drawing to the visible pixels of the image, thus preventing from drawing on transparent pixels.
|
||||
msgid "Masking:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reset"
|
||||
|
@ -1865,6 +1899,10 @@ msgstr ""
|
|||
msgid "Fills the drawn shape with color, instead of drawing a hollow shape"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the Pencil, Eraser and Shading tools. It is a percentage of how dense the brush is. 100% density means that the brush gets completely drawn, anything less leaves gaps inside the brush, acting like a spray tool.
|
||||
msgid "Density:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Brush color from"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2808,6 +2846,10 @@ msgstr ""
|
|||
msgid "Sort by value"
|
||||
msgstr ""
|
||||
|
||||
#. An option of the Sort palette button found in the palette panel. When selected, the colors of the palette are being sorted based on their OKHSL Lightness.
|
||||
msgid "Sort by lightness"
|
||||
msgstr ""
|
||||
|
||||
#. An option of the Sort palette button found in the palette panel. When selected, the colors of the palette are being sorted based on their red channel value.
|
||||
msgid "Sort by red"
|
||||
msgstr ""
|
||||
|
|
|
@ -534,6 +534,10 @@ class SelectionAPI:
|
|||
Global.canvas.selection.move_borders_start()
|
||||
else:
|
||||
Global.canvas.selection.transform_content_start()
|
||||
|
||||
if Global.canvas.selection.original_bitmap.is_empty(): # To avoid copying twice.
|
||||
Global.canvas.selection.original_bitmap.copy_from(Global.current_project.selection_map)
|
||||
|
||||
Global.canvas.selection.big_bounding_rectangle.size = new_size
|
||||
Global.canvas.selection.resize_selection()
|
||||
Global.canvas.selection.move_borders_end()
|
||||
|
|
|
@ -71,7 +71,7 @@ enum EffectsMenu {
|
|||
SHADER
|
||||
}
|
||||
## Enumeration of items present in the Select Menu.
|
||||
enum SelectMenu { SELECT_ALL, CLEAR_SELECTION, INVERT, TILE_MODE }
|
||||
enum SelectMenu { SELECT_ALL, CLEAR_SELECTION, INVERT, TILE_MODE, MODIFY }
|
||||
## Enumeration of items present in the Help Menu.
|
||||
enum HelpMenu {
|
||||
VIEW_SPLASH_SCREEN,
|
||||
|
@ -332,55 +332,8 @@ var default_height := 64 ## Found in Preferences. The default height of startup
|
|||
var default_fill_color := Color(0, 0, 0, 0)
|
||||
## Found in Preferences. The distance to the guide or grig below which cursor snapping activates.
|
||||
var snapping_distance := 32.0
|
||||
## Found in Preferences. The grid type defined by [enum GridTypes] enum.
|
||||
var grid_type := GridTypes.CARTESIAN:
|
||||
set(value):
|
||||
if value == grid_type:
|
||||
return
|
||||
grid_type = value
|
||||
if is_instance_valid(canvas.grid):
|
||||
canvas.grid.queue_redraw()
|
||||
## Found in Preferences. The size of rectangular grid.
|
||||
var grid_size := Vector2i(2, 2):
|
||||
set(value):
|
||||
if value == grid_size:
|
||||
return
|
||||
grid_size = value
|
||||
if is_instance_valid(canvas.grid):
|
||||
canvas.grid.queue_redraw()
|
||||
## Found in Preferences. The size of isometric grid.
|
||||
var isometric_grid_size := Vector2i(16, 8):
|
||||
set(value):
|
||||
if value == isometric_grid_size:
|
||||
return
|
||||
isometric_grid_size = value
|
||||
if is_instance_valid(canvas.grid):
|
||||
canvas.grid.queue_redraw()
|
||||
## Found in Preferences. The grid offset from top-left corner of the canvas.
|
||||
var grid_offset := Vector2i.ZERO:
|
||||
set(value):
|
||||
if value == grid_offset:
|
||||
return
|
||||
grid_offset = value
|
||||
if is_instance_valid(canvas.grid):
|
||||
canvas.grid.queue_redraw()
|
||||
## Found in Preferences. If [code]true[/code], The grid draws over the area extended by
|
||||
## tile-mode as well.
|
||||
var grid_draw_over_tile_mode := false:
|
||||
set(value):
|
||||
if value == grid_draw_over_tile_mode:
|
||||
return
|
||||
grid_draw_over_tile_mode = value
|
||||
if is_instance_valid(canvas.grid):
|
||||
canvas.grid.queue_redraw()
|
||||
## Found in Preferences. The color of grid.
|
||||
var grid_color := Color.BLACK:
|
||||
set(value):
|
||||
if value == grid_color:
|
||||
return
|
||||
grid_color = value
|
||||
if is_instance_valid(canvas.grid):
|
||||
canvas.grid.queue_redraw()
|
||||
## Contains dictionaries of individual grids.
|
||||
var grids: Array[Grid] = []
|
||||
## Found in Preferences. The minimum zoom after which pixel grid gets drawn if enabled.
|
||||
var pixel_grid_show_at_zoom := 1500.0: # percentage
|
||||
set(value):
|
||||
|
@ -672,6 +625,62 @@ var cel_button_scene: PackedScene = load("res://src/UI/Timeline/CelButton.tscn")
|
|||
@onready var error_dialog: AcceptDialog = control.find_child("ErrorDialog")
|
||||
|
||||
|
||||
class Grid:
|
||||
var grid_type := GridTypes.CARTESIAN:
|
||||
set(value):
|
||||
if value == grid_type:
|
||||
return
|
||||
grid_type = value
|
||||
if is_instance_valid(Global.canvas.grid):
|
||||
Global.canvas.grid.queue_redraw()
|
||||
## Found in Preferences. The size of rectangular grid.
|
||||
var grid_size := Vector2i(2, 2):
|
||||
set(value):
|
||||
if value == grid_size:
|
||||
return
|
||||
grid_size = value
|
||||
if is_instance_valid(Global.canvas.grid):
|
||||
Global.canvas.grid.queue_redraw()
|
||||
## Found in Preferences. The size of isometric grid.
|
||||
var isometric_grid_size := Vector2i(16, 8):
|
||||
set(value):
|
||||
if value == isometric_grid_size:
|
||||
return
|
||||
isometric_grid_size = value
|
||||
if is_instance_valid(Global.canvas.grid):
|
||||
Global.canvas.grid.queue_redraw()
|
||||
## Found in Preferences. The grid offset from top-left corner of the canvas.
|
||||
var grid_offset := Vector2i.ZERO:
|
||||
set(value):
|
||||
if value == grid_offset:
|
||||
return
|
||||
grid_offset = value
|
||||
if is_instance_valid(Global.canvas.grid):
|
||||
Global.canvas.grid.queue_redraw()
|
||||
## Found in Preferences. If [code]true[/code], The grid draws over the area extended by
|
||||
## tile-mode as well.
|
||||
var grid_draw_over_tile_mode := false:
|
||||
set(value):
|
||||
if value == grid_draw_over_tile_mode:
|
||||
return
|
||||
grid_draw_over_tile_mode = value
|
||||
if is_instance_valid(Global.canvas.grid):
|
||||
Global.canvas.grid.queue_redraw()
|
||||
## Found in Preferences. The color of grid.
|
||||
var grid_color := Color.BLACK:
|
||||
set(value):
|
||||
if value == grid_color:
|
||||
return
|
||||
grid_color = value
|
||||
if is_instance_valid(Global.canvas.grid):
|
||||
Global.canvas.grid.queue_redraw()
|
||||
|
||||
func _init(properties := {}) -> void:
|
||||
Global.grids.append(self)
|
||||
for prop in properties.keys():
|
||||
set(prop, properties[prop])
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
# Load settings from the config file
|
||||
config_cache.load(CONFIG_PATH)
|
||||
|
@ -708,6 +717,8 @@ func _init() -> void:
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
# Initialize Grid
|
||||
Grid.new() # gets auto added to grids array
|
||||
_initialize_keychain()
|
||||
default_width = config_cache.get_value("preferences", "default_width", default_width)
|
||||
default_height = config_cache.get_value("preferences", "default_height", default_height)
|
||||
|
@ -724,13 +735,23 @@ func _ready() -> void:
|
|||
if get(pref) == null:
|
||||
continue
|
||||
var value = config_cache.get_value("preferences", pref)
|
||||
set(pref, value)
|
||||
if pref == "grids":
|
||||
if value:
|
||||
update_grids(value)
|
||||
else:
|
||||
set(pref, value)
|
||||
if OS.is_sandboxed():
|
||||
Global.use_native_file_dialogs = true
|
||||
await get_tree().process_frame
|
||||
project_switched.emit()
|
||||
|
||||
|
||||
func update_grids(grids_data: Dictionary):
|
||||
grids.clear()
|
||||
for grid_idx in grids_data.size():
|
||||
Grid.new(grids_data[grid_idx]) # gets auto added to grids array
|
||||
|
||||
|
||||
func _initialize_keychain() -> void:
|
||||
Keychain.config_file = config_cache
|
||||
Keychain.actions = {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
class_name SelectionMap
|
||||
extends Image
|
||||
|
||||
var invert_shader := preload("res://src/Shaders/Effects/Invert.gdshader")
|
||||
const INVERT_SHADER := preload("res://src/Shaders/Effects/Invert.gdshader")
|
||||
const OUTLINE_INLINE_SHADER := preload("res://src/Shaders/Effects/OutlineInline.gdshader")
|
||||
|
||||
|
||||
func is_pixel_selected(pixel: Vector2i, calculate_offset := true) -> bool:
|
||||
|
@ -87,8 +88,7 @@ func clear() -> void:
|
|||
func invert() -> void:
|
||||
var params := {"red": true, "green": true, "blue": true, "alpha": true}
|
||||
var gen := ShaderImageEffect.new()
|
||||
gen.generate_image(self, invert_shader, params, get_size())
|
||||
self.convert(Image.FORMAT_LA8)
|
||||
gen.generate_image(self, INVERT_SHADER, params, get_size())
|
||||
|
||||
|
||||
## Returns a copy of itself that is cropped to [param size].
|
||||
|
@ -183,3 +183,36 @@ func resize_bitmap_values(
|
|||
if new_bitmap_size != size:
|
||||
crop(new_bitmap_size.x, new_bitmap_size.y)
|
||||
blit_rect(smaller_image, Rect2i(Vector2i.ZERO, new_bitmap_size), dst)
|
||||
|
||||
|
||||
func expand(width: int, brush: int) -> void:
|
||||
var params := {
|
||||
"color": Color(1, 1, 1, 1),
|
||||
"width": width,
|
||||
"brush": brush,
|
||||
}
|
||||
var gen := ShaderImageEffect.new()
|
||||
gen.generate_image(self, OUTLINE_INLINE_SHADER, params, get_size())
|
||||
|
||||
|
||||
func shrink(width: int, brush: int) -> void:
|
||||
var params := {
|
||||
"color": Color(0),
|
||||
"width": width,
|
||||
"brush": brush,
|
||||
"inside": true,
|
||||
}
|
||||
var gen := ShaderImageEffect.new()
|
||||
gen.generate_image(self, OUTLINE_INLINE_SHADER, params, get_size())
|
||||
|
||||
|
||||
func border(width: int, brush: int) -> void:
|
||||
var params := {
|
||||
"color": Color(1, 1, 1, 1),
|
||||
"width": width,
|
||||
"brush": brush,
|
||||
"inside": true,
|
||||
"keep_border_only": true,
|
||||
}
|
||||
var gen := ShaderImageEffect.new()
|
||||
gen.generate_image(self, OUTLINE_INLINE_SHADER, params, get_size())
|
||||
|
|
|
@ -54,7 +54,7 @@ func generate_image(img: Image, shader: Shader, params: Dictionary, size: Vector
|
|||
RenderingServer.free_rid(ci_rid)
|
||||
RenderingServer.free_rid(mat_rid)
|
||||
RenderingServer.free_rid(texture)
|
||||
viewport_texture.convert(Image.FORMAT_RGBA8)
|
||||
viewport_texture.convert(img.get_format())
|
||||
img.copy_from(viewport_texture)
|
||||
if resized_width:
|
||||
img.crop(img.get_width() - 1, img.get_height())
|
||||
|
|
|
@ -148,13 +148,13 @@ static func create_ui_for_shader_uniforms(
|
|||
|
||||
if u_value != "":
|
||||
slider.value = int(u_value)
|
||||
slider.min_value = min_value
|
||||
slider.max_value = max_value
|
||||
slider.step = step
|
||||
if params.has(u_name):
|
||||
slider.value = params[u_name]
|
||||
else:
|
||||
params[u_name] = slider.value
|
||||
slider.min_value = min_value
|
||||
slider.max_value = max_value
|
||||
slider.step = step
|
||||
slider.value_changed.connect(value_changed.bind(u_name))
|
||||
slider.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
hbox.add_child(slider)
|
||||
|
|
|
@ -90,3 +90,4 @@ func _on_PaletteScroll_gui_input(event: InputEvent) -> void:
|
|||
return
|
||||
resize_grid()
|
||||
set_sliders(palette_grid.current_palette, palette_grid.grid_window_origin + scroll_vector)
|
||||
get_window().set_input_as_handled()
|
||||
|
|
163
src/Preferences/GridPreferences.gd
Normal file
163
src/Preferences/GridPreferences.gd
Normal file
|
@ -0,0 +1,163 @@
|
|||
extends GridContainer
|
||||
|
||||
var grid_preferences: Array[GridPreference] = [
|
||||
GridPreference.new("grid_type", "GridType", "selected", Global.GridTypes.CARTESIAN),
|
||||
GridPreference.new("grid_size", "GridSizeValue", "value", Vector2i(2, 2)),
|
||||
GridPreference.new("isometric_grid_size", "IsometricGridSizeValue", "value", Vector2i(16, 8)),
|
||||
GridPreference.new("grid_offset", "GridOffsetValue", "value", Vector2i.ZERO),
|
||||
GridPreference.new("grid_draw_over_tile_mode", "GridDrawOverTileMode", "button_pressed", false),
|
||||
GridPreference.new("grid_color", "GridColor", "color", Color.BLACK),
|
||||
]
|
||||
|
||||
var grid_selected: int = 0:
|
||||
set(key):
|
||||
grid_selected = key
|
||||
for child: BaseButton in grids_select_container.get_children():
|
||||
if child.get_index() == grid_selected:
|
||||
child.self_modulate = Color.WHITE
|
||||
else:
|
||||
child.self_modulate = Color.DIM_GRAY
|
||||
var grids: Dictionary = Global.config_cache.get_value(
|
||||
"preferences", "grids", {0: create_default_properties()}
|
||||
)
|
||||
if grids.has(key):
|
||||
update_pref_ui(grids[key])
|
||||
|
||||
@onready var grids_select_container: HFlowContainer = $GridsSelectContainer
|
||||
|
||||
|
||||
class GridPreference:
|
||||
var prop_name: String
|
||||
var node_path: String
|
||||
var value_type: String
|
||||
var default_value
|
||||
|
||||
func _init(
|
||||
_prop_name: String,
|
||||
_node_path: String,
|
||||
_value_type: String,
|
||||
_default_value = null,
|
||||
_require_restart := false
|
||||
) -> void:
|
||||
prop_name = _prop_name
|
||||
node_path = _node_path
|
||||
value_type = _value_type
|
||||
if _default_value != null:
|
||||
default_value = _default_value
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var grids = Global.config_cache.get_value(
|
||||
"preferences", "grids", {0: create_default_properties()}
|
||||
)
|
||||
Global.config_cache.set_value("preferences", "grids", grids)
|
||||
$GridsCount.value = grids.size()
|
||||
if grids.size() == 1:
|
||||
add_remove_select_button(0)
|
||||
for pref in grid_preferences:
|
||||
if not has_node(pref.node_path):
|
||||
continue
|
||||
var node := get_node(pref.node_path)
|
||||
var restore_default_button := RestoreDefaultButton.new()
|
||||
restore_default_button.pressed.connect(
|
||||
_on_grid_pref_value_changed.bind(pref.default_value, pref, restore_default_button)
|
||||
)
|
||||
restore_default_button.setting_name = pref.prop_name
|
||||
restore_default_button.value_type = pref.value_type
|
||||
restore_default_button.default_value = pref.default_value
|
||||
restore_default_button.node = node
|
||||
|
||||
var node_position := node.get_index()
|
||||
node.get_parent().add_child(restore_default_button)
|
||||
node.get_parent().move_child(restore_default_button, node_position)
|
||||
|
||||
match pref.value_type:
|
||||
"button_pressed":
|
||||
node.toggled.connect(_on_grid_pref_value_changed.bind(pref, restore_default_button))
|
||||
"value":
|
||||
node.value_changed.connect(
|
||||
_on_grid_pref_value_changed.bind(pref, restore_default_button)
|
||||
)
|
||||
"color":
|
||||
node.get_picker().presets_visible = false
|
||||
node.color_changed.connect(
|
||||
_on_grid_pref_value_changed.bind(pref, restore_default_button)
|
||||
)
|
||||
"selected":
|
||||
node.item_selected.connect(
|
||||
_on_grid_pref_value_changed.bind(pref, restore_default_button)
|
||||
)
|
||||
grid_selected = 0
|
||||
|
||||
|
||||
func _on_grid_pref_value_changed(value, pref: GridPreference, button: RestoreDefaultButton) -> void:
|
||||
var grids: Dictionary = Global.config_cache.get_value(
|
||||
"preferences", "grids", {0: create_default_properties()}
|
||||
)
|
||||
if grids.has(grid_selected): # Failsafe (Always true)
|
||||
var grid_info: Dictionary = grids[grid_selected]
|
||||
var prop := pref.prop_name
|
||||
grid_info[prop] = value
|
||||
grids[grid_selected] = grid_info
|
||||
Global.update_grids(grids)
|
||||
var default_value = pref.default_value
|
||||
var disable: bool = Global.grids[grid_selected].get(prop) == default_value
|
||||
if typeof(value) == TYPE_COLOR:
|
||||
disable = value.is_equal_approx(default_value)
|
||||
disable_restore_default_button(button, disable)
|
||||
Global.config_cache.set_value("preferences", "grids", grids)
|
||||
|
||||
|
||||
func _on_grids_count_value_changed(value: float) -> void:
|
||||
var grid_idx = int(value - 1)
|
||||
var grids: Dictionary = Global.config_cache.get_value(
|
||||
"preferences", "grids", {0: create_default_properties()}
|
||||
)
|
||||
if grid_idx >= grids_select_container.get_child_count():
|
||||
for key in range(grids_select_container.get_child_count(), grid_idx + 1):
|
||||
if not grids.has(key):
|
||||
grids[key] = create_default_properties()
|
||||
add_remove_select_button(key)
|
||||
else:
|
||||
for key: int in range(grid_idx + 1, grids.size()):
|
||||
grids.erase(key)
|
||||
add_remove_select_button(key, true)
|
||||
Global.update_grids(grids)
|
||||
Global.config_cache.set_value("preferences", "grids", grids)
|
||||
|
||||
|
||||
func create_default_properties() -> Dictionary:
|
||||
var grid_info = {}
|
||||
for pref in grid_preferences:
|
||||
grid_info[pref.prop_name] = pref.default_value
|
||||
return grid_info
|
||||
|
||||
|
||||
func disable_restore_default_button(button: RestoreDefaultButton, disable: bool) -> void:
|
||||
button.disabled = disable
|
||||
if disable:
|
||||
button.mouse_default_cursor_shape = Control.CURSOR_ARROW
|
||||
button.tooltip_text = ""
|
||||
else:
|
||||
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
button.tooltip_text = "Restore default value"
|
||||
|
||||
|
||||
func add_remove_select_button(grid_idx: int, remove := false):
|
||||
if not remove:
|
||||
var select_button = Button.new()
|
||||
select_button.text = str(grid_idx)
|
||||
grids_select_container.add_child(select_button)
|
||||
select_button.pressed.connect(func(): grid_selected = grid_idx)
|
||||
else:
|
||||
if grid_idx < grids_select_container.get_child_count():
|
||||
grids_select_container.get_child(grid_idx).queue_free()
|
||||
grid_selected = min(grid_selected, grid_idx - 1)
|
||||
|
||||
|
||||
func update_pref_ui(grid_data: Dictionary):
|
||||
for pref in grid_preferences:
|
||||
var key = pref.prop_name
|
||||
if grid_data.has(key):
|
||||
var node := get_node(pref.node_path)
|
||||
node.set(pref.value_type, grid_data[key])
|
|
@ -94,21 +94,6 @@ var preferences: Array[Preference] = [
|
|||
Preference.new("smooth_zoom", "Canvas/ZoomOptions/SmoothZoom", "button_pressed", true),
|
||||
Preference.new("integer_zoom", "Canvas/ZoomOptions/IntegerZoom", "button_pressed", false),
|
||||
Preference.new("snapping_distance", "Canvas/SnappingOptions/DistanceValue", "value", 32.0),
|
||||
Preference.new(
|
||||
"grid_type", "Canvas/GridOptions/GridType", "selected", Global.GridTypes.CARTESIAN
|
||||
),
|
||||
Preference.new("grid_size", "Canvas/GridOptions/GridSizeValue", "value", Vector2i(2, 2)),
|
||||
Preference.new(
|
||||
"isometric_grid_size", "Canvas/GridOptions/IsometricGridSizeValue", "value", Vector2i(16, 8)
|
||||
),
|
||||
Preference.new("grid_offset", "Canvas/GridOptions/GridOffsetValue", "value", Vector2i.ZERO),
|
||||
Preference.new(
|
||||
"grid_draw_over_tile_mode",
|
||||
"Canvas/GridOptions/GridDrawOverTileMode",
|
||||
"button_pressed",
|
||||
false
|
||||
),
|
||||
Preference.new("grid_color", "Canvas/GridOptions/GridColor", "color", Color.BLACK),
|
||||
Preference.new(
|
||||
"pixel_grid_show_at_zoom", "Canvas/PixelGridOptions/ShowAtZoom", "value", 1500.0
|
||||
),
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
[gd_scene load_steps=9 format=3 uid="uid://b3hkjj3s6pe4x"]
|
||||
[gd_scene load_steps=11 format=3 uid="uid://b3hkjj3s6pe4x"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/Preferences/PreferencesDialog.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://bq7ibhm0txl5p" path="res://addons/keychain/ShortcutEdit.tscn" id="3"]
|
||||
[ext_resource type="Script" path="res://src/Preferences/ThemesPreferences.gd" id="3_nvl8k"]
|
||||
[ext_resource type="Script" path="res://src/Preferences/GridPreferences.gd" id="4_76iff"]
|
||||
[ext_resource type="PackedScene" uid="uid://yjhp0ssng2mp" path="res://src/UI/Nodes/ValueSlider.tscn" id="5_rlmsh"]
|
||||
[ext_resource type="PackedScene" path="res://src/UI/Nodes/ValueSliderV2.tscn" id="7"]
|
||||
[ext_resource type="Script" path="res://src/Preferences/ExtensionsPreferences.gd" id="7_8ume5"]
|
||||
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="8"]
|
||||
|
@ -482,6 +484,30 @@ layout_mode = 2
|
|||
theme_override_constants/h_separation = 4
|
||||
theme_override_constants/v_separation = 4
|
||||
columns = 3
|
||||
script = ExtResource("4_76iff")
|
||||
|
||||
[node name="GridsCountLabel" type="Label" parent="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions"]
|
||||
layout_mode = 2
|
||||
text = "Grids Visible:"
|
||||
|
||||
[node name="Spacer" type="Control" parent="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridsCount" parent="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions" instance=ExtResource("5_rlmsh")]
|
||||
layout_mode = 2
|
||||
min_value = 1.0
|
||||
max_value = 10.0
|
||||
value = 1.0
|
||||
|
||||
[node name="GridsSelectLabel" type="Label" parent="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions"]
|
||||
layout_mode = 2
|
||||
text = "Editing Grid:"
|
||||
|
||||
[node name="Spacer2" type="Control" parent="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridsSelectContainer" type="HFlowContainer" parent="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridTypeLabel" type="Label" parent="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions"]
|
||||
layout_mode = 2
|
||||
|
@ -1478,6 +1504,7 @@ dialog_text = "Are you sure you want to reset the selected options? There will b
|
|||
[connection signal="pressed" from="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Language/System Language" to="." method="_on_language_pressed" binds= [1]]
|
||||
[connection signal="pressed" from="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Interface/InterfaceOptions/ShrinkContainer/ShrinkApplyButton" to="." method="_on_shrink_apply_button_pressed"]
|
||||
[connection signal="pressed" from="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Interface/InterfaceOptions/FontSizeContainer/FontSizeApplyButton" to="." method="_on_font_size_apply_button_pressed"]
|
||||
[connection signal="value_changed" from="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions/GridsCount" to="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Canvas/GridOptions" method="_on_grids_count_value_changed"]
|
||||
[connection signal="pressed" from="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Extensions/ExtensionsHeader/Explore" to="Store" method="_on_explore_pressed"]
|
||||
[connection signal="empty_clicked" from="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Extensions/InstalledExtensions" to="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Extensions" method="_on_InstalledExtensions_empty_clicked"]
|
||||
[connection signal="item_selected" from="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Extensions/InstalledExtensions" to="HSplitContainer/VBoxContainer/ScrollContainer/RightSide/Extensions" method="_on_InstalledExtensions_item_selected"]
|
||||
|
|
|
@ -4,9 +4,10 @@ render_mode unshaded;
|
|||
|
||||
uniform vec4 color : source_color = vec4(1.0);
|
||||
uniform float width : hint_range(0, 10, 1) = 1.0;
|
||||
// uniform_data pattern type:: OptionButton [Diamond||Circle||Square]
|
||||
uniform int pattern : hint_range(0, 2) = 0;
|
||||
// uniform_data brush type:: OptionButton [Diamond||Circle||Square]
|
||||
uniform int brush : hint_range(0, 2) = 0;
|
||||
uniform bool inside = false;
|
||||
uniform bool keep_border_only = false;
|
||||
uniform sampler2D selection : filter_nearest;
|
||||
|
||||
bool is_zero_approx(float num) {
|
||||
|
@ -17,11 +18,11 @@ bool has_contrary_neighbour(vec2 uv, vec2 texture_pixel_size, sampler2D tex) {
|
|||
for (float i = -ceil(width); i <= ceil(width); i++) {
|
||||
float offset;
|
||||
|
||||
if (pattern == 0) {
|
||||
if (brush == 0) {
|
||||
offset = width - abs(i);
|
||||
} else if (pattern == 1) {
|
||||
} else if (brush == 1) {
|
||||
offset = floor(sqrt(pow(width + 0.5, 2) - i * i));
|
||||
} else if (pattern == 2) {
|
||||
} else if (brush == 2) {
|
||||
offset = width;
|
||||
}
|
||||
|
||||
|
@ -44,7 +45,15 @@ void fragment() {
|
|||
|
||||
if ((output.a > 0.0) == inside && has_contrary_neighbour(UV, TEXTURE_PIXEL_SIZE, TEXTURE)) {
|
||||
output.rgb = inside ? mix(output.rgb, color.rgb, color.a) : color.rgb;
|
||||
output.a += (1.0 - output.a) * color.a;
|
||||
if (is_zero_approx(color.a)) {
|
||||
output.a = color.a;
|
||||
}
|
||||
else {
|
||||
output.a += (1.0 - output.a) * color.a;
|
||||
}
|
||||
}
|
||||
else if (keep_border_only) {
|
||||
output.a = 0.0;
|
||||
}
|
||||
|
||||
COLOR = mix(original_color, output, selection_color.a);
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
extends BaseTool
|
||||
|
||||
const IMAGE_BRUSHES := [Brushes.FILE, Brushes.RANDOM_FILE, Brushes.CUSTOM]
|
||||
|
||||
var _brush := Brushes.get_default_brush()
|
||||
var _brush_size := 1
|
||||
var _brush_size_dynamics := 1
|
||||
var _brush_density := 100
|
||||
var _brush_flip_x := false
|
||||
var _brush_flip_y := false
|
||||
var _brush_rotate_90 := false
|
||||
|
@ -89,6 +92,12 @@ func _reset_dynamics() -> void:
|
|||
save_config()
|
||||
|
||||
|
||||
func _on_density_value_slider_value_changed(value: int) -> void:
|
||||
_brush_density = value
|
||||
update_config()
|
||||
save_config()
|
||||
|
||||
|
||||
func _on_InterpolateFactor_value_changed(value: float) -> void:
|
||||
_brush_interpolate = int(value)
|
||||
update_config()
|
||||
|
@ -111,6 +120,7 @@ func get_config() -> Dictionary:
|
|||
"brush_type": _brush.type,
|
||||
"brush_index": _brush.index,
|
||||
"brush_size": _brush_size,
|
||||
"brush_density": _brush_density,
|
||||
"brush_interpolate": _brush_interpolate,
|
||||
"brush_flip_x": _brush_flip_x,
|
||||
"brush_flip_y": _brush_flip_y,
|
||||
|
@ -128,6 +138,7 @@ func set_config(config: Dictionary) -> void:
|
|||
_brush_size_dynamics = _brush_size
|
||||
if Tools.dynamics_size != Tools.Dynamics.NONE:
|
||||
_brush_size_dynamics = Tools.brush_size_min
|
||||
_brush_density = config.get("brush_density", _brush_density)
|
||||
_brush_interpolate = config.get("brush_interpolate", _brush_interpolate)
|
||||
_brush_flip_x = config.get("brush_flip_x", _brush_flip_x)
|
||||
_brush_flip_y = config.get("brush_flip_y", _brush_flip_y)
|
||||
|
@ -177,11 +188,13 @@ func update_brush() -> void:
|
|||
_brush_texture = ImageTexture.create_from_image(_brush_image)
|
||||
update_mirror_brush()
|
||||
_stroke_dimensions = _brush_image.get_size()
|
||||
_circle_tool_shortcut = []
|
||||
_indicator = _create_brush_indicator()
|
||||
_polylines = _create_polylines(_indicator)
|
||||
$Brush/Type/Texture.texture = _brush_texture
|
||||
$ColorInterpolation.visible = _brush.type in [Brushes.FILE, Brushes.RANDOM_FILE, Brushes.CUSTOM]
|
||||
$RotationOptions.visible = _brush.type in [Brushes.FILE, Brushes.RANDOM_FILE, Brushes.CUSTOM]
|
||||
$DensityValueSlider.visible = _brush.type not in IMAGE_BRUSHES
|
||||
$ColorInterpolation.visible = _brush.type in IMAGE_BRUSHES
|
||||
$RotationOptions.visible = _brush.type in IMAGE_BRUSHES
|
||||
|
||||
|
||||
func update_random_image() -> void:
|
||||
|
@ -275,6 +288,7 @@ func draw_end(pos: Vector2i) -> void:
|
|||
super.draw_end(pos)
|
||||
_stroke_project = null
|
||||
_stroke_images = []
|
||||
_circle_tool_shortcut = []
|
||||
_brush_size_dynamics = _brush_size
|
||||
if Tools.dynamics_size != Tools.Dynamics.NONE:
|
||||
_brush_size_dynamics = Tools.brush_size_min
|
||||
|
@ -313,10 +327,6 @@ func _prepare_tool() -> void:
|
|||
# This may prevent a few tests when setting pixels
|
||||
_is_mask_size_zero = _mask.size() == 0
|
||||
match _brush.type:
|
||||
Brushes.CIRCLE:
|
||||
_prepare_circle_tool(false)
|
||||
Brushes.FILLED_CIRCLE:
|
||||
_prepare_circle_tool(true)
|
||||
Brushes.FILE, Brushes.RANDOM_FILE, Brushes.CUSTOM:
|
||||
# save _brush_image for safe keeping
|
||||
_brush_image = _create_blended_brush_image(_orignal_brush_image)
|
||||
|
@ -326,19 +336,6 @@ func _prepare_tool() -> void:
|
|||
_stroke_dimensions = _brush_image.get_size()
|
||||
|
||||
|
||||
func _prepare_circle_tool(fill: bool) -> void:
|
||||
var circle_tool_map := _create_circle_indicator(_brush_size_dynamics, fill)
|
||||
# Go through that BitMap and build an Array of the "displacement" from the center of the bits
|
||||
# that are true.
|
||||
var diameter := _brush_size_dynamics * 2 + 1
|
||||
for n in range(0, diameter):
|
||||
for m in range(0, diameter):
|
||||
if circle_tool_map.get_bitv(Vector2i(m, n)):
|
||||
_circle_tool_shortcut.append(
|
||||
Vector2i(m - _brush_size_dynamics, n - _brush_size_dynamics)
|
||||
)
|
||||
|
||||
|
||||
## Make sure to always have invoked _prepare_tool() before this. This computes the coordinates to be
|
||||
## drawn if it can (except for the generic brush, when it's actually drawing them)
|
||||
func _draw_tool(pos: Vector2) -> PackedVector2Array:
|
||||
|
@ -507,7 +504,7 @@ func draw_indicator(left: bool) -> void:
|
|||
|
||||
func draw_indicator_at(pos: Vector2i, offset: Vector2i, color: Color) -> void:
|
||||
var canvas: Node2D = Global.canvas.indicators
|
||||
if _brush.type in [Brushes.FILE, Brushes.RANDOM_FILE, Brushes.CUSTOM] and not _draw_line:
|
||||
if _brush.type in IMAGE_BRUSHES and not _draw_line:
|
||||
pos -= _brush_image.get_size() / 2
|
||||
pos -= offset
|
||||
canvas.draw_texture(_brush_texture, pos)
|
||||
|
@ -537,6 +534,8 @@ func _set_pixel(pos: Vector2i, ignore_mirroring := false) -> void:
|
|||
|
||||
|
||||
func _set_pixel_no_cache(pos: Vector2i, ignore_mirroring := false) -> void:
|
||||
if randi() % 100 >= _brush_density:
|
||||
return
|
||||
pos = _stroke_project.tiles.get_canon_position(pos)
|
||||
if Global.current_project.has_selection:
|
||||
pos = Global.current_project.selection_map.get_canon_position(pos)
|
||||
|
@ -621,10 +620,24 @@ func _create_pixel_indicator(brush_size: int) -> BitMap:
|
|||
|
||||
|
||||
func _create_circle_indicator(brush_size: int, fill := false) -> BitMap:
|
||||
_circle_tool_shortcut = []
|
||||
if Tools.dynamics_size != Tools.Dynamics.NONE:
|
||||
_circle_tool_shortcut = []
|
||||
var brush_size_v2 := Vector2i(brush_size, brush_size)
|
||||
var diameter := brush_size_v2 * 2 + Vector2i.ONE
|
||||
return _fill_bitmap_with_points(_compute_draw_tool_circle(brush_size_v2, fill), diameter)
|
||||
var diameter_v2 := brush_size_v2 * 2 + Vector2i.ONE
|
||||
var circle_tool_map := _fill_bitmap_with_points(
|
||||
_compute_draw_tool_circle(brush_size_v2, fill), diameter_v2
|
||||
)
|
||||
if _circle_tool_shortcut.is_empty():
|
||||
# Go through that BitMap and build an Array of the "displacement"
|
||||
# from the center of the bits that are true.
|
||||
var diameter := _brush_size_dynamics * 2 + 1
|
||||
for n in range(0, diameter):
|
||||
for m in range(0, diameter):
|
||||
if circle_tool_map.get_bitv(Vector2i(m, n)):
|
||||
_circle_tool_shortcut.append(
|
||||
Vector2i(m - _brush_size_dynamics, n - _brush_size_dynamics)
|
||||
)
|
||||
return circle_tool_map
|
||||
|
||||
|
||||
func _create_line_indicator(indicator: BitMap, start: Vector2i, end: Vector2i) -> BitMap:
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://ubyatap3sylf"]
|
||||
[gd_scene load_steps=9 format=3 uid="uid://ubyatap3sylf"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://yjhp0ssng2mp" path="res://src/UI/Nodes/ValueSlider.tscn" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://ctfgfelg0sho8" path="res://src/Tools/BaseTool.tscn" id="2"]
|
||||
[ext_resource type="Script" path="res://src/Tools/BaseDraw.gd" id="3"]
|
||||
[ext_resource type="Script" path="res://src/UI/Nodes/CollapsibleContainer.gd" id="3_76bek"]
|
||||
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="5_kdxku"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_7u3x0"]
|
||||
resource_name = "rotate"
|
||||
|
@ -130,7 +131,25 @@ suffix = "px"
|
|||
global_increment_action = "brush_size_increment"
|
||||
global_decrement_action = "brush_size_decrement"
|
||||
|
||||
[node name="ColorInterpolation" parent="." index="4" instance=ExtResource("1")]
|
||||
[node name="DensityValueSlider" type="TextureProgressBar" parent="." index="4"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 24)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
theme_type_variation = &"ValueSlider"
|
||||
min_value = 1.0
|
||||
value = 100.0
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource("5_kdxku")
|
||||
prefix = "Density:"
|
||||
suffix = "%"
|
||||
|
||||
[node name="ColorInterpolation" parent="." index="5" instance=ExtResource("1")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
tooltip_text = "0: Color from the brush itself, 100: the currently selected color"
|
||||
|
@ -143,4 +162,5 @@ prefix = "Brush color from:"
|
|||
[connection signal="toggled" from="RotationOptions/GridContainer/Rotate/Rotate270" to="." method="_on_rotate_270_toggled"]
|
||||
[connection signal="pressed" from="Brush/Type" to="." method="_on_BrushType_pressed"]
|
||||
[connection signal="value_changed" from="Brush/BrushSize" to="." method="_on_BrushSize_value_changed"]
|
||||
[connection signal="value_changed" from="DensityValueSlider" to="." method="_on_density_value_slider_value_changed"]
|
||||
[connection signal="value_changed" from="ColorInterpolation" to="." method="_on_InterpolateFactor_value_changed"]
|
||||
|
|
|
@ -159,16 +159,17 @@ func draw_move(pos: Vector2i) -> void:
|
|||
else:
|
||||
pos.x = _start_pos.x
|
||||
if Input.is_action_pressed("transform_snap_grid"):
|
||||
_offset = _offset.snapped(Global.grid_size)
|
||||
_offset = _offset.snapped(Global.grids[0].grid_size)
|
||||
var prev_pos: Vector2i = selection_node.big_bounding_rectangle.position
|
||||
selection_node.big_bounding_rectangle.position = prev_pos.snapped(Global.grid_size)
|
||||
selection_node.big_bounding_rectangle.position = prev_pos.snapped(Global.grids[0].grid_size)
|
||||
selection_node.marching_ants_outline.offset += Vector2(
|
||||
selection_node.big_bounding_rectangle.position - prev_pos
|
||||
)
|
||||
pos = pos.snapped(Global.grid_size)
|
||||
var grid_offset := Global.grid_offset
|
||||
pos = pos.snapped(Global.grids[0].grid_size)
|
||||
var grid_offset := Global.grids[0].grid_offset
|
||||
grid_offset = Vector2i(
|
||||
fmod(grid_offset.x, Global.grid_size.x), fmod(grid_offset.y, Global.grid_size.y)
|
||||
fmod(grid_offset.x, Global.grids[0].grid_size.x),
|
||||
fmod(grid_offset.y, Global.grids[0].grid_size.y)
|
||||
)
|
||||
pos += grid_offset
|
||||
|
||||
|
|
|
@ -129,19 +129,20 @@ func draw_preview() -> void:
|
|||
func snap_position(pos: Vector2) -> Vector2:
|
||||
var snapping_distance := Global.snapping_distance / Global.camera.zoom.x
|
||||
if Global.snap_to_rectangular_grid_boundary:
|
||||
var grid_pos := pos.snapped(Global.grid_size)
|
||||
grid_pos += Vector2(Global.grid_offset)
|
||||
var grid_pos := pos.snapped(Global.grids[0].grid_size)
|
||||
grid_pos += Vector2(Global.grids[0].grid_offset)
|
||||
# keeping grid_pos as is would have been fine but this adds extra accuracy as to
|
||||
# which snap point (from the list below) is closest to mouse and occupy THAT point
|
||||
var t_l := grid_pos + Vector2(-Global.grid_size.x, -Global.grid_size.y)
|
||||
var t_c := grid_pos + Vector2(0, -Global.grid_size.y) # t_c is for "top centre" and so on
|
||||
var t_r := grid_pos + Vector2(Global.grid_size.x, -Global.grid_size.y)
|
||||
var m_l := grid_pos + Vector2(-Global.grid_size.x, 0)
|
||||
# t_l is for "top left" and so on
|
||||
var t_l := grid_pos + Vector2(-Global.grids[0].grid_size.x, -Global.grids[0].grid_size.y)
|
||||
var t_c := grid_pos + Vector2(0, -Global.grids[0].grid_size.y)
|
||||
var t_r := grid_pos + Vector2(Global.grids[0].grid_size.x, -Global.grids[0].grid_size.y)
|
||||
var m_l := grid_pos + Vector2(-Global.grids[0].grid_size.x, 0)
|
||||
var m_c := grid_pos
|
||||
var m_r := grid_pos + Vector2(Global.grid_size.x, 0)
|
||||
var b_l := grid_pos + Vector2(-Global.grid_size.x, Global.grid_size.y)
|
||||
var b_c := grid_pos + Vector2(0, Global.grid_size.y)
|
||||
var b_r := grid_pos + Vector2(Global.grid_size)
|
||||
var m_r := grid_pos + Vector2(Global.grids[0].grid_size.x, 0)
|
||||
var b_l := grid_pos + Vector2(-Global.grids[0].grid_size.x, Global.grids[0].grid_size.y)
|
||||
var b_c := grid_pos + Vector2(0, Global.grids[0].grid_size.y)
|
||||
var b_r := grid_pos + Vector2(Global.grids[0].grid_size)
|
||||
var vec_arr: PackedVector2Array = [t_l, t_c, t_r, m_l, m_c, m_r, b_l, b_c, b_r]
|
||||
for vec in vec_arr:
|
||||
if vec.distance_to(pos) < grid_pos.distance_to(pos):
|
||||
|
@ -152,19 +153,22 @@ func snap_position(pos: Vector2) -> Vector2:
|
|||
pos = grid_point.floor()
|
||||
|
||||
if Global.snap_to_rectangular_grid_center:
|
||||
var grid_center := pos.snapped(Global.grid_size) + Vector2(Global.grid_size / 2)
|
||||
grid_center += Vector2(Global.grid_offset)
|
||||
var grid_center := (
|
||||
pos.snapped(Global.grids[0].grid_size) + Vector2(Global.grids[0].grid_size / 2)
|
||||
)
|
||||
grid_center += Vector2(Global.grids[0].grid_offset)
|
||||
# keeping grid_center as is would have been fine but this adds extra accuracy as to
|
||||
# which snap point (from the list below) is closest to mouse and occupy THAT point
|
||||
var t_l := grid_center + Vector2(-Global.grid_size.x, -Global.grid_size.y)
|
||||
var t_c := grid_center + Vector2(0, -Global.grid_size.y) # t_c is for "top centre" and so on
|
||||
var t_r := grid_center + Vector2(Global.grid_size.x, -Global.grid_size.y)
|
||||
var m_l := grid_center + Vector2(-Global.grid_size.x, 0)
|
||||
# t_l is for "top left" and so on
|
||||
var t_l := grid_center + Vector2(-Global.grids[0].grid_size.x, -Global.grids[0].grid_size.y)
|
||||
var t_c := grid_center + Vector2(0, -Global.grids[0].grid_size.y)
|
||||
var t_r := grid_center + Vector2(Global.grids[0].grid_size.x, -Global.grids[0].grid_size.y)
|
||||
var m_l := grid_center + Vector2(-Global.grids[0].grid_size.x, 0)
|
||||
var m_c := grid_center
|
||||
var m_r := grid_center + Vector2(Global.grid_size.x, 0)
|
||||
var b_l := grid_center + Vector2(-Global.grid_size.x, Global.grid_size.y)
|
||||
var b_c := grid_center + Vector2(0, Global.grid_size.y)
|
||||
var b_r := grid_center + Vector2(Global.grid_size)
|
||||
var m_r := grid_center + Vector2(Global.grids[0].grid_size.x, 0)
|
||||
var b_l := grid_center + Vector2(-Global.grids[0].grid_size.x, Global.grids[0].grid_size.y)
|
||||
var b_c := grid_center + Vector2(0, Global.grids[0].grid_size.y)
|
||||
var b_r := grid_center + Vector2(Global.grids[0].grid_size)
|
||||
var vec_arr := [t_l, t_c, t_r, m_l, m_c, m_r, b_l, b_c, b_r]
|
||||
for vec in vec_arr:
|
||||
if vec.distance_to(pos) < grid_center.distance_to(pos):
|
||||
|
|
|
@ -16,17 +16,17 @@ func _input(event: InputEvent) -> void:
|
|||
return
|
||||
if event.is_action_pressed("transform_snap_grid"):
|
||||
_snap_to_grid = true
|
||||
_offset = _offset.snapped(Global.grid_size)
|
||||
_offset = _offset.snapped(Global.grids[0].grid_size)
|
||||
if Global.current_project.has_selection and selection_node.is_moving_content:
|
||||
var prev_pos: Vector2i = selection_node.big_bounding_rectangle.position
|
||||
selection_node.big_bounding_rectangle.position = Vector2i(
|
||||
prev_pos.snapped(Global.grid_size)
|
||||
prev_pos.snapped(Global.grids[0].grid_size)
|
||||
)
|
||||
# The first time transform_snap_grid is enabled then _snap_position() is not called
|
||||
# and the selection had wrong offset, so do selection offsetting here
|
||||
var grid_offset := Vector2i(
|
||||
fmod(Global.grid_offset.x, Global.grid_size.x),
|
||||
fmod(Global.grid_offset.y, Global.grid_size.y)
|
||||
fmod(Global.grids[0].grid_offset.x, Global.grids[0].grid_size.x),
|
||||
fmod(Global.grids[0].grid_offset.y, Global.grids[0].grid_size.y)
|
||||
)
|
||||
selection_node.big_bounding_rectangle.position += grid_offset
|
||||
selection_node.marching_ants_outline.offset += Vector2(
|
||||
|
@ -107,16 +107,18 @@ func _snap_position(pos: Vector2) -> Vector2:
|
|||
else:
|
||||
pos.x = _start_pos.x
|
||||
if _snap_to_grid: # Snap to grid
|
||||
pos = pos.snapped(Global.grid_size)
|
||||
pos = pos.snapped(Global.grids[0].grid_size)
|
||||
# The part below only corrects the offset for situations when there is no selection
|
||||
# Offsets when there is selection is controlled in _input() function
|
||||
if !Global.current_project.has_selection:
|
||||
var move_offset := Vector2.ZERO
|
||||
move_offset.x = (
|
||||
_start_pos.x - (_start_pos.x / Global.grid_size.x) * Global.grid_size.x
|
||||
_start_pos.x
|
||||
- (_start_pos.x / Global.grids[0].grid_size.x) * Global.grids[0].grid_size.x
|
||||
)
|
||||
move_offset.y = (
|
||||
_start_pos.y - (_start_pos.y / Global.grid_size.y) * Global.grid_size.y
|
||||
_start_pos.y
|
||||
- (_start_pos.y / Global.grids[0].grid_size.y) * Global.grids[0].grid_size.y
|
||||
)
|
||||
pos += move_offset
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
extends Node2D
|
||||
|
||||
var unique_rect_lines := PackedVector2Array()
|
||||
var unique_iso_lines := PackedVector2Array()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Global.project_switched.connect(queue_redraw)
|
||||
|
@ -10,54 +13,60 @@ func _draw() -> void:
|
|||
return
|
||||
|
||||
var target_rect: Rect2i
|
||||
if Global.grid_draw_over_tile_mode:
|
||||
target_rect = Global.current_project.tiles.get_bounding_rect()
|
||||
else:
|
||||
target_rect = Rect2i(Vector2i.ZERO, Global.current_project.size)
|
||||
if not target_rect.has_area():
|
||||
return
|
||||
unique_rect_lines.clear()
|
||||
unique_iso_lines.clear()
|
||||
for grid_idx in range(Global.grids.size() - 1, -1, -1):
|
||||
if Global.grids[grid_idx].grid_draw_over_tile_mode:
|
||||
target_rect = Global.current_project.tiles.get_bounding_rect()
|
||||
else:
|
||||
target_rect = Rect2i(Vector2i.ZERO, Global.current_project.size)
|
||||
if not target_rect.has_area():
|
||||
return
|
||||
|
||||
var grid_type := Global.grid_type
|
||||
if grid_type == Global.GridTypes.CARTESIAN || grid_type == Global.GridTypes.ALL:
|
||||
_draw_cartesian_grid(target_rect)
|
||||
var grid_type := Global.grids[grid_idx].grid_type
|
||||
if grid_type == Global.GridTypes.CARTESIAN || grid_type == Global.GridTypes.ALL:
|
||||
_draw_cartesian_grid(grid_idx, target_rect)
|
||||
|
||||
if grid_type == Global.GridTypes.ISOMETRIC || grid_type == Global.GridTypes.ALL:
|
||||
_draw_isometric_grid(target_rect)
|
||||
if grid_type == Global.GridTypes.ISOMETRIC || grid_type == Global.GridTypes.ALL:
|
||||
_draw_isometric_grid(grid_idx, target_rect)
|
||||
|
||||
|
||||
func _draw_cartesian_grid(target_rect: Rect2i) -> void:
|
||||
func _draw_cartesian_grid(grid_index: int, target_rect: Rect2i) -> void:
|
||||
var grid = Global.grids[grid_index]
|
||||
var grid_multiline_points := PackedVector2Array()
|
||||
|
||||
var x: float = (
|
||||
target_rect.position.x
|
||||
+ fposmod(Global.grid_offset.x - target_rect.position.x, Global.grid_size.x)
|
||||
+ fposmod(grid.grid_offset.x - target_rect.position.x, grid.grid_size.x)
|
||||
)
|
||||
while x <= target_rect.end.x:
|
||||
grid_multiline_points.push_back(Vector2(x, target_rect.position.y))
|
||||
grid_multiline_points.push_back(Vector2(x, target_rect.end.y))
|
||||
x += Global.grid_size.x
|
||||
if not Vector2(x, target_rect.position.y) in unique_rect_lines:
|
||||
grid_multiline_points.push_back(Vector2(x, target_rect.position.y))
|
||||
grid_multiline_points.push_back(Vector2(x, target_rect.end.y))
|
||||
x += grid.grid_size.x
|
||||
|
||||
var y: float = (
|
||||
target_rect.position.y
|
||||
+ fposmod(Global.grid_offset.y - target_rect.position.y, Global.grid_size.y)
|
||||
+ fposmod(grid.grid_offset.y - target_rect.position.y, grid.grid_size.y)
|
||||
)
|
||||
while y <= target_rect.end.y:
|
||||
grid_multiline_points.push_back(Vector2(target_rect.position.x, y))
|
||||
grid_multiline_points.push_back(Vector2(target_rect.end.x, y))
|
||||
y += Global.grid_size.y
|
||||
if not Vector2(target_rect.position.x, y) in unique_rect_lines:
|
||||
grid_multiline_points.push_back(Vector2(target_rect.position.x, y))
|
||||
grid_multiline_points.push_back(Vector2(target_rect.end.x, y))
|
||||
y += grid.grid_size.y
|
||||
|
||||
unique_rect_lines.append_array(grid_multiline_points)
|
||||
if not grid_multiline_points.is_empty():
|
||||
draw_multiline(grid_multiline_points, Global.grid_color)
|
||||
draw_multiline(grid_multiline_points, grid.grid_color)
|
||||
|
||||
|
||||
func _draw_isometric_grid(target_rect: Rect2i) -> void:
|
||||
func _draw_isometric_grid(grid_index: int, target_rect: Rect2i) -> void:
|
||||
var grid = Global.grids[grid_index]
|
||||
var grid_multiline_points := PackedVector2Array()
|
||||
|
||||
var cell_size: Vector2 = Global.isometric_grid_size
|
||||
var cell_size: Vector2 = grid.isometric_grid_size
|
||||
var max_cell_count: Vector2 = Vector2(target_rect.size) / cell_size
|
||||
var origin_offset: Vector2 = Vector2(Global.grid_offset - target_rect.position).posmodv(
|
||||
cell_size
|
||||
)
|
||||
var origin_offset: Vector2 = Vector2(grid.grid_offset - target_rect.position).posmodv(cell_size)
|
||||
|
||||
# lines ↗↗↗ (from bottom-left to top-right)
|
||||
var per_cell_offset: Vector2 = cell_size * Vector2(1, -1)
|
||||
|
@ -70,8 +79,9 @@ func _draw_isometric_grid(target_rect: Rect2i) -> void:
|
|||
var start: Vector2 = Vector2(target_rect.position) + Vector2(0, y)
|
||||
var cells_to_rect_bounds: float = minf(max_cell_count.x, y / cell_size.y)
|
||||
var end := start + cells_to_rect_bounds * per_cell_offset
|
||||
grid_multiline_points.push_back(start)
|
||||
grid_multiline_points.push_back(end)
|
||||
if not start in unique_iso_lines:
|
||||
grid_multiline_points.push_back(start)
|
||||
grid_multiline_points.push_back(end)
|
||||
y += cell_size.y
|
||||
|
||||
# lines ↗↗↗ starting from the rect's bottom side (left to right):
|
||||
|
@ -80,8 +90,9 @@ func _draw_isometric_grid(target_rect: Rect2i) -> void:
|
|||
var start: Vector2 = Vector2(target_rect.position) + Vector2(x, target_rect.size.y)
|
||||
var cells_to_rect_bounds: float = minf(max_cell_count.y, max_cell_count.x - x / cell_size.x)
|
||||
var end: Vector2 = start + cells_to_rect_bounds * per_cell_offset
|
||||
grid_multiline_points.push_back(start)
|
||||
grid_multiline_points.push_back(end)
|
||||
if not start in unique_iso_lines:
|
||||
grid_multiline_points.push_back(start)
|
||||
grid_multiline_points.push_back(end)
|
||||
x += cell_size.x
|
||||
|
||||
# lines ↘↘↘ (from top-left to bottom-right)
|
||||
|
@ -93,8 +104,9 @@ func _draw_isometric_grid(target_rect: Rect2i) -> void:
|
|||
var start: Vector2 = Vector2(target_rect.position) + Vector2(0, y)
|
||||
var cells_to_rect_bounds: float = minf(max_cell_count.x, max_cell_count.y - y / cell_size.y)
|
||||
var end: Vector2 = start + cells_to_rect_bounds * per_cell_offset
|
||||
grid_multiline_points.push_back(start)
|
||||
grid_multiline_points.push_back(end)
|
||||
if not start in unique_iso_lines:
|
||||
grid_multiline_points.push_back(start)
|
||||
grid_multiline_points.push_back(end)
|
||||
y += cell_size.y
|
||||
|
||||
# lines ↘↘↘ starting from the rect's top side (left to right):
|
||||
|
@ -103,9 +115,11 @@ func _draw_isometric_grid(target_rect: Rect2i) -> void:
|
|||
var start: Vector2 = Vector2(target_rect.position) + Vector2(x, 0)
|
||||
var cells_to_rect_bounds: float = minf(max_cell_count.y, max_cell_count.x - x / cell_size.x)
|
||||
var end: Vector2 = start + cells_to_rect_bounds * per_cell_offset
|
||||
grid_multiline_points.push_back(start)
|
||||
grid_multiline_points.push_back(end)
|
||||
if not start in unique_iso_lines:
|
||||
grid_multiline_points.push_back(start)
|
||||
grid_multiline_points.push_back(end)
|
||||
x += cell_size.x
|
||||
grid_multiline_points.append_array(grid_multiline_points)
|
||||
|
||||
if not grid_multiline_points.is_empty():
|
||||
draw_multiline(grid_multiline_points, Global.grid_color)
|
||||
draw_multiline(grid_multiline_points, grid.grid_color)
|
||||
|
|
|
@ -106,6 +106,11 @@ func _input(event: InputEvent) -> void:
|
|||
image_current_pixel = canvas.current_pixel
|
||||
if Global.mirror_view:
|
||||
image_current_pixel.x = Global.current_project.size.x - image_current_pixel.x
|
||||
if is_moving_content:
|
||||
if Input.is_action_just_pressed(&"transformation_confirm"):
|
||||
transform_content_confirm()
|
||||
elif Input.is_action_just_pressed(&"transformation_cancel"):
|
||||
transform_content_cancel()
|
||||
if not project.layers[project.current_layer].can_layer_get_drawn():
|
||||
return
|
||||
if event is InputEventKey:
|
||||
|
@ -209,7 +214,7 @@ func _move_with_arrow_keys(event: InputEvent) -> void:
|
|||
if _is_action_direction(event) and arrow_key_move:
|
||||
var step := Vector2.ONE
|
||||
if Input.is_key_pressed(KEY_CTRL):
|
||||
step = Global.grid_size
|
||||
step = Global.grids[0].grid_size
|
||||
var input := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
|
||||
var move := input.rotated(snappedf(Global.camera.rotation, PI / 2))
|
||||
# These checks are needed to fix a bug where the selection got stuck
|
||||
|
|
|
@ -3,7 +3,7 @@ extends Node2D
|
|||
var tiles: Tiles
|
||||
var draw_center := false
|
||||
|
||||
@onready var canvas := get_parent() as Canvas
|
||||
@onready var canvas := Global.canvas
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
|
|
|
@ -31,7 +31,7 @@ func commit_action(cel: Image, project := Global.current_project) -> void:
|
|||
var params := {
|
||||
"color": color,
|
||||
"width": anim_thickness,
|
||||
"pattern": pattern,
|
||||
"brush": pattern,
|
||||
"inside": inside_image,
|
||||
"selection": selection_tex
|
||||
}
|
||||
|
|
|
@ -51,16 +51,15 @@ size_flags_horizontal = 3
|
|||
[node name="PatternLabel" type="Label" parent="VBoxContainer/OutlineOptions" index="4"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Pattern:"
|
||||
text = "Brush:"
|
||||
|
||||
[node name="PatternOptionButton" type="OptionButton" parent="VBoxContainer/OutlineOptions" index="5"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_default_cursor_shape = 2
|
||||
item_count = 3
|
||||
selected = 0
|
||||
item_count = 3
|
||||
popup/item_0/text = "Diamond"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Circle"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Square"
|
||||
|
|
43
src/UI/Dialogs/ModifySelection.gd
Normal file
43
src/UI/Dialogs/ModifySelection.gd
Normal file
|
@ -0,0 +1,43 @@
|
|||
extends ConfirmationDialog
|
||||
|
||||
enum Types { EXPAND, SHRINK, BORDER }
|
||||
|
||||
@export var type := Types.EXPAND:
|
||||
set(value):
|
||||
type = value
|
||||
if type == Types.EXPAND:
|
||||
title = "Expand Selection"
|
||||
elif type == Types.SHRINK:
|
||||
title = "Shrink Selection"
|
||||
else:
|
||||
title = "Border Selection"
|
||||
|
||||
@onready var width_slider: ValueSlider = $GridContainer/WidthSlider
|
||||
@onready var brush_option_button: OptionButton = $GridContainer/BrushOptionButton
|
||||
@onready var selection_node := Global.canvas.selection
|
||||
|
||||
|
||||
func _on_visibility_changed() -> void:
|
||||
if not visible:
|
||||
Global.dialog_open(false)
|
||||
|
||||
|
||||
func _on_confirmed() -> void:
|
||||
var project := Global.current_project
|
||||
if !project.has_selection:
|
||||
return
|
||||
selection_node.transform_content_confirm()
|
||||
var undo_data_tmp := selection_node.get_undo_data(false)
|
||||
var width: int = width_slider.value
|
||||
var brush := brush_option_button.selected
|
||||
project.selection_map.crop(project.size.x, project.size.y)
|
||||
if type == Types.EXPAND:
|
||||
project.selection_map.expand(width, brush)
|
||||
elif type == Types.SHRINK:
|
||||
project.selection_map.shrink(width, brush)
|
||||
else:
|
||||
project.selection_map.border(width, brush)
|
||||
selection_node.big_bounding_rectangle = project.selection_map.get_used_rect()
|
||||
project.selection_offset = Vector2.ZERO
|
||||
selection_node.commit_undo("Modify Selection", undo_data_tmp)
|
||||
selection_node.queue_redraw()
|
60
src/UI/Dialogs/ModifySelection.tscn
Normal file
60
src/UI/Dialogs/ModifySelection.tscn
Normal file
|
@ -0,0 +1,60 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://wcbpnsm7gptu"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="1_3jelw"]
|
||||
[ext_resource type="Script" path="res://src/UI/Dialogs/ModifySelection.gd" id="1_w6rs7"]
|
||||
|
||||
[node name="ModifySelection" type="ConfirmationDialog"]
|
||||
title = "Expand selection"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(260, 130)
|
||||
script = ExtResource("1_w6rs7")
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="."]
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 252.0
|
||||
offset_bottom = 81.0
|
||||
columns = 2
|
||||
|
||||
[node name="WidthLabel" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Width:"
|
||||
|
||||
[node name="WidthSlider" type="TextureProgressBar" parent="GridContainer"]
|
||||
custom_minimum_size = Vector2(0, 24)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
theme_type_variation = &"ValueSlider"
|
||||
min_value = 1.0
|
||||
max_value = 25.0
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource("1_3jelw")
|
||||
|
||||
[node name="BrushLabel" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Brush:"
|
||||
|
||||
[node name="BrushOptionButton" type="OptionButton" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_default_cursor_shape = 2
|
||||
selected = 0
|
||||
item_count = 3
|
||||
popup/item_0/text = "Diamond"
|
||||
popup/item_1/text = "Circle"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Square"
|
||||
popup/item_2/id = 2
|
||||
|
||||
[connection signal="confirmed" from="." to="." method="_on_confirmed"]
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
|
|
@ -1,11 +1,12 @@
|
|||
extends ConfirmationDialog
|
||||
|
||||
@onready var x_basis_x_spinbox: SpinBox = $VBoxContainer/HBoxContainer/OptionsContainer/XBasisX
|
||||
@onready var x_basis_y_spinbox: SpinBox = $VBoxContainer/HBoxContainer/OptionsContainer/XBasisY
|
||||
@onready var y_basis_x_spinbox: SpinBox = $VBoxContainer/HBoxContainer/OptionsContainer/YBasisX
|
||||
@onready var y_basis_y_spinbox: SpinBox = $VBoxContainer/HBoxContainer/OptionsContainer/YBasisY
|
||||
@onready var x_basis_label: Label = $VBoxContainer/OptionsContainer/XBasisLabel
|
||||
@onready var x_basis: ValueSliderV2 = $VBoxContainer/OptionsContainer/XBasis
|
||||
@onready var y_basis_label: Label = $VBoxContainer/OptionsContainer/YBasisLabel
|
||||
@onready var y_basis: ValueSliderV2 = $VBoxContainer/OptionsContainer/YBasis
|
||||
@onready var preview_rect: Control = $VBoxContainer/AspectRatioContainer/Preview
|
||||
@onready var tile_mode: Node2D = $VBoxContainer/AspectRatioContainer/Preview/TileMode
|
||||
@onready var masking: CheckButton = $VBoxContainer/OptionsContainer/Masking
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
@ -37,35 +38,25 @@ func _on_TileModeOffsetsDialog_about_to_show() -> void:
|
|||
tile_mode.tiles.mode = Global.current_project.tiles.mode
|
||||
tile_mode.tiles.x_basis = Global.current_project.tiles.x_basis
|
||||
tile_mode.tiles.y_basis = Global.current_project.tiles.y_basis
|
||||
x_basis_x_spinbox.value = tile_mode.tiles.x_basis.x
|
||||
x_basis_y_spinbox.value = tile_mode.tiles.x_basis.y
|
||||
y_basis_x_spinbox.value = tile_mode.tiles.y_basis.x
|
||||
y_basis_y_spinbox.value = tile_mode.tiles.y_basis.y
|
||||
x_basis.value = tile_mode.tiles.x_basis
|
||||
y_basis.value = tile_mode.tiles.y_basis
|
||||
|
||||
_show_options()
|
||||
if Global.current_project.tiles.mode == Tiles.MODE.X_AXIS:
|
||||
y_basis_x_spinbox.visible = false
|
||||
y_basis_y_spinbox.visible = false
|
||||
$VBoxContainer/HBoxContainer/OptionsContainer/YBasisXLabel.visible = false
|
||||
$VBoxContainer/HBoxContainer/OptionsContainer/YBasisYLabel.visible = false
|
||||
y_basis.visible = false
|
||||
y_basis_label.visible = false
|
||||
elif Global.current_project.tiles.mode == Tiles.MODE.Y_AXIS:
|
||||
x_basis_x_spinbox.visible = false
|
||||
x_basis_y_spinbox.visible = false
|
||||
$VBoxContainer/HBoxContainer/OptionsContainer/XBasisXLabel.visible = false
|
||||
$VBoxContainer/HBoxContainer/OptionsContainer/XBasisYLabel.visible = false
|
||||
x_basis.visible = false
|
||||
x_basis_label.visible = false
|
||||
|
||||
update_preview()
|
||||
|
||||
|
||||
func _show_options() -> void:
|
||||
x_basis_x_spinbox.visible = true
|
||||
x_basis_y_spinbox.visible = true
|
||||
y_basis_x_spinbox.visible = true
|
||||
y_basis_y_spinbox.visible = true
|
||||
$VBoxContainer/HBoxContainer/OptionsContainer/YBasisXLabel.visible = true
|
||||
$VBoxContainer/HBoxContainer/OptionsContainer/YBasisYLabel.visible = true
|
||||
$VBoxContainer/HBoxContainer/OptionsContainer/XBasisXLabel.visible = true
|
||||
$VBoxContainer/HBoxContainer/OptionsContainer/XBasisYLabel.visible = true
|
||||
x_basis.visible = true
|
||||
y_basis.visible = true
|
||||
x_basis_label.visible = true
|
||||
y_basis_label.visible = true
|
||||
|
||||
|
||||
func _on_TileModeOffsetsDialog_confirmed() -> void:
|
||||
|
@ -75,23 +66,13 @@ func _on_TileModeOffsetsDialog_confirmed() -> void:
|
|||
Global.transparent_checker.update_rect()
|
||||
|
||||
|
||||
func _on_XBasisX_value_changed(value: int) -> void:
|
||||
tile_mode.tiles.x_basis.x = value
|
||||
func _on_x_basis_value_changed(value: Vector2) -> void:
|
||||
tile_mode.tiles.x_basis = value
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_XBasisY_value_changed(value: int) -> void:
|
||||
tile_mode.tiles.x_basis.y = value
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_YBasisX_value_changed(value: int) -> void:
|
||||
tile_mode.tiles.y_basis.x = value
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_YBasisY_value_changed(value: int) -> void:
|
||||
tile_mode.tiles.y_basis.y = value
|
||||
func _on_y_basis_value_changed(value: Vector2) -> void:
|
||||
tile_mode.tiles.y_basis = value
|
||||
update_preview()
|
||||
|
||||
|
||||
|
@ -122,10 +103,17 @@ func _on_TileModeOffsetsDialog_size_changed() -> void:
|
|||
func _on_Reset_pressed() -> void:
|
||||
tile_mode.tiles.x_basis = Vector2i(Global.current_project.size.x, 0)
|
||||
tile_mode.tiles.y_basis = Vector2i(0, Global.current_project.size.y)
|
||||
x_basis_x_spinbox.value = Global.current_project.size.x
|
||||
x_basis_y_spinbox.value = 0
|
||||
y_basis_x_spinbox.value = 0
|
||||
y_basis_y_spinbox.value = Global.current_project.size.y
|
||||
x_basis.value = tile_mode.tiles.x_basis
|
||||
y_basis.value = tile_mode.tiles.y_basis
|
||||
update_preview()
|
||||
|
||||
|
||||
func _on_isometric_pressed() -> void:
|
||||
tile_mode.tiles.x_basis = Global.current_project.size / 2
|
||||
tile_mode.tiles.x_basis.y *= -1
|
||||
tile_mode.tiles.y_basis = Global.current_project.size / 2
|
||||
x_basis.value = tile_mode.tiles.x_basis
|
||||
y_basis.value = tile_mode.tiles.y_basis
|
||||
update_preview()
|
||||
|
||||
|
||||
|
@ -138,10 +126,7 @@ func change_mask() -> void:
|
|||
var tiles_size := tiles.tile_size
|
||||
var image := Image.create(tiles_size.x, tiles_size.y, false, Image.FORMAT_RGBA8)
|
||||
DrawingAlgos.blend_layers(image, current_frame)
|
||||
if (
|
||||
image.get_used_rect().size == Vector2i.ZERO
|
||||
or not $VBoxContainer/HBoxContainer/Masking.button_pressed
|
||||
):
|
||||
if image.get_used_rect().size == Vector2i.ZERO or not masking.button_pressed:
|
||||
tiles.reset_mask()
|
||||
else:
|
||||
load_mask(image)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://c0nuukjakmai2"]
|
||||
[gd_scene load_steps=6 format=3 uid="uid://c0nuukjakmai2"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://3pmb60gpst7b" path="res://src/UI/Nodes/TransparentChecker.tscn" id="1"]
|
||||
[ext_resource type="Script" path="res://src/UI/Canvas/TileMode.gd" id="2"]
|
||||
[ext_resource type="PackedScene" path="res://src/UI/Nodes/ValueSliderV2.tscn" id="2_ul2eq"]
|
||||
[ext_resource type="Script" path="res://src/UI/Dialogs/TileModeOffsetsDialog.gd" id="3"]
|
||||
|
||||
[sub_resource type="CanvasItemMaterial" id="1"]
|
||||
|
@ -10,83 +11,72 @@ blend_mode = 4
|
|||
[node name="TileModeOffsetsDialog" type="ConfirmationDialog"]
|
||||
canvas_item_default_texture_filter = 0
|
||||
title = "Tile Mode Offsets"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(298, 536)
|
||||
script = ExtResource("3")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 293.0
|
||||
offset_bottom = 386.0
|
||||
offset_right = 290.0
|
||||
offset_bottom = 487.0
|
||||
|
||||
[node name="TileModeOffsets" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Tile Mode Offsets"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="OptionsContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer"]
|
||||
[node name="OptionsContainer" type="GridContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 2
|
||||
theme_override_constants/v_separation = 4
|
||||
columns = 2
|
||||
|
||||
[node name="XBasisXLabel" type="Label" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
[node name="XBasisLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
text = "X-basis x:"
|
||||
size_flags_horizontal = 3
|
||||
text = "X-basis:"
|
||||
|
||||
[node name="XBasisX" type="SpinBox" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
[node name="XBasis" parent="VBoxContainer/OptionsContainer" instance=ExtResource("2_ul2eq")]
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = -16384.0
|
||||
max_value = 16384.0
|
||||
suffix = "px"
|
||||
size_flags_horizontal = 3
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
suffix_x = "px"
|
||||
suffix_y = "px"
|
||||
|
||||
[node name="XBasisYLabel" type="Label" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
[node name="YBasisLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
text = "X-basis y:"
|
||||
size_flags_horizontal = 3
|
||||
text = "Y-basis:"
|
||||
|
||||
[node name="XBasisY" type="SpinBox" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
[node name="YBasis" parent="VBoxContainer/OptionsContainer" instance=ExtResource("2_ul2eq")]
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = -16384.0
|
||||
max_value = 16384.0
|
||||
suffix = "px"
|
||||
size_flags_horizontal = 3
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
suffix_x = "px"
|
||||
suffix_y = "px"
|
||||
|
||||
[node name="YBasisXLabel" type="Label" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
[node name="MaskingLabel" type="Label" parent="VBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
text = "Y-basis x:"
|
||||
text = "Masking:"
|
||||
|
||||
[node name="YBasisX" type="SpinBox" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = -16384.0
|
||||
max_value = 16384.0
|
||||
suffix = "px"
|
||||
|
||||
[node name="YBasisYLabel" type="Label" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
text = "Y-basis y:"
|
||||
|
||||
[node name="YBasisY" type="SpinBox" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
min_value = -16384.0
|
||||
max_value = 16384.0
|
||||
suffix = "px"
|
||||
|
||||
[node name="Reset" type="Button" parent="VBoxContainer/HBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Reset"
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Masking" type="CheckButton" parent="VBoxContainer/HBoxContainer"]
|
||||
[node name="Masking" type="CheckButton" parent="VBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
text = "Masking"
|
||||
|
||||
[node name="Reset" type="Button" parent="VBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Default"
|
||||
|
||||
[node name="Isometric" type="Button" parent="VBoxContainer/OptionsContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Isometric"
|
||||
|
||||
[node name="AspectRatioContainer" type="AspectRatioContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
@ -111,9 +101,8 @@ anchor_bottom = 1.0
|
|||
[connection signal="confirmed" from="." to="." method="_on_TileModeOffsetsDialog_confirmed"]
|
||||
[connection signal="size_changed" from="." to="." method="_on_TileModeOffsetsDialog_size_changed"]
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_TileModeOffsetsDialog_visibility_changed"]
|
||||
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/OptionsContainer/XBasisX" to="." method="_on_XBasisX_value_changed"]
|
||||
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/OptionsContainer/XBasisY" to="." method="_on_XBasisY_value_changed"]
|
||||
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/OptionsContainer/YBasisX" to="." method="_on_YBasisX_value_changed"]
|
||||
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/OptionsContainer/YBasisY" to="." method="_on_YBasisY_value_changed"]
|
||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/OptionsContainer/Reset" to="." method="_on_Reset_pressed"]
|
||||
[connection signal="toggled" from="VBoxContainer/HBoxContainer/Masking" to="." method="_on_Masking_toggled"]
|
||||
[connection signal="value_changed" from="VBoxContainer/OptionsContainer/XBasis" to="." method="_on_x_basis_value_changed"]
|
||||
[connection signal="value_changed" from="VBoxContainer/OptionsContainer/YBasis" to="." method="_on_y_basis_value_changed"]
|
||||
[connection signal="toggled" from="VBoxContainer/OptionsContainer/Masking" to="." method="_on_Masking_toggled"]
|
||||
[connection signal="pressed" from="VBoxContainer/OptionsContainer/Reset" to="." method="_on_Reset_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/OptionsContainer/Isometric" to="." method="_on_isometric_pressed"]
|
||||
|
|
|
@ -80,15 +80,21 @@ func _notification(what: int) -> void:
|
|||
_reset_display(false)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if not editable or not is_visible_in_tree():
|
||||
return
|
||||
if event.is_action_pressed(global_increment_action, true):
|
||||
if (
|
||||
not global_increment_action.is_empty()
|
||||
and event.is_action_pressed(global_increment_action, true)
|
||||
):
|
||||
if snap_by_default:
|
||||
value += step if event.ctrl_pressed else snap_step
|
||||
else:
|
||||
value += snap_step if event.ctrl_pressed else step
|
||||
elif event.is_action_pressed(global_decrement_action, true):
|
||||
elif (
|
||||
not global_decrement_action.is_empty()
|
||||
and event.is_action_pressed(global_decrement_action, true)
|
||||
):
|
||||
if snap_by_default:
|
||||
value -= step if event.ctrl_pressed else snap_step
|
||||
else:
|
||||
|
@ -108,11 +114,13 @@ func _gui_input(event: InputEvent) -> void:
|
|||
value += step if event.ctrl_pressed else snap_step
|
||||
else:
|
||||
value += snap_step if event.ctrl_pressed else step
|
||||
get_viewport().set_input_as_handled()
|
||||
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
||||
if snap_by_default:
|
||||
value -= step if event.ctrl_pressed else snap_step
|
||||
else:
|
||||
value -= snap_step if event.ctrl_pressed else step
|
||||
get_viewport().set_input_as_handled()
|
||||
elif state == HELD:
|
||||
if event.is_action_released("left_mouse"):
|
||||
state = TYPING
|
||||
|
|
|
@ -85,6 +85,9 @@ class Recorder:
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
if OS.get_name() == "Web":
|
||||
ExtensionsApi.panel.remove_node_from_tab.call_deferred(self)
|
||||
return
|
||||
Global.project_switched.connect(_on_project_switched)
|
||||
# Make a recordings folder if there isn't one
|
||||
chosen_dir = Global.home_data_directory.path_join("Recordings")
|
||||
|
|
|
@ -141,6 +141,7 @@ func _input(event: InputEvent) -> void:
|
|||
if timeline_rect.has_point(mouse_pos):
|
||||
if Input.is_key_pressed(KEY_CTRL):
|
||||
cel_size += (2 * int(event.is_action("zoom_in")) - 2 * int(event.is_action("zoom_out")))
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func reset_settings() -> void:
|
||||
|
|
|
@ -17,6 +17,7 @@ var zen_mode := false
|
|||
var new_image_dialog := Dialog.new("res://src/UI/Dialogs/CreateNewImage.tscn")
|
||||
var project_properties_dialog := Dialog.new("res://src/UI/Dialogs/ProjectProperties.tscn")
|
||||
var preferences_dialog := Dialog.new("res://src/Preferences/PreferencesDialog.tscn")
|
||||
var modify_selection := Dialog.new("res://src/UI/Dialogs/ModifySelection.tscn")
|
||||
var offset_image_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/OffsetImage.tscn")
|
||||
var scale_image_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/ScaleImage.tscn")
|
||||
var resize_canvas_dialog := Dialog.new("res://src/UI/Dialogs/ImageEffects/ResizeCanvas.tscn")
|
||||
|
@ -54,6 +55,7 @@ var about_dialog := Dialog.new("res://src/UI/Dialogs/AboutDialog.tscn")
|
|||
|
||||
@onready var greyscale_vision: ColorRect = main_ui.find_child("GreyscaleVision")
|
||||
@onready var tile_mode_submenu := PopupMenu.new()
|
||||
@onready var selection_modify_submenu := PopupMenu.new()
|
||||
@onready var snap_to_submenu := PopupMenu.new()
|
||||
@onready var panels_submenu := PopupMenu.new()
|
||||
@onready var layouts_submenu := PopupMenu.new()
|
||||
|
@ -440,17 +442,30 @@ func _setup_select_menu() -> void:
|
|||
"All": "select_all",
|
||||
"Clear": "clear_selection",
|
||||
"Invert": "invert_selection",
|
||||
"Tile Mode": ""
|
||||
"Tile Mode": "",
|
||||
"Modify": ""
|
||||
}
|
||||
for i in select_menu_items.size():
|
||||
var item: String = select_menu_items.keys()[i]
|
||||
if item == "Tile Mode":
|
||||
select_menu.add_check_item(item, i)
|
||||
elif item == "Modify":
|
||||
_setup_selection_modify_submenu(item)
|
||||
else:
|
||||
_set_menu_shortcut(select_menu_items[item], select_menu, i, item)
|
||||
select_menu.id_pressed.connect(select_menu_id_pressed)
|
||||
|
||||
|
||||
func _setup_selection_modify_submenu(item: String) -> void:
|
||||
selection_modify_submenu.set_name("selection_modify_submenu")
|
||||
selection_modify_submenu.add_item("Expand")
|
||||
selection_modify_submenu.add_item("Shrink")
|
||||
selection_modify_submenu.add_item("Border")
|
||||
selection_modify_submenu.id_pressed.connect(_selection_modify_submenu_id_pressed)
|
||||
select_menu.add_child(selection_modify_submenu)
|
||||
select_menu.add_submenu_item(item, selection_modify_submenu.get_name())
|
||||
|
||||
|
||||
func _setup_help_menu() -> void:
|
||||
# Order as in Global.HelpMenu enum
|
||||
var help_menu_items := {
|
||||
|
@ -667,6 +682,11 @@ func _tile_mode_submenu_id_pressed(id: Tiles.MODE) -> void:
|
|||
get_tree().current_scene.tile_mode_offsets_dialog.change_mask()
|
||||
|
||||
|
||||
func _selection_modify_submenu_id_pressed(id: int) -> void:
|
||||
modify_selection.popup()
|
||||
modify_selection.node.type = id
|
||||
|
||||
|
||||
func _snap_to_submenu_id_pressed(id: int) -> void:
|
||||
if id == 0:
|
||||
Global.snap_to_rectangular_grid_boundary = !Global.snap_to_rectangular_grid_boundary
|
||||
|
|
Loading…
Reference in a new issue