bugs squished
This commit is contained in:
parent
24c4b98a5e
commit
17d8637156
54
assets/play_small.svg
Normal file
54
assets/play_small.svg
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="13"
|
||||||
|
height="13"
|
||||||
|
viewBox="0 0 3.4395833 3.4395833"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="play_small.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#111111"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="35.356609"
|
||||||
|
inkscape:cx="6.3495909"
|
||||||
|
inkscape:cy="5.8263506"
|
||||||
|
inkscape:window-width="1896"
|
||||||
|
inkscape:window-height="1029"
|
||||||
|
inkscape:window-x="12"
|
||||||
|
inkscape:window-y="39"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid7036"
|
||||||
|
originx="0"
|
||||||
|
originy="0" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
id="rect11457"
|
||||||
|
style="fill:#ffffff;stroke-width:1.85208;paint-order:stroke markers fill;stop-color:#000000"
|
||||||
|
d="M 0.26458333,0.26458333 3.175,1.7197917 0.26458333,3.175 Z"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
35
assets/play_small.svg.import
Normal file
35
assets/play_small.svg.import
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="StreamTexture"
|
||||||
|
path="res://.import/play_small.svg-6ecf1cf55097c1673c0917a7e7624a3c.stex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/play_small.svg"
|
||||||
|
dest_files=[ "res://.import/play_small.svg-6ecf1cf55097c1673c0917a7e7624a3c.stex" ]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_mode=0
|
||||||
|
compress/bptc_ldr=0
|
||||||
|
compress/normal_map=0
|
||||||
|
flags/repeat=0
|
||||||
|
flags/filter=true
|
||||||
|
flags/mipmaps=false
|
||||||
|
flags/anisotropic=false
|
||||||
|
flags/srgb=2
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/HDR_as_SRGB=false
|
||||||
|
process/invert_color=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
stream=false
|
||||||
|
size_limit=0
|
||||||
|
detect_3d=true
|
||||||
|
svg/scale=1.0
|
@ -86,6 +86,7 @@ window/per_pixel_transparency/enabled=true
|
|||||||
window/energy_saving/keep_screen_on=false
|
window/energy_saving/keep_screen_on=false
|
||||||
window/handheld/orientation="portrait"
|
window/handheld/orientation="portrait"
|
||||||
window/ios/hide_home_indicator=false
|
window/ios/hide_home_indicator=false
|
||||||
|
window/stretch/aspect="keep"
|
||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
class_name CMD
|
class_name CMD
|
||||||
|
|
||||||
var command_line_arguments: Dictionary = {}
|
|
||||||
|
|
||||||
func unsurround(value: String, quotes := PoolStringArray(['"', "'"])) -> String:
|
var _parsed := false
|
||||||
|
|
||||||
|
## @type Dictionary[String, String|bool]
|
||||||
|
var command_line_arguments: Dictionary = {} setget set_command_line_arguments, get_command_line_arguments
|
||||||
|
|
||||||
|
|
||||||
|
## Removes the first element find from the `quotes` array from the start and end of a string
|
||||||
|
## Also removes any whitespace resulting from removing the quoting elements
|
||||||
|
static func unsurround(value: String, quotes := PoolStringArray(['"', "'"])) -> String:
|
||||||
for quote_str in quotes:
|
for quote_str in quotes:
|
||||||
if value.begins_with(quote_str) \
|
if value.begins_with(quote_str) \
|
||||||
and value.ends_with(quote_str) \
|
and value.ends_with(quote_str) \
|
||||||
@ -10,6 +17,7 @@ func unsurround(value: String, quotes := PoolStringArray(['"', "'"])) -> String:
|
|||||||
return value.trim_prefix(quote_str).trim_suffix(quote_str).strip_edges()
|
return value.trim_prefix(quote_str).trim_suffix(quote_str).strip_edges()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
## Returns a dictionary of all arguments passed after `--` on the command line
|
## Returns a dictionary of all arguments passed after `--` on the command line
|
||||||
## arguments take one of 2 forms:
|
## arguments take one of 2 forms:
|
||||||
## - `--arg` which is a boolean (using `--no-arg` for `false` is possible)
|
## - `--arg` which is a boolean (using `--no-arg` for `false` is possible)
|
||||||
@ -17,7 +25,7 @@ func unsurround(value: String, quotes := PoolStringArray(['"', "'"])) -> String:
|
|||||||
## unsurround the string
|
## unsurround the string
|
||||||
## This function does no evaluation and does not attempt to guess the type of
|
## This function does no evaluation and does not attempt to guess the type of
|
||||||
## arguments. You will receive either bools, or strings.
|
## arguments. You will receive either bools, or strings.
|
||||||
func _read_arguments() -> Dictionary:
|
static func parse_cmd_arguments() -> Dictionary:
|
||||||
var arguments := {}
|
var arguments := {}
|
||||||
for arg in OS.get_cmdline_args():
|
for arg in OS.get_cmdline_args():
|
||||||
var argument: String = arg.lstrip("--").to_lower()
|
var argument: String = arg.lstrip("--").to_lower()
|
||||||
@ -36,12 +44,27 @@ func _read_arguments() -> Dictionary:
|
|||||||
return arguments
|
return arguments
|
||||||
|
|
||||||
|
|
||||||
|
func set_command_line_arguments(_arguments: Dictionary) -> void:
|
||||||
|
printerr("get_command_line_arguments is a read only value")
|
||||||
|
|
||||||
|
|
||||||
|
func get_command_line_arguments() -> Dictionary:
|
||||||
|
if not _parsed:
|
||||||
|
_parsed = true
|
||||||
|
command_line_arguments = parse_cmd_arguments()
|
||||||
|
return command_line_arguments
|
||||||
|
|
||||||
|
|
||||||
|
## Returns a single argument passed after `--` on the command line
|
||||||
|
## if the argument does not exist, `default` is returned instead
|
||||||
|
## _parse_cmd_arguments() has to be called first
|
||||||
func get_argument(name: String, default = null):
|
func get_argument(name: String, default = null):
|
||||||
if command_line_arguments.has(name):
|
if get_command_line_arguments().has(name):
|
||||||
return command_line_arguments[name]
|
return get_command_line_arguments()[name]
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
## Verifies an argument exists on the command line
|
||||||
|
## _parse_cmd_arguments() has to be called first
|
||||||
func has_argument(name: String) -> bool:
|
func has_argument(name: String) -> bool:
|
||||||
return command_line_arguments.has(name)
|
return get_command_line_arguments().has(name)
|
||||||
|
@ -1,25 +1,41 @@
|
|||||||
|
## Reads the config, sets values. Acts a singleton because it proxies a const
|
||||||
|
## file path.
|
||||||
class_name ConfigManager extends Resource
|
class_name ConfigManager extends Resource
|
||||||
|
|
||||||
|
|
||||||
signal file_changed
|
|
||||||
signal time_sheet_reloaded
|
|
||||||
|
|
||||||
const CONFIG_PATH := "user://settings.cfg"
|
const CONFIG_PATH := "user://settings.cfg"
|
||||||
|
|
||||||
var timesheet: TimeSheet setget ,get_timesheet
|
|
||||||
|
|
||||||
var _config := ConfigFile.new()
|
var _config := ConfigFile.new()
|
||||||
var _watcher: FileWatcher
|
var _watcher: FileWatcher
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# SIGNAL
|
||||||
|
#
|
||||||
|
|
||||||
|
signal time_sheet_loaded
|
||||||
|
|
||||||
|
func emit_loaded() -> void:
|
||||||
|
emit_signal("time_sheet_loaded")
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# TIMESHEET FILE LOADING AND PARSING
|
||||||
|
#
|
||||||
|
|
||||||
|
var timesheet: TimeSheet setget ,get_timesheet
|
||||||
|
|
||||||
|
|
||||||
func get_timesheet() -> TimeSheet:
|
func get_timesheet() -> TimeSheet:
|
||||||
if timesheet == null:
|
if timesheet == null:
|
||||||
timesheet = _load_timesheet(get_current_file())
|
timesheet = _load_timesheet(get_current_timesheet_file_path())
|
||||||
return timesheet
|
return timesheet
|
||||||
|
|
||||||
|
|
||||||
func _load_timesheet(path: String) -> TimeSheet:
|
func _load_timesheet(path: String) -> TimeSheet:
|
||||||
timesheet = TimeSheet.restore(path)
|
var new_timesheet := TimeSheet.restore(path)
|
||||||
if timesheet == null:
|
if new_timesheet == null:
|
||||||
return null
|
return null
|
||||||
_watcher = FileWatcher.new()
|
_watcher = FileWatcher.new()
|
||||||
_watcher.file_name = path
|
_watcher.file_name = path
|
||||||
@ -30,72 +46,94 @@ func _load_timesheet(path: String) -> TimeSheet:
|
|||||||
#timesheet.connect("entry_started", self, "_on_entry_started")
|
#timesheet.connect("entry_started", self, "_on_entry_started")
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
#timesheet.connect("entry_stopped", self, "_on_entry_stopped")
|
#timesheet.connect("entry_stopped", self, "_on_entry_stopped")
|
||||||
return timesheet
|
return new_timesheet
|
||||||
|
|
||||||
|
|
||||||
func reload_timesheet() -> void:
|
func reload_timesheet() -> void:
|
||||||
var new_timesheet = _load_timesheet(get_current_file())
|
var new_timesheet = _load_timesheet(get_current_timesheet_file_path())
|
||||||
if new_timesheet == null:
|
if new_timesheet == null:
|
||||||
printerr("failed to load new timesheet")
|
printerr("failed to load new timesheet")
|
||||||
return
|
return
|
||||||
timesheet = new_timesheet
|
timesheet = new_timesheet
|
||||||
emit_signal("time_sheet_reloaded")
|
emit_loaded()
|
||||||
|
|
||||||
|
|
||||||
var current_file: String = "" setget set_current_file, get_current_file
|
###############################################################################
|
||||||
|
#
|
||||||
|
# TIMESHEET FILE PATH
|
||||||
|
#
|
||||||
|
|
||||||
|
var current_timesheet_file_path: String = "" setget set_current_timesheet_file_path, get_current_timesheet_file_path
|
||||||
|
|
||||||
|
|
||||||
func set_current_file(value: String) -> void:
|
func set_current_timesheet_file_path(value: String) -> void:
|
||||||
timesheet = _load_timesheet(value)
|
timesheet = _load_timesheet(value)
|
||||||
if timesheet == null:
|
if timesheet == null:
|
||||||
return
|
return
|
||||||
current_file = value
|
current_timesheet_file_path = value
|
||||||
_config.set_value("MAIN", "file", value)
|
_config.set_value("MAIN", "file", value)
|
||||||
emit_signal("file_changed")
|
emit_loaded()
|
||||||
save()
|
save()
|
||||||
|
|
||||||
|
|
||||||
func get_current_file() -> String:
|
func get_current_timesheet_file_path() -> String:
|
||||||
var _default_path := OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS, true).plus_file("mouse_timer.csv")
|
var _default_path := OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS, true).plus_file("mouse_timer.csv")
|
||||||
return _config.get_value("MAIN", "file", _default_path)
|
return _config.get_value("MAIN", "file", _default_path)
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# THEME FILE LOADING AND PARSING
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
var theme: Theme setget , get_theme
|
var theme: Theme setget , get_theme
|
||||||
|
|
||||||
|
|
||||||
func get_theme() -> Theme:
|
func get_theme() -> Theme:
|
||||||
if theme == null:
|
if theme == null:
|
||||||
theme = ResourceLoader.load(theme_path, "Theme")
|
theme = ResourceLoader.load(theme_file_path, "Theme")
|
||||||
return theme
|
return theme
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# THEME FILE PATH
|
||||||
|
#
|
||||||
|
|
||||||
signal theme_changed
|
signal theme_changed
|
||||||
var theme_path: String = "" setget set_theme_path, get_theme_path
|
var theme_file_path: String = "" setget set_theme_file_path, get_theme_file_path
|
||||||
|
|
||||||
|
|
||||||
func set_theme_path(value: String) -> void:
|
func set_theme_file_path(value: String) -> void:
|
||||||
var new_theme: Theme = ResourceLoader.load(value, "Theme")
|
var new_theme: Theme = ResourceLoader.load(value, "Theme")
|
||||||
if new_theme != null:
|
if new_theme != null:
|
||||||
theme = new_theme
|
theme = new_theme
|
||||||
theme_path = value
|
theme_file_path = value
|
||||||
_config.set_value("MAIN", "theme", value)
|
_config.set_value("MAIN", "theme", value)
|
||||||
emit_signal("theme_changed")
|
emit_signal("theme_changed")
|
||||||
save()
|
save()
|
||||||
|
|
||||||
|
|
||||||
func get_theme_path() -> String:
|
func get_theme_file_path() -> String:
|
||||||
return _config.get_value("MAIN", "theme", preload("res://assets/default_theme.theme").resource_path)
|
return _config.get_value("MAIN", "theme", preload("res://assets/default_theme.theme").resource_path)
|
||||||
|
|
||||||
|
|
||||||
var last_task_name: String = "" setget set_last_task_name, get_last_task_name
|
#var current_task_name: String = "" setget set_current_task_name, get_current_task_name
|
||||||
|
#
|
||||||
|
#func set_current_task_name(value: String) -> void:
|
||||||
|
# current_task_name = value
|
||||||
|
# _config.set_value("MAIN", "current_task_name", value)
|
||||||
|
# save()
|
||||||
|
#
|
||||||
|
#func get_current_task_name() -> String:
|
||||||
|
# return _config.get_value("MAIN", "current_task_name", "")
|
||||||
|
|
||||||
func set_last_task_name(value: String) -> void:
|
|
||||||
last_task_name = value
|
|
||||||
_config.set_value("MAIN", "last_task_name", value)
|
|
||||||
save()
|
|
||||||
|
|
||||||
func get_last_task_name() -> String:
|
|
||||||
return _config.get_value("MAIN", "last_task_name", "")
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# SOUND OPTION
|
||||||
|
#
|
||||||
|
|
||||||
var sound_fx_on: bool = true setget set_sound_fx_on, get_sound_fx_on
|
var sound_fx_on: bool = true setget set_sound_fx_on, get_sound_fx_on
|
||||||
|
|
||||||
@ -108,6 +146,30 @@ func get_sound_fx_on() -> bool:
|
|||||||
return _config.get_value("MAIN", "sound_fx", true)
|
return _config.get_value("MAIN", "sound_fx", true)
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# SOME SETTINGS CACHE
|
||||||
|
#
|
||||||
|
|
||||||
|
var current_task_name := "" setget set_current_task_name, get_current_task_name
|
||||||
|
|
||||||
|
|
||||||
|
func set_current_task_name(value: String) -> void:
|
||||||
|
current_task_name = value
|
||||||
|
_config.set_value("CACHE", "current_task_name", value)
|
||||||
|
save()
|
||||||
|
|
||||||
|
|
||||||
|
func get_current_task_name() -> String:
|
||||||
|
return _config.get_value("CACHE", "current_task_name", "")
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# BOOTSTRAP
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
_config.load(CONFIG_PATH)
|
_config.load(CONFIG_PATH)
|
||||||
|
@ -37,6 +37,7 @@ func check() -> void:
|
|||||||
var new_modified := _file.get_modified_time(file_name)
|
var new_modified := _file.get_modified_time(file_name)
|
||||||
if new_modified != _last_modified:
|
if new_modified != _last_modified:
|
||||||
_last_modified = new_modified
|
_last_modified = new_modified
|
||||||
|
print("file changed")
|
||||||
emit_signal("file_changed")
|
emit_signal("file_changed")
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
class_name TimeEntry
|
class_name TimeEntry
|
||||||
|
|
||||||
|
|
||||||
signal end_time_updated
|
|
||||||
signal closed
|
|
||||||
|
|
||||||
var name := ""
|
var name := ""
|
||||||
var is_closed := false
|
var is_closed := false
|
||||||
var start_time := TimeStamp.new()
|
var start_time := TimeStamp.new()
|
||||||
@ -13,20 +10,21 @@ var end_time := TimeStamp.new()
|
|||||||
|
|
||||||
|
|
||||||
func start_recording() -> TimeEntry:
|
func start_recording() -> TimeEntry:
|
||||||
start_time = start_time.from_current_time()
|
# warning-ignore:return_value_discarded
|
||||||
end_time = end_time.from_current_time()
|
start_time.from_current_time()
|
||||||
|
# warning-ignore:return_value_discarded
|
||||||
|
end_time.from_current_time()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
func update() -> void:
|
func update() -> void:
|
||||||
end_time = end_time.from_current_time()
|
# warning-ignore:return_value_discarded
|
||||||
emit_signal("end_time_updated")
|
end_time.from_current_time()
|
||||||
|
|
||||||
|
|
||||||
func close() -> void:
|
func close() -> void:
|
||||||
update()
|
update()
|
||||||
is_closed = true
|
is_closed = true
|
||||||
emit_signal("closed")
|
|
||||||
|
|
||||||
|
|
||||||
func get_elapsed_seconds() -> int:
|
func get_elapsed_seconds() -> int:
|
||||||
@ -88,5 +86,6 @@ func to_dict() -> Dictionary:
|
|||||||
closed = is_closed,
|
closed = is_closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func _to_string() -> String:
|
func _to_string() -> String:
|
||||||
return "%s\t%s\t%s"%[name, tr(Consts.ONGOING) if is_closed == false else end_time.to_string(), start_time]
|
return "%s\t%s\t%s"%[name, tr(Consts.ONGOING) if is_closed == false else end_time.to_string(), start_time]
|
||||||
|
@ -6,7 +6,9 @@ var time_entry: TimeEntry
|
|||||||
var children := {}
|
var children := {}
|
||||||
var time_entries := []
|
var time_entries := []
|
||||||
|
|
||||||
|
|
||||||
func get_child(parts: Array, or_create := false):
|
func get_child(parts: Array, or_create := false):
|
||||||
|
# workaround for cyclic dependencies bug
|
||||||
var TimeEntryTreeItem = load("res://scripts/time_entry_tree_item.gd")
|
var TimeEntryTreeItem = load("res://scripts/time_entry_tree_item.gd")
|
||||||
if parts.size() == 0:
|
if parts.size() == 0:
|
||||||
return self
|
return self
|
||||||
@ -21,14 +23,23 @@ func get_child(parts: Array, or_create := false):
|
|||||||
return children[part].get_child(parts, or_create)
|
return children[part].get_child(parts, or_create)
|
||||||
|
|
||||||
|
|
||||||
|
func find_active_time_entry() -> TimeEntry:
|
||||||
|
for _time_entry_tree_item in time_entries:
|
||||||
|
var time_entry_tree_item := _time_entry_tree_item as TimeEntryTreeItem
|
||||||
|
var current_time_entry := time_entry_tree_item.time_entry
|
||||||
|
if not current_time_entry.is_closed:
|
||||||
|
return current_time_entry
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
func append(new_time_entry: TimeEntry) -> void:
|
func append(new_time_entry: TimeEntry) -> void:
|
||||||
var TimeEntryTreeItem = load("res://scripts/time_entry_tree_item.gd")
|
var TimeEntryTreeItem = load("res://scripts/time_entry_tree_item.gd")
|
||||||
var time_entry_tree_item = TimeEntryTreeItem.new()
|
var time_entry_tree_item = TimeEntryTreeItem.new()
|
||||||
time_entry_tree_item.time_entry = new_time_entry
|
time_entry_tree_item.time_entry = new_time_entry
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
new_time_entry.connect("end_time_updated", time_entry_tree_item, "_on_end_time_updated")
|
# new_time_entry.connect("end_time_updated", time_entry_tree_item, "_on_end_time_updated")
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
time_entry_tree_item.connect("end_time_updated", self, "_on_end_time_updated")
|
# time_entry_tree_item.connect("end_time_updated", self, "_on_end_time_updated")
|
||||||
time_entries.append(time_entry_tree_item)
|
time_entries.append(time_entry_tree_item)
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,8 @@ class_name TimeSheet
|
|||||||
|
|
||||||
var source_path := ""
|
var source_path := ""
|
||||||
var entries := []
|
var entries := []
|
||||||
var tree := TimeEntryTreeItem.new()
|
# warning-ignore:integer_division
|
||||||
|
var _last_update := Time.get_ticks_msec() / 1000
|
||||||
|
|
||||||
## Loads the data file
|
## Loads the data file
|
||||||
func load_file() -> bool:
|
func load_file() -> bool:
|
||||||
@ -25,20 +26,10 @@ func load_file() -> bool:
|
|||||||
continue
|
continue
|
||||||
var entry := TimeEntry.new().from_csv_line(line)
|
var entry := TimeEntry.new().from_csv_line(line)
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
# warning-ignore:return_value_discarded
|
|
||||||
entry.connect("closed", self, "save")
|
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
entries.sort_custom(self, "_sort_entries")
|
entries.sort_custom(self, "_sort_entries")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for entry_index in entries.size():
|
|
||||||
var entry: TimeEntry = entries[entry_index]
|
|
||||||
var parts: PoolStringArray = entry.name.split("/")
|
|
||||||
var repo: TimeEntryTreeItem = tree.get_child(parts, true)
|
|
||||||
repo.append(entry)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
@ -46,22 +37,56 @@ func _sort_entries(a: TimeEntry, b: TimeEntry) -> bool:
|
|||||||
return a.name < b.name
|
return a.name < b.name
|
||||||
|
|
||||||
|
|
||||||
|
func get_active_entry_from_name(task_name: String) -> TimeEntry:
|
||||||
|
for _entry in entries:
|
||||||
|
var current_time_entry := _entry as TimeEntry
|
||||||
|
if current_time_entry.name == task_name and not current_time_entry.is_closed:
|
||||||
|
return current_time_entry
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
## Adds a new time entry to the tree and to the data file
|
## Adds a new time entry to the tree and to the data file
|
||||||
func start_entry(entry_name: String) -> void:
|
func add_entry(entry_name: String) -> TimeEntry:
|
||||||
var current_entry := TimeEntry.new().start_recording()
|
var current_entry := TimeEntry.new().start_recording()
|
||||||
current_entry.name = entry_name
|
current_entry.name = entry_name
|
||||||
current_entry.closed = false
|
current_entry.is_closed = false
|
||||||
var file := File.new()
|
var file := File.new()
|
||||||
var success := file.open(source_path, File.READ_WRITE)
|
var success := file.open(source_path, File.READ_WRITE)
|
||||||
if success != OK:
|
if success != OK:
|
||||||
printerr("Could not open file")
|
printerr("Could not open file")
|
||||||
return
|
return null
|
||||||
|
file.seek_end()
|
||||||
entries.append(current_entry)
|
entries.append(current_entry)
|
||||||
file.store_csv_line(current_entry.to_csv_line())
|
file.store_csv_line(current_entry.to_csv_line())
|
||||||
emit_signal("entry_started")
|
return current_entry
|
||||||
|
|
||||||
|
|
||||||
|
func stop_entry(entry_name: String, do_save := true) -> bool:
|
||||||
|
prints("stopping", entry_name)
|
||||||
|
for _entry in entries:
|
||||||
|
var current_time_entry := _entry as TimeEntry
|
||||||
|
if current_time_entry.name == entry_name and not current_time_entry.is_closed:
|
||||||
|
current_time_entry.close()
|
||||||
|
if do_save:
|
||||||
|
save()
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
func toggle_entry(entry_name: String, do_save := true) -> void:
|
||||||
|
if stop_entry(entry_name, do_save):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# warning-ignore:return_value_discarded
|
||||||
|
add_entry(entry_name)
|
||||||
|
|
||||||
|
|
||||||
func update() -> void:
|
func update() -> void:
|
||||||
|
# warning-ignore:integer_division
|
||||||
|
var current_time := Time.get_ticks_msec() / 1000
|
||||||
|
if current_time == _last_update:
|
||||||
|
return
|
||||||
|
_last_update = current_time
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
var time_entry := entry as TimeEntry
|
var time_entry := entry as TimeEntry
|
||||||
if time_entry.is_closed == false:
|
if time_entry.is_closed == false:
|
||||||
@ -74,10 +99,21 @@ func save() -> void:
|
|||||||
if success != OK:
|
if success != OK:
|
||||||
printerr("Could not open file")
|
printerr("Could not open file")
|
||||||
return
|
return
|
||||||
|
prints("saving")
|
||||||
for time_entry in entries:
|
for time_entry in entries:
|
||||||
file.store_csv_line(time_entry.to_csv_line())
|
file.store_csv_line(time_entry.to_csv_line())
|
||||||
|
|
||||||
|
|
||||||
|
func make_items_tree() -> TimeEntryTreeItem:
|
||||||
|
var tree := TimeEntryTreeItem.new()
|
||||||
|
for entry_index in entries.size():
|
||||||
|
var entry := entries[entry_index] as TimeEntry
|
||||||
|
var parts := entry.name.split("/")
|
||||||
|
var repo: TimeEntryTreeItem = tree.get_child(parts, true)
|
||||||
|
repo.append(entry)
|
||||||
|
return tree
|
||||||
|
|
||||||
|
|
||||||
static func restore(file_path: String) -> TimeSheet:
|
static func restore(file_path: String) -> TimeSheet:
|
||||||
var timesheet = load("res://scripts/time_sheet.gd").new()
|
var timesheet = load("res://scripts/time_sheet.gd").new()
|
||||||
timesheet.source_path = file_path
|
timesheet.source_path = file_path
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
extends PanelContainer
|
extends Control
|
||||||
|
|
||||||
var config: ConfigManager = preload("res://config_manager.tres")
|
var config: ConfigManager = preload("res://config_manager.tres")
|
||||||
|
|
||||||
@ -26,11 +26,13 @@ func _ready() -> void:
|
|||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
file_path_file_dialog.connect("file_selected", self, "_on_current_file_selected")
|
file_path_file_dialog.connect("file_selected", self, "_on_current_file_selected")
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
file_path_line_edit.connect("text_submitted", self, "_on_current_file_selected")
|
file_path_line_edit.connect("text_entered", self, "_on_current_file_selected")
|
||||||
|
|
||||||
theme_path_button.connected("pressed", self, "_on_theme_path_button_pressed")
|
# warning-ignore:return_value_discarded
|
||||||
|
theme_path_button.connect("pressed", self, "_on_theme_path_button_pressed")
|
||||||
|
|
||||||
theme_path_file_dialog.connected("file_selected", self, "_on_new_theme_selected")
|
# warning-ignore:return_value_discarded
|
||||||
|
theme_path_file_dialog.connect("file_selected", self, "_on_new_theme_selected")
|
||||||
|
|
||||||
theme_path_file_dialog.hide()
|
theme_path_file_dialog.hide()
|
||||||
file_path_file_dialog.hide()
|
file_path_file_dialog.hide()
|
||||||
@ -46,6 +48,7 @@ func _ready() -> void:
|
|||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
open_data_dir_button.connect("pressed", self, "_on_open_data_dir_button_pressed")
|
open_data_dir_button.connect("pressed", self, "_on_open_data_dir_button_pressed")
|
||||||
|
|
||||||
|
_on_current_file_changed()
|
||||||
|
|
||||||
|
|
||||||
func _on_current_file_changed() -> void:
|
func _on_current_file_changed() -> void:
|
||||||
|
@ -116,19 +116,31 @@ This game uses Godot Engine, available under the following license:
|
|||||||
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
"
|
"
|
||||||
|
|
||||||
[node name="FilePathFileDialog" type="FileDialog" parent="."]
|
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||||
|
|
||||||
|
[node name="FilePathFileDialog" type="FileDialog" parent="CanvasLayer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
margin_right = 442.0
|
anchor_right = 1.0
|
||||||
margin_bottom = 760.0
|
anchor_bottom = 1.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
window_title = "Pick a file"
|
window_title = "Pick a file"
|
||||||
|
resizable = true
|
||||||
|
dialog_hide_on_ok = true
|
||||||
|
mode_overrides_title = false
|
||||||
access = 2
|
access = 2
|
||||||
filters = PoolStringArray( "*.csv ; Comma Separated Files" )
|
filters = PoolStringArray( "*.csv ; Comma Separated Files" )
|
||||||
|
|
||||||
[node name="ThemePathFileDialog" type="FileDialog" parent="."]
|
[node name="ThemePathFileDialog" type="FileDialog" parent="CanvasLayer"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
margin_right = 360.0
|
anchor_right = 1.0
|
||||||
margin_bottom = 760.0
|
anchor_bottom = 1.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
window_title = "Open a File"
|
window_title = "Open a File"
|
||||||
|
resizable = true
|
||||||
|
dialog_hide_on_ok = true
|
||||||
|
mode_overrides_title = false
|
||||||
mode = 0
|
mode = 0
|
||||||
access = 2
|
access = 2
|
||||||
filters = PoolStringArray( "*.theme ; Theme Files" )
|
filters = PoolStringArray( "*.theme ; Theme Files" )
|
||||||
|
@ -18,16 +18,14 @@ func _ready() -> void:
|
|||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
_timer.connect("timeout", self, "_on_timer_timeout")
|
_timer.connect("timeout", self, "_on_timer_timeout")
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
config.connect("file_changed", self, "populate_entries")
|
config.connect("time_sheet_loaded", self, "populate_entries")
|
||||||
# warning-ignore:return_value_discarded
|
|
||||||
config.connect("time_sheet_reloaded", self, "populate_entries")
|
|
||||||
populate_entries()
|
populate_entries()
|
||||||
|
|
||||||
|
|
||||||
func populate_entries() -> void:
|
func populate_entries() -> void:
|
||||||
clear()
|
clear()
|
||||||
var tree_items_root := create_item()
|
var tree_items_root := create_item()
|
||||||
var item_entries_tree := config.timesheet.tree
|
var item_entries_tree := config.timesheet.make_items_tree()
|
||||||
_populate_from_entry(tree_items_root, item_entries_tree)
|
_populate_from_entry(tree_items_root, item_entries_tree)
|
||||||
_timer.start()
|
_timer.start()
|
||||||
#for item in entries:
|
#for item in entries:
|
||||||
@ -56,18 +54,23 @@ func _populate_from_entry(tree_item_root: TreeItem, time_entry_item_root: TimeEn
|
|||||||
for time_entry_name in children:
|
for time_entry_name in children:
|
||||||
var time_entry_item: TimeEntryTreeItem = children[time_entry_name]
|
var time_entry_item: TimeEntryTreeItem = children[time_entry_name]
|
||||||
var item := find_or_create_item(tree_item_root, time_entry_name)
|
var item := find_or_create_item(tree_item_root, time_entry_name)
|
||||||
item.set_metadata(COL.TEXT, time_entry_item)
|
item.set_metadata(COL.TEXT, time_entry_name)
|
||||||
item.set_text(COL.TIME, time_entry_item.get_period())
|
item.set_text(COL.TIME, time_entry_item.get_period())
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
time_entry_item.connect("end_time_updated", self, "_on_time_entry_changed_update_item", [time_entry_item, item])
|
time_entry_item.connect("end_time_updated", self, "_on_time_entry_changed_update_item", [time_entry_item, item])
|
||||||
_populate_from_entry(item, time_entry_item)
|
_populate_from_entry(item, time_entry_item)
|
||||||
|
var has_at_least_one_running_entry := time_entry_item.find_active_time_entry() != null
|
||||||
|
var texture := preload("res://assets/stop_small.svg") \
|
||||||
|
if has_at_least_one_running_entry \
|
||||||
|
else preload("res://assets/play_small.svg")
|
||||||
|
item.add_button(COL.TIME, texture)
|
||||||
var entries = time_entry_item_root.time_entries
|
var entries = time_entry_item_root.time_entries
|
||||||
for entry_item in entries:
|
for entry_item in entries:
|
||||||
var time_entry_item := entry_item as TimeEntryTreeItem
|
var time_entry_item := entry_item as TimeEntryTreeItem
|
||||||
var item := create_item(tree_item_root)
|
var item := create_item(tree_item_root)
|
||||||
var time_entry := time_entry_item.time_entry
|
var time_entry := time_entry_item.time_entry
|
||||||
item.set_text(COL.TEXT, time_entry.start_time.to_string())
|
item.set_text(COL.TEXT, time_entry.start_time.to_string())
|
||||||
item.set_metadata(COL.TEXT, time_entry_item)
|
item.set_metadata(COL.TEXT, time_entry_item.time_entry.name)
|
||||||
item.set_text(COL.TIME, time_entry_item.get_period())
|
item.set_text(COL.TIME, time_entry_item.get_period())
|
||||||
if time_entry.is_closed == false:
|
if time_entry.is_closed == false:
|
||||||
var texture := preload("res://assets/stop_small.svg")
|
var texture := preload("res://assets/stop_small.svg")
|
||||||
@ -81,17 +84,10 @@ func _on_time_entry_changed_update_item(time_entry_item: TimeEntryTreeItem, item
|
|||||||
|
|
||||||
|
|
||||||
func _on_button_pressed(item: TreeItem, _column: int, _id: int) -> void:
|
func _on_button_pressed(item: TreeItem, _column: int, _id: int) -> void:
|
||||||
var time_entry_tree_item: TimeEntryTreeItem = item.get_metadata(COL.TEXT)
|
var task_name: String = item.get_metadata(COL.TEXT)
|
||||||
if time_entry_tree_item == null:
|
if task_name == "":
|
||||||
return
|
return
|
||||||
var time_entry := time_entry_tree_item.time_entry
|
config.timesheet.toggle_entry(task_name)
|
||||||
if time_entry == null:
|
|
||||||
return
|
|
||||||
if time_entry.is_closed:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
time_entry.close()
|
|
||||||
item.erase_button(COL.TIME, 0)
|
|
||||||
|
|
||||||
|
|
||||||
## Unecessary in Godot 4, can bre replaced with get_children()
|
## Unecessary in Godot 4, can bre replaced with get_children()
|
||||||
|
@ -10,6 +10,7 @@ onready var start_button := $"%StartButton" as Button
|
|||||||
onready var timer := $"%Timer" as Timer
|
onready var timer := $"%Timer" as Timer
|
||||||
onready var audio_stream_player := $"%AudioStreamPlayer" as AudioStreamPlayer
|
onready var audio_stream_player := $"%AudioStreamPlayer" as AudioStreamPlayer
|
||||||
|
|
||||||
|
var current_time_entry: TimeEntry
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
@ -18,13 +19,16 @@ func _ready() -> void:
|
|||||||
start_button.toggle_mode = true
|
start_button.toggle_mode = true
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
start_button.connect("toggled", self, "_on_start_button_toggled")
|
start_button.connect("toggled", self, "_on_start_button_toggled")
|
||||||
task_name_line_edit.text = config.last_task_name
|
task_name_line_edit.text = config.current_task_name
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
task_name_line_edit.connect("text_changed", self, "_on_task_name_line_edit_text_changed")
|
task_name_line_edit.connect("text_changed", self, "_on_task_name_line_edit_text_changed")
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
config.connect("entry_started", self, "set_button_as_started")
|
config.connect("time_sheet_loaded", self, "_on_time_sheet_loaded")
|
||||||
|
update_timer_state()
|
||||||
# warning-ignore:return_value_discarded
|
# warning-ignore:return_value_discarded
|
||||||
config.connect("entry_stopped", self, "set_button_as_stopped")
|
#config.connect("entry_started", self, "set_button_as_started")
|
||||||
|
# warning-ignore:return_value_discarded
|
||||||
|
#config.connect("entry_stopped", self, "set_button_as_stopped")
|
||||||
# TODO: connect this:
|
# TODO: connect this:
|
||||||
#time_entries_items_tree.item_selected.connect(
|
#time_entries_items_tree.item_selected.connect(
|
||||||
# func item_selected() -> void:
|
# func item_selected() -> void:
|
||||||
@ -34,25 +38,33 @@ func _ready() -> void:
|
|||||||
|
|
||||||
func _on_timer_timeout() -> void:
|
func _on_timer_timeout() -> void:
|
||||||
config.timesheet.update()
|
config.timesheet.update()
|
||||||
|
time_label.text = current_time_entry.get_period()
|
||||||
time_label.text = config.timesheet.get_period()
|
|
||||||
|
|
||||||
## TODO: change this
|
|
||||||
# var total_elapsed: int = config.timesheet.get_total_elapsed_seconds()
|
|
||||||
# time_entries_items_tree.set_time_elapsed(total_elapsed)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_start_button_toggled(is_on: bool) -> void:
|
func _on_start_button_toggled(_is_on: bool) -> void:
|
||||||
if config.sound_fx_on:
|
if config.sound_fx_on:
|
||||||
audio_stream_player.play()
|
audio_stream_player.play()
|
||||||
if is_on:
|
if current_time_entry != null:
|
||||||
config.timesheet.start_entry(task_name_line_edit.text)
|
config.timesheet.stop_entry(task_name_line_edit.text)
|
||||||
else:
|
else:
|
||||||
config.timesheet.close_entry()
|
config.timesheet.add_entry(task_name_line_edit.text)
|
||||||
|
|
||||||
|
|
||||||
func _on_task_name_line_edit_text_changed(new_text: String) -> void:
|
func _on_task_name_line_edit_text_changed(new_text: String) -> void:
|
||||||
config.last_task_name = new_text
|
config.current_task_name = new_text
|
||||||
|
update_timer_state()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_time_sheet_loaded():
|
||||||
|
print("loaded")
|
||||||
|
update_timer_state()
|
||||||
|
|
||||||
|
func update_timer_state() -> void:
|
||||||
|
current_time_entry = config.timesheet.get_active_entry_from_name(config.current_task_name)
|
||||||
|
if current_time_entry:
|
||||||
|
set_button_as_started()
|
||||||
|
else:
|
||||||
|
set_button_as_stopped()
|
||||||
|
|
||||||
|
|
||||||
func set_button_as_stopped() -> void:
|
func set_button_as_stopped() -> void:
|
||||||
@ -70,9 +82,3 @@ func set_button_as_started() -> void:
|
|||||||
start_button.hint_tooltip = tr(Consts.STOP)
|
start_button.hint_tooltip = tr(Consts.STOP)
|
||||||
start_button.theme_type_variation = Consts.THEME_OVERRIDE_STOP
|
start_button.theme_type_variation = Consts.THEME_OVERRIDE_STOP
|
||||||
timer.start()
|
timer.start()
|
||||||
|
|
||||||
|
|
||||||
func set_initial_state() -> void:
|
|
||||||
if config.timesheet.current_entry != null and config.timesheet.current_entry.closed == false:
|
|
||||||
start_button.set_pressed_no_signal(true)
|
|
||||||
set_button_as_started()
|
|
||||||
|
Loading…
Reference in New Issue
Block a user