EditorFileDialog or FileDialog popup from EditorScript - godot

hello Godot's comunity !
I try to integrate in a project a way to import custom json file to generate Tilemap. So I use a EditorScript to generate it but now I want to open a file dialog popup to choose specific file in my disk. So i tried something like this :
tool
extends EditorScript
func _run():
var fileDialog = EditorFileDialog.new()
fileDialog.mode = EditorFileDialog.MODE_OPEN_FILE
fileDialog.access = EditorFileDialog.ACCESS_FILESYSTEM
fileDialog.popup()
...
but nothing happened. Only this in debugger :
scene/gui/control.cpp:2154 - Condition "!is_inside_tree()" is true.
scene/2d/canvas_item.cpp:469 - Condition "!is_inside_tree()" is true. Returned: get_transform()
scene/2d/canvas_item.cpp:939 - Condition "!is_inside_tree()" is true. Returned: Rect2()
scene/gui/control.cpp:2128 - Condition "!is_inside_tree()" is true.
scene/gui/control.cpp:2128 - Condition "!is_inside_tree()" is true.
./scene/main/node.h:269 - Condition "!data.tree" is true. Returned: nullptr
Any idea to do that ?

You need to give the FileDialog a viewport to be shown in.
You do so by getting the viewport of the editor:
var viewport = get_editor_interface().get_editor_viewport()
viewport.add_child(fileDialog)
this will show the dialog, but to ensure you do not run into followup problems:
The editor script is freed soon after running the run function, so connected signals will be not fired after choosing a file. (See here)
In the issue is the solution to secure the connected functions in the script are fired, by keeping the instance in the dialog. After you finished you should free the dialog to make sure you can run the script again, without closing the whole editor.
Complete code would look something like this:
tool
extends EditorScript
var fileDialog : EditorFileDialog = null
func _run():
fileDialog = EditorFileDialog.new()
fileDialog.mode = EditorFileDialog.MODE_OPEN_FILE
fileDialog.access = EditorFileDialog.ACCESS_FILESYSTEM
fileDialog.connect("file_selected", self, "on_file_selected")
var viewport = get_editor_interface().get_editor_viewport()
viewport.add_child(fileDialog)
fileDialog.set_meta("_created_by", self) # needed so the script is not directly freed after the run function. Would disconnect all signals otherwise
fileDialog.popup(Rect2(0,0, 700, 500)) # Giving the dialog a predefined size
print("end")
func on_file_selected(filename : String) :
print(filename)
if (fileDialog != null):
fileDialog.queue_free() # Dialog has to be freed in order for the script to be called again.

Related

Checking if key is being held down

I've got a game where you place towers on the map, it's working fine they can click and the towers place the the build mode is set to false. I Want to allow the player to hold shift down then they can place multiply towers then when they release shift the build mode will change to false but i can't figure it out.
This is what i've got so far but doesn't seem to work as intended
func _input(event):
if event.is_action_released("ui_accept") and build_mode == true:
verify_and_build()
if event.is_action_released("multi_build"):
cancel_build_mode()
I've assigned ui_accept to left click and multi_build to Shift
You are going to get a call to _input per input event. And the event object you get as parameter in _input represents that single input.
If want to to this with the event object, it would be something like this:
func _input(event):
if event.is_action_pressed("multi_build"):
build_mode = true
if event.is_action_released("multi_build"):
build_mode = false
Alternatively, you can use Input to check if the action is currently pressed: Input.is_action_pressed("multi_build").

A problem for selecting a image from the imagepupop dialog

