mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-14 17:53:09 +00:00
Compare commits
23 commits
a772a41158
...
bb3fd7f6c8
Author | SHA1 | Date | |
---|---|---|---|
|
bb3fd7f6c8 | ||
|
7f4c7a6bf1 | ||
|
41ea287df4 | ||
|
a3e372c5d8 | ||
|
6224d06428 | ||
|
6459151549 | ||
|
fe6efb0f1d | ||
|
8b1367494d | ||
|
01b55aca07 | ||
|
5f53a3eb7b | ||
|
658477ed4b | ||
|
3fb8484ac5 | ||
|
0484b1012f | ||
|
b87a8e2ab8 | ||
|
e6c4a72158 | ||
|
1dcb696c35 | ||
|
d580523c6e | ||
|
11da07b9ac | ||
|
7cf87ac142 | ||
|
bd7d3b19cc | ||
|
996a234d0d | ||
|
77f6bcf07b | ||
|
5bfe44a202 |
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -4,6 +4,28 @@ 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.1] - 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
|
||||
- Indexed mode has finally been implemented! [#1136](https://github.com/Orama-Interactive/Pixelorama/pull/1136)
|
||||
- Added a new text tool. Destructive only for now, meaning that once the text is confirmed, it cannot be changed later. [#1134](https://github.com/Orama-Interactive/Pixelorama/pull/1134)
|
||||
- Implemented support for multiple grids. [#1122](https://github.com/Orama-Interactive/Pixelorama/pull/1122)
|
||||
|
||||
### Changed
|
||||
- System font names are now sorted by alphabetical order.
|
||||
|
||||
### Fixed
|
||||
- Fixed crash when Pixelorama starts without a palette.
|
||||
- Undo/redo now works again when the cursor is hovering over the timeline.
|
||||
- Palette swatches now get deleted when the user removes all palettes
|
||||
- Fixed the Palettize effect and palette exporting to images storing slightly wrong color values. [77f6bcf](https://github.com/Orama-Interactive/Pixelorama/commit/77f6bcf07bd80bc042e478bb883d05900cebe436)
|
||||
- Fixed some issues with the Palettize effect where the output would be different if the palette size changed and empty swatches were added, even if the colors themselves stayed the same. Initially fixed by [bd7d3b1](https://github.com/Orama-Interactive/Pixelorama/commit/bd7d3b19cc98804e9b99754153c4d553d2048ee3), but [1dcb696](https://github.com/Orama-Interactive/Pixelorama/commit/1dcb696c35121f8208bde699f87bb75deff99d13) is the proper fix.
|
||||
- Fixed recorder label not updating when project is changed. [#1139](https://github.com/Orama-Interactive/Pixelorama/pull/1139)
|
||||
|
||||
## [v1.0.5] - 2024-11-18
|
||||
This update has been brought to you by the contributions of:
|
||||
Fayez Akhtar ([@Variable-ind](https://github.com/Variable-ind))
|
||||
|
|
|
@ -3,16 +3,23 @@ Name=Pixelorama
|
|||
GenericName=2D sprite editor
|
||||
GenericName[el]=Επεξεργαστής δισδιάστατων εικόνων
|
||||
GenericName[fr]=Éditeur de sprites 2D
|
||||
GenericName[ru]=2Д редактор спрайтов
|
||||
GenericName[pt_BR]=Editor de sprites 2D
|
||||
GenericName[uk]=2Д редактор спрайтів
|
||||
GenericName[zh_CN]=2D 精灵编辑器
|
||||
Comment=Create and edit static or animated 2D sprites
|
||||
Comment[el]=Δημιουργήστε και επεξεργαστείτε στατικές ή κινούμενες δισδιάστατες εικόνες
|
||||
Comment[fr]=Créez et modifiez des sprites 2D statiques ou animées
|
||||
Comment[ru]=Создавайте и редактируйте статичные и анимированные 2Д спрайты
|
||||
Comment[pt_BR]=Crie e edite sprites 2D estáticos ou animados
|
||||
Comment[uk]=Створюйте та редагуйте статичні та анімовані 2Д спрайти
|
||||
Comment[zh_CN]=创建并编辑 2D 精灵图片或动画
|
||||
Exec=pixelorama
|
||||
Icon=pixelorama
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Graphics;2DGraphics;RasterGraphics;
|
||||
Keywords=pixel;retro;animation;art;image;2d;sprite;graphics;drawing;editor;
|
||||
Keywords[ru]=pixel;retro;animation;art;image;2d;sprite;graphics;drawing;editor;пиксель;ретро;анимация;арт;изображение;2д;спрайт;графика;рисование;редактор;
|
||||
Keywords[uk]=pixel;retro;animation;art;image;2d;sprite;graphics;drawing;editor;піксель;ретро;анімація;арт;зображення;2д;спрайт;графіка;малювання;редактор;
|
||||
MimeType=image/pxo;image/png;image/bmp;image/vnd.radiance;image/jpeg;image/svg+xml;image/x-tga;image/webp;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 374 B |
Binary file not shown.
Before Width: | Height: | Size: 162 B After Width: | Height: | Size: 136 B |
264
installer/pixelorama pl.nsi
Normal file
264
installer/pixelorama pl.nsi
Normal file
|
@ -0,0 +1,264 @@
|
|||
; Pixelorama Installer NSIS Script
|
||||
; Copyright Xenofon Konitsas (huskee) 2021
|
||||
; Licensed under the MIT License
|
||||
|
||||
|
||||
; Helper variables so that we don't change 20 instances of the version for every update
|
||||
|
||||
!define APPNAME "Pixelorama"
|
||||
!define APPVERSION "v0.11.3"
|
||||
!define COMPANYNAME "Orama Interactive"
|
||||
|
||||
|
||||
; Include the Modern UI library
|
||||
|
||||
!include "MUI2.nsh"
|
||||
!include "x64.nsh"
|
||||
|
||||
|
||||
; Basic Installer Info
|
||||
|
||||
Name "${APPNAME} ${APPVERSION}"
|
||||
OutFile "${APPNAME}_${APPVERSION}_setup.exe"
|
||||
Unicode True
|
||||
|
||||
|
||||
; Default installation folder
|
||||
|
||||
InstallDir "$APPDATA\${COMPANYNAME}\${APPNAME}"
|
||||
|
||||
|
||||
; Get installation folder from registry if available
|
||||
|
||||
InstallDirRegKey HKCU "Software\${COMPANYNAME}\${APPNAME}" "InstallDir"
|
||||
|
||||
|
||||
; Request application privileges for Vista and later
|
||||
|
||||
RequestExecutionLevel admin
|
||||
|
||||
|
||||
; Interface Settings
|
||||
|
||||
!define MUI_ICON "assets\pixel-install.ico"
|
||||
!define MUI_UNICON "assets\pixel-uninstall.ico"
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "assets\wizard.bmp"
|
||||
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "assets\wizard.bmp"
|
||||
!define MUI_HEADERIMAGE
|
||||
!define MUI_HEADERIMAGE_RIGHT
|
||||
!define MUI_HEADERIMAGE_BITMAP "assets\header.bmp"
|
||||
!define MUI_HEADERIMAGE_UNBITMAP "assets\header.bmp"
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_COMPONENTSPAGE_SMALLDESC
|
||||
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\pixelorama.exe"
|
||||
|
||||
; Language selection settings
|
||||
|
||||
!define MUI_LANGDLL_ALLLANGUAGES
|
||||
## Remember the installer language
|
||||
!define MUI_LANGDLL_REGISTRY_ROOT HKCU
|
||||
!define MUI_LANGDLL_REGISTRY_KEY "Software\${COMPANYNAME}\${APPNAME}"
|
||||
!define MUI_LANGDLL_REGISTRY_VALUENAME "Język instalatora"
|
||||
|
||||
|
||||
; Installer pages
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_LICENSE "Licencja"
|
||||
!insertmacro MUI_PAGE_COMPONENTS
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_UNPAGE_WELCOME
|
||||
!insertmacro MUI_UNPAGE_COMPONENTS
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
!insertmacro MUI_UNPAGE_FINISH
|
||||
|
||||
|
||||
; Multilingual support
|
||||
|
||||
!insertmacro MUI_LANGUAGE "Polski"
|
||||
;@INSERT_TRANSLATIONS@
|
||||
|
||||
|
||||
; Assign language strings to installer/uninstaller section names
|
||||
|
||||
LangString SecInstall ${LANG_ENGLISH} "Zainstaluj ${APPNAME}"
|
||||
LangString SecStartmenu ${LANG_ENGLISH} "Utwórz skróty w menu Start (opcjonalnie)"
|
||||
LangString SecDesktop ${LANG_ENGLISH} "Utwórz skrót na pulpicie (opcjonalnie)"
|
||||
LangString un.SecUninstall ${LANG_ENGLISH} "Odinstaluj ${APPNAME} ${APPVERSION}"
|
||||
LangString un.SecConfig ${LANG_ENGLISH} "Usuń pliki konfiguracyjne (opcjonalnie)"
|
||||
|
||||
|
||||
; Installer sections
|
||||
|
||||
Section "$(SecInstall)" SecInstall ; Main install section
|
||||
|
||||
SectionIn RO ; Non optional section
|
||||
|
||||
; Set the installation folder as the output directory
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
; Copy all files to install directory
|
||||
${If} ${RunningX64}
|
||||
File "..\build\windows-64bit\pixelorama.exe"
|
||||
File "..\build\windows-64bit\pixelorama.pck"
|
||||
${Else}
|
||||
File "..\build\windows-32bit\pixelorama.exe"
|
||||
File "..\build\windows-32bit\pixelorama.pck"
|
||||
${EndIf}
|
||||
File "..\assets\graphics\icons\pxo.ico"
|
||||
|
||||
SetOutPath "$INSTDIR\pixelorama_data"
|
||||
File /nonfatal /r "..\build\pixelorama_data\*"
|
||||
|
||||
; Store installation folder in the registry
|
||||
WriteRegStr HKCU "Software\${COMPANYNAME}\${APPNAME}" "InstallDir" $INSTDIR
|
||||
|
||||
; Create uninstaller
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
; Create Add/Remove Programs entry
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"DisplayName" "${APPNAME}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"UninstallString" "$INSTDIR\uninstall.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"DisplayIcon" "$INSTDIR\pixelorama.exe,0"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"InstallLocation" "$INSTDIR"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"Publisher" "${COMPANYNAME}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"HelpLink" "https://orama-interactive.github.io/Pixelorama-Docs"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"DisplayVersion" "${APPVERSION}"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" \
|
||||
"NoRepair" 1
|
||||
|
||||
; Associate .pxo files with Pixelorama
|
||||
WriteRegStr HKCR ".pxo" "" "Pixelorama project"
|
||||
WriteRegStr HKCR ".pxo" "ContentType" "image/pixelorama"
|
||||
WriteRegStr HKCR ".pxo" "PerceivedType" "document"
|
||||
|
||||
WriteRegStr HKCR "Pixelorama project" "" "Pixelorama project"
|
||||
WriteRegStr HKCR "Pixelorama project\shell" "" "open"
|
||||
WriteRegStr HKCR "Pixelorama project\DefaultIcon" "" "$INSTDIR\pxo.ico"
|
||||
|
||||
WriteRegStr HKCR "Pixelorama project\shell\open\command" "" '$INSTDIR\${APPNAME}.exe "%1"'
|
||||
WriteRegStr HKCR "Pixelorama project\shell\edit" "" "Edytuj projekt w ${APPNAME}"
|
||||
WriteRegStr HKCR "Pixelorama project\shell\edit\command" "" '$INSTDIR\${APPNAME}.exe "%1"'
|
||||
SectionEnd
|
||||
|
||||
|
||||
Section /o "$(SecStartmenu)" SecStartmenu ; Create Start Menu shortcuts
|
||||
|
||||
; Create folder in Start Menu\Programs and create shortcuts for app and uninstaller
|
||||
CreateDirectory "$SMPROGRAMS\${COMPANYNAME}"
|
||||
|
||||
CreateShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME} ${APPVERSION}.lnk" "$INSTDIR\Pixelorama.exe"
|
||||
CreateShortCut "$SMPROGRAMS\${COMPANYNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
|
||||
SectionEnd
|
||||
|
||||
|
||||
Section /o "$(SecDesktop)" SecDesktop ; Create Desktop shortcut
|
||||
|
||||
; Create shortcut for app on desktop
|
||||
CreateShortCut "$DESKTOP\${APPNAME} ${APPVERSION}.lnk" "$INSTDIR\Pixelorama.exe"
|
||||
|
||||
SectionEnd
|
||||
|
||||
|
||||
; Installer functions
|
||||
|
||||
Function .onInit
|
||||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
; Uninstaller sections
|
||||
|
||||
Section "un.$(un.SecUninstall)" un.SecUninstall ; Main uninstall section
|
||||
|
||||
SectionIn RO
|
||||
|
||||
; Delete all files and folders created by the installer
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
Delete "$INSTDIR\Pixelorama.exe"
|
||||
Delete "$INSTDIR\Pixelorama.pck"
|
||||
Delete "$INSTDIR\pxo.ico"
|
||||
RMDir /r "$INSTDIR\pixelorama_data"
|
||||
RMDir "$INSTDIR"
|
||||
|
||||
; Delete shortcuts
|
||||
RMDir /r "$SMPROGRAMS\${COMPANYNAME}"
|
||||
Delete "$DESKTOP\${APPNAME} ${APPVERSION}.lnk"
|
||||
|
||||
; Delete the install folder
|
||||
SetOutPath "$APPDATA"
|
||||
RMDir /r "${COMPANYNAME}"
|
||||
|
||||
; If empty, delete the application's registry key
|
||||
DeleteRegKey /ifempty HKCU "Software\${COMPANYNAME}\${APPNAME}"
|
||||
|
||||
; Delete the Add/Remove Programs entry
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
|
||||
|
||||
; Delete the .pxo file association
|
||||
DeleteRegKey HKCR "Pixelorama project"
|
||||
DeleteRegKey HKCR ".pxo"
|
||||
|
||||
SectionEnd
|
||||
|
||||
|
||||
Section "un.$(un.SecConfig)" un.SecConfig ; Configuration removal section
|
||||
|
||||
; Delete the application's settings file
|
||||
Delete "$APPDATA\Godot\app_userdata\${APPNAME}\cache.ini"
|
||||
|
||||
SectionEnd
|
||||
|
||||
; Uninstaller functions
|
||||
|
||||
Function un.onInit
|
||||
!insertmacro MUI_UNGETLANGUAGE
|
||||
|
||||
FunctionEnd
|
||||
|
||||
|
||||
; Section description language strings for multilingual support
|
||||
|
||||
LangString DESC_SecInstall ${LANG_ENGLISH} "Instalowanie ${APPNAME} ${APPVERSION}."
|
||||
LangString DESC_SecStartmenu ${LANG_ENGLISH} "Tworzenie skrótów w menu Start ${APPNAME}."
|
||||
LangString DESC_SecDesktop ${LANG_ENGLISH} "Tworzenie skrótu na pulpicie dla ${APPNAME}."
|
||||
LangString DESC_un.SecUninstall ${LANG_ENGLISH} "Odinstalowywanie ${APPNAME} ${APPVERSION} i usuwanie wszystkich skrótów."
|
||||
LangString DESC_un.SecConfig ${LANG_ENGLISH} "Usuwanie plików konfiguracyjnych ${APPNAME}."
|
||||
|
||||
|
||||
; Assign language strings to installer/uninstaller descriptions
|
||||
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecStartmenu} $(DESC_SecStartmenu)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecDesktop} $(DESC_SecDesktop)
|
||||
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
|
||||
|
||||
!insertmacro MUI_UNFUNCTION_DESCRIPTION_BEGIN
|
||||
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${un.SecUninstall} $(DESC_un.SecUninstall)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${un.SecConfig} $(DESC_un.SecConfig)
|
||||
|
||||
!insertmacro MUI_UNFUNCTION_DESCRIPTION_END
|
||||
|
||||
|
|
@ -337,55 +337,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):
|
||||
|
@ -677,6 +630,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)
|
||||
|
@ -713,6 +722,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)
|
||||
|
@ -729,13 +740,27 @@ 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):
|
||||
# Remove old grids
|
||||
grids.clear()
|
||||
if is_instance_valid(Global.canvas.grid):
|
||||
Global.canvas.grid.queue_redraw()
|
||||
# ADD new ones
|
||||
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 = {
|
||||
|
@ -1066,7 +1091,9 @@ func get_available_font_names() -> PackedStringArray:
|
|||
if font_name in font_names:
|
||||
continue
|
||||
font_names.append(font_name)
|
||||
for system_font_name in OS.get_system_fonts():
|
||||
var system_fonts := OS.get_system_fonts()
|
||||
system_fonts.sort()
|
||||
for system_font_name in system_fonts:
|
||||
if system_font_name in font_names:
|
||||
continue
|
||||
font_names.append(system_font_name)
|
||||
|
|
|
@ -90,9 +90,9 @@ func get_brush_files_from_directory(directory: String): # -> Array
|
|||
func add_randomised_brush(fpaths: Array, tooltip_name: String) -> void:
|
||||
# Attempt to load the images from the file paths.
|
||||
var loaded_images: Array = []
|
||||
for filen in fpaths:
|
||||
for file in fpaths:
|
||||
var image := Image.new()
|
||||
var err := image.load(filen)
|
||||
var err := image.load(file)
|
||||
if err == OK:
|
||||
image.convert(Image.FORMAT_RGBA8)
|
||||
loaded_images.append(image)
|
||||
|
|
|
@ -36,9 +36,10 @@ func does_palette_exist(palette_name: String) -> bool:
|
|||
|
||||
|
||||
func select_palette(palette_name: String) -> void:
|
||||
current_palette = palettes.get(palette_name)
|
||||
current_palette = palettes.get(palette_name, null)
|
||||
_clear_selected_colors()
|
||||
Global.config_cache.set_value("data", "last_palette", current_palette.name)
|
||||
if is_instance_valid(current_palette):
|
||||
Global.config_cache.set_value("data", "last_palette", current_palette.name)
|
||||
palette_selected.emit(palette_name)
|
||||
|
||||
|
||||
|
@ -224,6 +225,7 @@ func current_palete_delete(permanent := true) -> void:
|
|||
select_palette(palettes.keys()[0])
|
||||
else:
|
||||
current_palette = null
|
||||
select_palette("")
|
||||
|
||||
|
||||
func current_palette_add_color(mouse_button: int, start_index := 0) -> void:
|
||||
|
|
|
@ -8,7 +8,7 @@ var image: ImageExtended:
|
|||
set = image_changed
|
||||
|
||||
|
||||
func _init(_image: ImageExtended, _opacity := 1.0) -> void:
|
||||
func _init(_image := ImageExtended.new(), _opacity := 1.0) -> void:
|
||||
image_texture = ImageTexture.new()
|
||||
image = _image # Set image and call setter
|
||||
opacity = _opacity
|
||||
|
@ -20,7 +20,7 @@ func image_changed(value: ImageExtended) -> void:
|
|||
image_texture.set_image(image)
|
||||
|
||||
|
||||
func get_content():
|
||||
func get_content() -> ImageExtended:
|
||||
return image
|
||||
|
||||
|
||||
|
@ -34,17 +34,19 @@ func set_content(content, texture: ImageTexture = null) -> void:
|
|||
image_texture.update(image)
|
||||
|
||||
|
||||
func create_empty_content():
|
||||
var empty_image := Image.create(
|
||||
image.get_size().x, image.get_size().y, false, Image.FORMAT_RGBA8
|
||||
)
|
||||
return empty_image
|
||||
func create_empty_content() -> ImageExtended:
|
||||
var empty := Image.create(image.get_width(), image.get_height(), false, image.get_format())
|
||||
var new_image := ImageExtended.new()
|
||||
new_image.copy_from_custom(empty, image.is_indexed)
|
||||
return new_image
|
||||
|
||||
|
||||
func copy_content():
|
||||
var copy_image := Image.create_from_data(
|
||||
image.get_width(), image.get_height(), false, Image.FORMAT_RGBA8, image.get_data()
|
||||
func copy_content() -> ImageExtended:
|
||||
var tmp_image := Image.create_from_data(
|
||||
image.get_width(), image.get_height(), false, image.get_format(), image.get_data()
|
||||
)
|
||||
var copy_image := ImageExtended.new()
|
||||
copy_image.copy_from_custom(tmp_image, image.is_indexed)
|
||||
return copy_image
|
||||
|
||||
|
||||
|
|
|
@ -74,17 +74,20 @@ func select_palette(_name: String, convert_to_rgb := true) -> void:
|
|||
|
||||
## Updates [member palette] to contain the colors of [member current_palette].
|
||||
func update_palette() -> void:
|
||||
if palette.size() != current_palette.colors.size():
|
||||
palette.resize(current_palette.colors.size())
|
||||
if not is_instance_valid(current_palette):
|
||||
return
|
||||
if palette.size() != current_palette.colors_max:
|
||||
palette.resize(current_palette.colors_max)
|
||||
palette.fill(TRANSPARENT)
|
||||
for i in current_palette.colors:
|
||||
palette[i] = current_palette.colors[i].color
|
||||
|
||||
|
||||
## Displays the actual RGBA values of each pixel in the image from indexed mode.
|
||||
func convert_indexed_to_rgb() -> void:
|
||||
if not is_indexed:
|
||||
if not is_indexed or not is_instance_valid(current_palette):
|
||||
return
|
||||
var palette_image := Palettes.current_palette.convert_to_image()
|
||||
var palette_image := current_palette.convert_to_image(false)
|
||||
var palette_texture := ImageTexture.create_from_image(palette_image)
|
||||
var shader_image_effect := ShaderImageEffect.new()
|
||||
var indices_texture := ImageTexture.create_from_image(indices_image)
|
||||
|
@ -96,9 +99,9 @@ func convert_indexed_to_rgb() -> void:
|
|||
## Automatically maps each color of the image's pixel to the closest color of the palette,
|
||||
## by finding the palette color's index and storing it in [member indices_image].
|
||||
func convert_rgb_to_indexed() -> void:
|
||||
if not is_indexed:
|
||||
if not is_indexed or not is_instance_valid(current_palette):
|
||||
return
|
||||
var palette_image := Palettes.current_palette.convert_to_image()
|
||||
var palette_image := current_palette.convert_to_image(false)
|
||||
var palette_texture := ImageTexture.create_from_image(palette_image)
|
||||
var params := {
|
||||
"palette_texture": palette_texture, "rgb_texture": ImageTexture.create_from_image(self)
|
||||
|
|
|
@ -25,7 +25,7 @@ var colors_max := 0
|
|||
|
||||
|
||||
class PaletteColor:
|
||||
var color := Color.TRANSPARENT
|
||||
var color := Color(0, 0, 0, 0)
|
||||
var index := -1
|
||||
|
||||
func _init(init_color := Color.BLACK, init_index := -1) -> void:
|
||||
|
@ -358,9 +358,11 @@ static func strip_unvalid_characters(string_to_strip: String) -> String:
|
|||
return regex.sub(string_to_strip, "", true)
|
||||
|
||||
|
||||
func convert_to_image() -> Image:
|
||||
func convert_to_image(crop_image := true) -> Image:
|
||||
var image := Image.create(colors_max, 1, false, Image.FORMAT_RGBA8)
|
||||
for i in colors_max:
|
||||
if colors.has(i):
|
||||
image.set_pixel(i, 0, colors[i].color)
|
||||
image.set_pixel(i, 0, Color(colors[i].color.to_html()))
|
||||
if crop_image:
|
||||
image.copy_from(image.get_region(image.get_used_rect()))
|
||||
return image
|
||||
|
|
|
@ -23,10 +23,6 @@ func _ready() -> void:
|
|||
|
||||
|
||||
func set_palette(new_palette: Palette) -> void:
|
||||
# Only display valid palette objects
|
||||
if not new_palette:
|
||||
return
|
||||
|
||||
current_palette = new_palette
|
||||
grid_window_origin = Vector2.ZERO
|
||||
|
||||
|
@ -87,6 +83,8 @@ func scroll_palette(origin: Vector2i) -> void:
|
|||
## Called when the color changes, either the left or the right, determined by [param mouse_button].
|
||||
## If current palette has [param target_color] as a [Color], then select it.
|
||||
func find_and_select_color(target_color: Color, mouse_button: int) -> void:
|
||||
if not is_instance_valid(current_palette):
|
||||
return
|
||||
var old_index := Palettes.current_palette_get_selected_color_index(mouse_button)
|
||||
for color_ind in swatches.size():
|
||||
if (
|
||||
|
@ -115,6 +113,8 @@ func find_and_select_color(target_color: Color, mouse_button: int) -> void:
|
|||
|
||||
## Displays a left/right highlight over a swatch
|
||||
func select_swatch(mouse_button: int, palette_index: int, old_palette_index: int) -> void:
|
||||
if not is_instance_valid(current_palette):
|
||||
return
|
||||
var index := convert_palette_index_to_grid_index(palette_index)
|
||||
var old_index := convert_palette_index_to_grid_index(old_palette_index)
|
||||
if index >= 0 and index < swatches.size():
|
||||
|
@ -159,16 +159,17 @@ func convert_palette_index_to_grid_index(palette_index: int) -> int:
|
|||
|
||||
|
||||
func resize_grid(new_rect_size: Vector2) -> void:
|
||||
if not is_instance_valid(current_palette):
|
||||
return
|
||||
var grid_x: int = (
|
||||
new_rect_size.x / (swatch_size.x + get("theme_override_constants/h_separation"))
|
||||
)
|
||||
var grid_y: int = (
|
||||
new_rect_size.y / (swatch_size.y + get("theme_override_constants/v_separation"))
|
||||
)
|
||||
grid_size.x = mini(grid_x, current_palette.width)
|
||||
grid_size.y = mini(grid_y, current_palette.height)
|
||||
if is_instance_valid(current_palette):
|
||||
grid_size.x = mini(grid_x, current_palette.width)
|
||||
grid_size.y = mini(grid_y, current_palette.height)
|
||||
else:
|
||||
grid_size = Vector2i.ZERO
|
||||
setup_swatches()
|
||||
draw_palette()
|
||||
|
||||
|
|
|
@ -89,16 +89,16 @@ func select_palette(palette_name: String) -> void:
|
|||
var palette_id = palettes_path_id.get(palette_name)
|
||||
if palette_id != null:
|
||||
palette_select.selected = palette_id
|
||||
palette_grid.set_palette(Palettes.current_palette)
|
||||
palette_scroll.resize_grid()
|
||||
palette_scroll.set_sliders(Palettes.current_palette, palette_grid.grid_window_origin)
|
||||
palette_grid.set_palette(Palettes.current_palette)
|
||||
palette_scroll.resize_grid()
|
||||
palette_scroll.set_sliders(Palettes.current_palette, palette_grid.grid_window_origin)
|
||||
|
||||
var left_selected := Palettes.current_palette_get_selected_color_index(MOUSE_BUTTON_LEFT)
|
||||
var right_selected := Palettes.current_palette_get_selected_color_index(MOUSE_BUTTON_RIGHT)
|
||||
palette_grid.select_swatch(MOUSE_BUTTON_LEFT, left_selected, left_selected)
|
||||
palette_grid.select_swatch(MOUSE_BUTTON_RIGHT, right_selected, right_selected)
|
||||
var left_selected := Palettes.current_palette_get_selected_color_index(MOUSE_BUTTON_LEFT)
|
||||
var right_selected := Palettes.current_palette_get_selected_color_index(MOUSE_BUTTON_RIGHT)
|
||||
palette_grid.select_swatch(MOUSE_BUTTON_LEFT, left_selected, left_selected)
|
||||
palette_grid.select_swatch(MOUSE_BUTTON_RIGHT, right_selected, right_selected)
|
||||
|
||||
toggle_add_delete_buttons()
|
||||
toggle_add_delete_buttons()
|
||||
|
||||
|
||||
## Select and display current palette
|
||||
|
@ -115,6 +115,8 @@ func redraw_current_palette() -> void:
|
|||
|
||||
|
||||
func toggle_add_delete_buttons() -> void:
|
||||
if not is_instance_valid(Palettes.current_palette):
|
||||
return
|
||||
add_color_button.disabled = Palettes.current_palette.is_full()
|
||||
if add_color_button.disabled:
|
||||
add_color_button.mouse_default_cursor_shape = CURSOR_FORBIDDEN
|
||||
|
@ -252,6 +254,7 @@ func _on_ColorPicker_color_changed(color: Color) -> void:
|
|||
== Palettes.current_palette_get_selected_color_index(MOUSE_BUTTON_RIGHT)
|
||||
):
|
||||
Tools.assign_color(color, MOUSE_BUTTON_RIGHT)
|
||||
Palettes.current_palette_set_color(edited_swatch_index, edited_swatch_color)
|
||||
|
||||
|
||||
## Saves edited swatch to palette file when color selection dialog is closed
|
||||
|
|
|
@ -4,9 +4,9 @@ var scroll := Vector2i.ZERO
|
|||
var drag_started := false
|
||||
var drag_start_position := Vector2i.ZERO
|
||||
|
||||
@onready var h_slider := %HScrollBar
|
||||
@onready var v_slider := %VScrollBar
|
||||
@onready var palette_grid := %PaletteGrid
|
||||
@onready var h_slider := %HScrollBar as HScrollBar
|
||||
@onready var v_slider := %VScrollBar as VScrollBar
|
||||
@onready var palette_grid := %PaletteGrid as PaletteGrid
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
|
@ -17,16 +17,21 @@ func _input(event: InputEvent) -> void:
|
|||
|
||||
|
||||
func set_sliders(palette: Palette, origin: Vector2i) -> void:
|
||||
if not is_instance_valid(palette):
|
||||
return
|
||||
h_slider.value = origin.x
|
||||
h_slider.max_value = palette.width
|
||||
h_slider.page = palette_grid.grid_size.x
|
||||
if is_instance_valid(palette):
|
||||
h_slider.value = origin.x
|
||||
h_slider.max_value = palette.width
|
||||
h_slider.page = palette_grid.grid_size.x
|
||||
v_slider.value = origin.y
|
||||
v_slider.max_value = palette.height
|
||||
v_slider.page = palette_grid.grid_size.y
|
||||
else:
|
||||
h_slider.value = 0
|
||||
h_slider.max_value = 0
|
||||
h_slider.page = 0
|
||||
v_slider.value = 0
|
||||
v_slider.max_value = 0
|
||||
v_slider.page = 0
|
||||
h_slider.visible = false if h_slider.max_value <= palette_grid.grid_size.x else true
|
||||
|
||||
v_slider.value = origin.y
|
||||
v_slider.max_value = palette.height
|
||||
v_slider.page = palette_grid.grid_size.y
|
||||
v_slider.visible = false if v_slider.max_value <= palette_grid.grid_size.y else true
|
||||
|
||||
|
||||
|
@ -58,7 +63,7 @@ func _on_PaletteGrid_gui_input(event: InputEvent) -> void:
|
|||
drag_started = true
|
||||
# Keeps position where the dragging started
|
||||
drag_start_position = (
|
||||
event.position + Vector2i(h_slider.value, v_slider.value) * palette_grid.swatch_size
|
||||
event.position + Vector2(h_slider.value, v_slider.value) * palette_grid.swatch_size
|
||||
)
|
||||
|
||||
if event is InputEventMouseMotion and drag_started:
|
||||
|
|
200
src/Preferences/GridPreferences.gd
Normal file
200
src/Preferences/GridPreferences.gd
Normal file
|
@ -0,0 +1,200 @@
|
|||
extends GridContainer
|
||||
|
||||
# We should use pre defined initial grid colors instead of random colors
|
||||
const INITIAL_GRID_COLORS := [
|
||||
Color.BLACK,
|
||||
Color.WHITE,
|
||||
Color.YELLOW,
|
||||
Color.GREEN,
|
||||
Color.BLUE,
|
||||
Color.GRAY,
|
||||
Color.ORANGE,
|
||||
Color.PINK,
|
||||
Color.SIENNA,
|
||||
Color.CORAL,
|
||||
]
|
||||
|
||||
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 new_grids: Dictionary = Global.config_cache.get_value(
|
||||
"preferences", "grids", {0: create_default_properties()}
|
||||
)
|
||||
var last_grid_idx = int(value - 1)
|
||||
if last_grid_idx >= grids_select_container.get_child_count():
|
||||
# Add missing grids
|
||||
for key in range(grids_select_container.get_child_count(), value):
|
||||
if not new_grids.has(key):
|
||||
var new_grid := create_default_properties()
|
||||
if new_grids.has(key - 1): # Failsafe
|
||||
var last_grid = new_grids[key - 1]
|
||||
# This small bit of code is there to make ui look a little neater
|
||||
# Reasons:
|
||||
# - Usually user intends to make the next grid twice the size.
|
||||
# - Having all grids being same size initially may cause confusion for some
|
||||
# users when they try to change color of a middle grid not seeing it's changing
|
||||
# (due to being covered by grids above it).
|
||||
if (
|
||||
new_grid.has("grid_size")
|
||||
and new_grid.has("isometric_grid_size")
|
||||
and new_grid.has("grid_color")
|
||||
):
|
||||
new_grid["grid_size"] = last_grid["grid_size"] * 2
|
||||
new_grid["isometric_grid_size"] = last_grid["isometric_grid_size"] * 2
|
||||
if key < INITIAL_GRID_COLORS.size():
|
||||
new_grid["grid_color"] = INITIAL_GRID_COLORS[key]
|
||||
new_grids[key] = new_grid
|
||||
add_remove_select_button(key)
|
||||
else:
|
||||
# Remove extra grids
|
||||
for key: int in range(value, new_grids.size()):
|
||||
new_grids.erase(key)
|
||||
add_remove_select_button(key, true)
|
||||
grid_selected = min(grid_selected, last_grid_idx)
|
||||
Global.update_grids(new_grids)
|
||||
Global.config_cache.set_value("preferences", "grids", new_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()
|
||||
|
||||
|
||||
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])
|
||||
if pref.value_type == "color":
|
||||
# the signal doesn't seem to be emitted automatically
|
||||
node.color_changed.emit(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"]
|
||||
|
|
|
@ -14,7 +14,7 @@ vec4 swap_color(vec4 color) {
|
|||
|
||||
int n_of_colors = textureSize(palette_texture, 0).x;
|
||||
int color_index = find_index(color, n_of_colors, palette_texture);
|
||||
return texture(palette_texture, vec2(float(color_index) / float(n_of_colors), 0.0));
|
||||
return texelFetch(palette_texture, ivec2(color_index, 0), 0);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
|
|
|
@ -2,8 +2,7 @@ int find_index(vec4 color, int n_of_colors, sampler2D palette_texture) {
|
|||
int color_index = 0;
|
||||
float smaller_distance = distance(color, texture(palette_texture, vec2(0.0)));
|
||||
for (int i = 0; i <= n_of_colors; i++) {
|
||||
vec2 uv = vec2(float(i) / float(n_of_colors), 0.0);
|
||||
vec4 palette_color = texture(palette_texture, uv);
|
||||
vec4 palette_color = texelFetch(palette_texture, ivec2(i, 0), 0);
|
||||
float dist = distance(color, palette_color);
|
||||
if (dist < smaller_distance) {
|
||||
smaller_distance = dist;
|
||||
|
|
|
@ -7,16 +7,16 @@ uniform sampler2D palette_texture : filter_nearest;
|
|||
uniform sampler2D indices_texture : filter_nearest;
|
||||
|
||||
void fragment() {
|
||||
float index = texture(indices_texture, UV).r;
|
||||
float index = texture(indices_texture, UV).r * 255.0;
|
||||
if (index <= EPSILON) { // If index is zero, make it transparent
|
||||
COLOR = vec4(0.0);
|
||||
}
|
||||
else {
|
||||
float n_of_colors = float(textureSize(palette_texture, 0).x);
|
||||
index -= 1.0 / 255.0;
|
||||
float index_normalized = ((index * 255.0)) / n_of_colors;
|
||||
if (index_normalized + EPSILON < 1.0) {
|
||||
COLOR = texture(palette_texture, vec2(index_normalized + EPSILON, 0.0));
|
||||
index -= 1.0;
|
||||
float index_normalized = index / n_of_colors;
|
||||
if (index < n_of_colors) {
|
||||
COLOR = texelFetch(palette_texture, ivec2(int(index), 0), 0);
|
||||
}
|
||||
else {
|
||||
// If index is bigger than the size of the palette, make it transparent.
|
||||
|
|
|
@ -8,7 +8,7 @@ uniform sampler2D palette_texture : filter_nearest;
|
|||
|
||||
void fragment() {
|
||||
vec4 color = texture(rgb_texture, UV);
|
||||
if (color.a <= 0.01) {
|
||||
if (color.a <= 0.0001) {
|
||||
COLOR.r = 0.0;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -269,9 +269,11 @@ func fill_in_selection() -> void:
|
|||
var selection_map_copy := project.selection_map.return_cropped_copy(project.size)
|
||||
for image in images:
|
||||
image.blit_rect_mask(filler, selection_map_copy, rect, rect.position)
|
||||
image.convert_rgb_to_indexed()
|
||||
else:
|
||||
for image in images:
|
||||
image.fill(tool_slot.color)
|
||||
image.convert_rgb_to_indexed()
|
||||
else:
|
||||
# End early if we are filling with an empty pattern
|
||||
var pattern_image: Image = _pattern.image
|
||||
|
|
|
@ -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(
|
||||
|
@ -110,16 +110,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,4 +1,4 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://ct4o5i1jeul3k"]
|
||||
[gd_scene load_steps=6 format=3 uid="uid://bdregpkflev7u"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://ctfgfelg0sho8" path="res://src/Tools/BaseTool.tscn" id="1_1q6ub"]
|
||||
[ext_resource type="Script" path="res://src/Tools/UtilityTools/Text.gd" id="2_ql5g6"]
|
||||
|
@ -63,6 +63,8 @@ stretch_margin_bottom = 3
|
|||
script = ExtResource("3_tidsq")
|
||||
prefix = "Size:"
|
||||
suffix = "px"
|
||||
global_increment_action = "brush_size_increment"
|
||||
global_decrement_action = "brush_size_decrement"
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="." index="4"]
|
||||
layout_mode = 2
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -214,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
|
||||
|
@ -808,9 +808,11 @@ func delete(selected_cels := true) -> void:
|
|||
image.blit_rect_mask(
|
||||
blank, selection_map_copy, big_bounding_rectangle, big_bounding_rectangle.position
|
||||
)
|
||||
image.convert_rgb_to_indexed()
|
||||
else:
|
||||
for image in images:
|
||||
image.fill(0)
|
||||
image.convert_rgb_to_indexed()
|
||||
commit_undo("Draw", undo_data_tmp)
|
||||
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ class Recorder:
|
|||
dir.make_dir_recursive(save_directory)
|
||||
project.removed.connect(recorder_panel.finalize_recording.bind(project))
|
||||
project.undo_redo.version_changed.connect(capture_frame)
|
||||
recorder_panel.captured_label.text = ""
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
|
@ -100,6 +101,9 @@ func _on_project_switched() -> void:
|
|||
initialize_recording()
|
||||
start_button.set_pressed_no_signal(true)
|
||||
Global.change_button_texturerect(start_button.get_child(0), "stop.png")
|
||||
captured_label.text = str(
|
||||
"Saved: ", recorded_projects[Global.current_project].frames_captured
|
||||
)
|
||||
else:
|
||||
finalize_recording()
|
||||
start_button.set_pressed_no_signal(false)
|
||||
|
|
|
@ -40,15 +40,18 @@ mouse_default_cursor_shape = 2
|
|||
toggle_mode = true
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/CenterContainer/GridContainer/Start"]
|
||||
layout_mode = 0
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -10.0
|
||||
offset_top = -10.5
|
||||
offset_right = 10.0
|
||||
offset_bottom = 10.5
|
||||
offset_left = -11.0
|
||||
offset_top = -11.0
|
||||
offset_right = 11.0
|
||||
offset_bottom = 11.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = ExtResource("1")
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
@ -74,7 +77,7 @@ offset_bottom = 10.5
|
|||
texture = ExtResource("3")
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="OpenFolder" type="Button" parent="ScrollContainer/CenterContainer/GridContainer"]
|
||||
[node name="OpenFolder" type="Button" parent="ScrollContainer/CenterContainer/GridContainer" groups=["UIButtons"]]
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
layout_mode = 2
|
||||
tooltip_text = "Open Folder"
|
||||
|
|
|
@ -421,7 +421,6 @@ func _setup_color_mode_submenu(item: String) -> void:
|
|||
color_mode_submenu.add_radio_check_item("RGBA", ColorModes.RGBA)
|
||||
color_mode_submenu.set_item_checked(ColorModes.RGBA, true)
|
||||
color_mode_submenu.add_radio_check_item("Indexed", ColorModes.INDEXED)
|
||||
color_mode_submenu.hide_on_checkable_item_selection = false
|
||||
|
||||
color_mode_submenu.id_pressed.connect(_color_mode_submenu_id_pressed)
|
||||
image_menu.add_child(color_mode_submenu)
|
||||
|
|
Loading…
Reference in a new issue