One more question. If I create a image-popup dialog, I find it only works when the frontimage (the top one in the image list). If other image is selected, the program will report "the image used in the expression does not exist". I can not understand the logic behind this error.
The following is a modified code pasted in the answer of the previous question. It can work well if the first image is selected, but the error message appears if the second image is selected.
I use GSM 2.30.xxxx
Class CMyDLG : UIframe
{
TagGroup DLG,DLGItems,imgPop
object Init(object self)
{
DLG = DLGCreateDialog("Test",DLGItems)
imgPop = DLGCreateImagePopup()
DLGItems.DLGAddElement( imgPop )
return self.super.init(DLG)
}
image GetSelectedImage( object self )
{
string selectedImageLabel
imgPop.DLGGetValue(selectedImageLabel) //DLGGetValue can return the label of the image diretly
Result("\n" + selectedImageLabel)
// From the string, get the label
//string label = selectedImageLabel.left( selectedImageLabel.find(":") )
//Result("\n" + label)
// From label, return image
//return FindImageByLabel(label)
return FindImageByLabel(selectedImageLabel)
}
}
// main
{
object dlg = Alloc(CMyDLG).Init()
dlg.Pose()
image selected = dlg.GetSelectedImage()
if ( selected.ImageIsValid() )
{
selected.SetName( "Selected" + random())
selected.ShowImage()
}
else Throw( "Error, nothing selected." )
}
Using the test code on GMS 3.3 it works except for the bug mentioned. I presume it's the same for GMS 2.3 but I haven't verified.
To make sure we test the same thing, here are exact instructions and a break-down:
Start out with two images A and B and A being front-most.
Run script
Don't change anything in the dialog
Press OK
ERROR
The dialog - taggroup does not (yet) hold any value. It possibly should, I consider this a bug.
Start out with two images A and B and A being front-most.
Run script
Click the selection box and select "A" from the drop-down
Press OK
A is correctly selected
Start out with two images A and B and A being front-most.
Run script
Click the selection box and select "B" from the drop-down
Press OK
ERROR
The dialog - taggroup does not (yet) hold any value. It definitly should, I consider this a bug. It is most likely what you were describing?
Start out with two images A and B and A being front-most.
Run script
Click the selection box and select "A" from the drop-down
Click the selection box and select "B" from the drop-down
Press OK
B is correctly selected
To summarize:
Yes, there is a bug and nothing wrong with your script.
The selection box only works after selecting an items for the second time.
The example code (first script) in this answer seems to work on any of the open images when selected.
However, there is the (mentioned) bug that it does not work on first selection, only when you select one image and then another.
If your code fails, please provided a slimmed-down code-example of the failing code so that a mistake can possibly be spotted.

Godot indexing and keyboard both at the same time

I have been trying to develop keyboard and touch indexed game, playable on PC browsers and phone. How can I index?
Tried some samples in Godot Sample menu. None of them helped. Either for keyboard or touch screen
func _unhandled_input(event):
if event is InputEventScreenTouch:
if event.pressed:
# Down
if !_os2own.has(event.index): # Defensively discard index if already known
var ptr_id = _find_free_pointer_id()
state[ptr_id] = event.position
_os2own[event.index] = ptr_id
else:
# Up
if _os2own.has(event.index): # Defensively discard index if not known
var ptr_id = _os2own[event.index]
state.erase(ptr_id)
_os2own.erase(event.index)
return true
Need to touching and clicking game. Both for APK and HTML
For simple input handling (e.g. pressed and released) you'll want to map input to actions. You can add actions via "Project Settings -> Input Map" or InputMap singleton.
From the "Project Settings -> Input Map" you can map mouse, keyboard, and controller input to actions.
For touch screens you can use a TouchScreenButton and set it's action. When pressed or released it will send that action event down the scene tree via _input(). TouchScreenButton hides the logic needed to write in _input() to handle presses and releases, such as: is this finger index new? which finger index moved last frame? is this finger area in bounds of input area? and more. While also having the advantage over a plain Button by also emitting an action and can have no texture as it uses a Shape for input detection.
So this creates an one-to-many relationship from actions to inputs like:
my_action -> left mouse button pressed,
-> controller r1 pressed,
-> right half of touch screen pressed,
-> control-shift-f pressed
Using the action
func _input(event):
if not event.is_action('my_action'):
return
if event.is_action_pressed('my_action'):
start_something()
else:
stop_something()
Going further
Since the post specified keyboard and touch input, I only covered press and release input action mapping. However, you can map complex inputs like gestures to actions as well. You can inherit from InputEvent or any of it's subclasses to extend or create a new event. You can map the new event class to an action and then manually process input to find an event. Then you stop the propagation of the event, form the event from your new event class, and then call Input.parse_input_event(my_new_event) to send it down the tree.
Custom Action
# SomeLeafNode.gd
class MyEvent extends InputEvent:
var my_custom_message = 'Hello, World'
func _ready():
InputMap.add_action('my_event')
InputMap.action_add_event('my_event', MyEvent.new())
func _input(event):
# ... logic to see if event could be MyEvent
get_tree().set_input_as_handled()
var my_event = MyEvent.new()
my_event.my_custom_message = 'Caught my event!'
Input.parse_input_event(my_event)
# SomeInputHandlingGameplayNode.gd
func _input(event):
if event.is_action('my_event'):
print(event.my_custom_message) # prints 'Caught my event!'

How can I detect uiview is an activated viewport

I need to detect whether a Uiview is a standard opened view or if it is an activated viewport on a sheet. Querying the uiview’s view Id returns the Id of the activated viewport's view. I have found no direct way to detect that a uiview is actually a sheet with an activated viewport.
I am already tracking opened views in the view activated event for another purpose. So I considered storing the view Id with the uiview hashcode for later checking that it was indeed a sheetview prior to becoming an activated view. Unfortunately, and I think in opposition to standard use, the uiview hashcode is not stable. Multiple hashcode requests from the uiview object return different values.
Does anyone have a way to detect this condition? I need to be able to use the the methods on the uiview still. So any help to find the actual child windows I would like to relate to the uiview object. The view still says "Sheet: ..." in the title when a view is activated.
TaskDialog mainDialog = new TaskDialog("Hello, viewport check!");
mainDialog.MainInstruction = "Hello, viewport check!";
mainDialog.MainContent =
"Sadly Revit API doesn't automatically know if the user is in an active viewport. "
+ "Please click 'Yes' if your are, or 'No' if your not.";
mainDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink1,
"Yes, I am in an active viewport on a sheet.");
mainDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink2,
"No, I am just in an ordinary view.");
mainDialog.CommonButtons = TaskDialogCommonButtons.Close;
mainDialog.DefaultButton = TaskDialogResult.Close;
TaskDialogResult tResult = mainDialog.Show();
bool YesOrNo = true;
if (TaskDialogResult.CommandLink1 == tResult)
{
YesOrNo = true;
}
else if (TaskDialogResult.CommandLink2 == tResult)
{
YesOrNo = false;
}
else{
return;
}
You can use the ViewSheet GetAllViewports method to determine all the viewports on a given sheet. Using that, you could put together a bi-directional dictionary lookup system map any sheet to all the viewports it hosts and vice versa. That should help solve your task. Here is some example usage:
http://thebuildingcoder.typepad.com/blog/2014/04/determining-the-size-and-location-of-viewports-on-a-sheet.html
Im late to the party - but another way to sense if the user is in a viewport is to investigate the Process.MainWindow title. Something like this (in RevitPythonShell):
import threading, clr
from System.Diagnostics import Process
# need winform libraries for feedback form only
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Form, Label
app = __revit__.Application
doc = __revit__.ActiveUIDocument.Document
ui = __revit__.ActiveUIDocument
def lookAtWindow(activeView):
# Looking for one of three conditions:
# 1. User is on a sheet (ActiveView will be DrawingSheet)
# 2. User is in an active ViewPort on a sheet (ActiveView will NOT be be DrawingSheet, but MainWindowTitle will contain " - [Sheet: " string)
# 3. User is on a View (neither of the previous two conditions)
result = False
if str(activeView.ViewType) == 'DrawingSheet':
result = 'Youre on a sheet'
else:
processes = list(Process.GetProcesses())
for process in processes:
window = process.MainWindowTitle
if window and 'Autodesk Revit '+app.VersionName[-4:] in window and ' - [Sheet: ' in window and ' - '+doc.Title+']' in window:
result = 'I reckon youre in a Viewport'
if not result:
result = 'so you must be in a '+str(activeView.ViewType)
form = Form()
form.Width = 300
form.Height = 100
label = Label()
label.Width = 280
label.Height = 70
label.Text = result
label.Parent = form
form.ShowDialog()
# need to close RevitPythonShell console before checking MainWindowTitle, so run on timer
threading.Timer(1, lookAtWindow, [ui.ActiveView]).start()
__window__.Close()

Hold-Hover drop down menu Delay Time

Im creating a drop down menu and i want to know if there is anyway to implement the following:
I need to keep the sub-menu open for like 1 sec if the user moves the mouse away from the tab he selected. Much likely like in current intel web page www.intel.com , here u hover over menu, but if u take the mouse away from the tab or the sub-menu is opens it takes a few to hide the sub menu.
Im using .mouseover from jquery to show the menu (a div) but i cant find a way to make it stay for a few moments.
Thanks in advance
This may be of service
What is the JavaScript version of sleep()?
If you want to do something in the interim setTimeout() takes the arguments as shown where continue execution is another subroutine. If you just want this one tab to work this way have mouseover call doStuff and set a boolean (e.g. mouseStillIn) to TRUE. When the mouse exits set this boolean to FALSE, call a recursive function everytime mouseStillIn is TRUE.
e.g.
var mouseStillIn : boolean = false;
function MouseIn()
{
mouseStillIn=true;
CheckMouse();
}
function CheckMouse()
{
if(mouseStillIn)
{
setTimeout(CheckMouse, 1000);
}
}
function MouseOut()
{
mouseStillIn=false;
}

Resources