Skip to content

Commit 1e99e52

Browse files
author
Roberto De Ioris
committed
added menu builder
1 parent db25e4c commit 1e99e52

File tree

5 files changed

+219
-0
lines changed

5 files changed

+219
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#include "UnrealEnginePythonPrivatePCH.h"
2+
3+
#if WITH_EDITOR
4+
5+
#include "Runtime/Slate/Public/Framework/Commands/UIAction.h"
6+
7+
static PyObject *py_ue_fmenu_builder_begin_section(ue_PyFMenuBuilder *self, PyObject * args) {
8+
char *name;
9+
char *text;
10+
if (!PyArg_ParseTuple(args, "ss:begin_section", &name, &text))
11+
return NULL;
12+
13+
self->menu_builder->BeginSection(name, FText::FromString(UTF8_TO_TCHAR(text)));
14+
15+
Py_INCREF(Py_None);
16+
return Py_None;
17+
}
18+
19+
static PyObject *py_ue_fmenu_builder_end_section(ue_PyFMenuBuilder *self, PyObject * args) {
20+
self->menu_builder->EndSection();
21+
22+
Py_INCREF(Py_None);
23+
return Py_None;
24+
}
25+
26+
static PyObject *py_ue_fmenu_builder_make_widget(ue_PyFMenuBuilder *self, PyObject * args) {
27+
ue_PySWidget *ret = (ue_PySWidget *)PyObject_New(ue_PySWidget, &ue_PySWidgetType);
28+
ret->s_widget_owned = self->menu_builder->MakeWidget();
29+
ret->s_widget = &ret->s_widget_owned.Get();
30+
return (PyObject *)ret;
31+
}
32+
33+
static PyObject *py_ue_fmenu_builder_add_menu_entry(ue_PyFMenuBuilder *self, PyObject * args) {
34+
char *label;
35+
char *tooltip;
36+
PyObject *py_callable;
37+
PyObject *py_obj = nullptr;
38+
if (!PyArg_ParseTuple(args, "ssO|O:add_menu_entry", &label, &tooltip, &py_callable, &py_obj))
39+
return NULL;
40+
41+
if (!PyCallable_Check(py_callable)) {
42+
return PyErr_Format(PyExc_Exception, "argument is not callable");
43+
}
44+
45+
FExecuteAction handler;
46+
UPythonSlateDelegate *py_delegate = NewObject<UPythonSlateDelegate>();
47+
py_delegate->SetPyCallable(py_callable);
48+
py_delegate->AddToRoot();
49+
50+
if (py_obj) {
51+
handler.BindUObject(py_delegate, &UPythonSlateDelegate::ExecuteAction, py_obj);
52+
}
53+
else {
54+
handler.BindUObject(py_delegate, &UPythonSlateDelegate::SimpleExecuteAction);
55+
}
56+
57+
self->menu_builder->AddMenuEntry(FText::FromString(UTF8_TO_TCHAR(label)), FText::FromString(UTF8_TO_TCHAR(tooltip)), FSlateIcon(), FUIAction(handler));
58+
59+
Py_INCREF(Py_None);
60+
return Py_None;
61+
}
62+
63+
static PyMethodDef ue_PyFMenuBuilder_methods[] = {
64+
{ "begin_section", (PyCFunction)py_ue_fmenu_builder_begin_section, METH_VARARGS, "" },
65+
{ "end_section", (PyCFunction)py_ue_fmenu_builder_end_section, METH_VARARGS, "" },
66+
{ "make_widget", (PyCFunction)py_ue_fmenu_builder_make_widget, METH_VARARGS, "" },
67+
{ "add_menu_entry", (PyCFunction)py_ue_fmenu_builder_add_menu_entry, METH_VARARGS, "" },
68+
{ NULL } /* Sentinel */
69+
};
70+
71+
72+
static PyObject *ue_PyFMenuBuilder_str(ue_PyFMenuBuilder *self)
73+
{
74+
return PyUnicode_FromFormat("<unreal_engine.FMenuBuilder '%p'}>",
75+
&self->menu_builder);
76+
}
77+
78+
static void ue_py_fmenu_builder_dealloc(ue_PyFMenuBuilder *self) {
79+
delete(self->menu_builder);
80+
#if PY_MAJOR_VERSION < 3
81+
self->ob_type->tp_free((PyObject*)self);
82+
#else
83+
Py_TYPE(self)->tp_free((PyObject*)self);
84+
#endif
85+
}
86+
87+
static PyTypeObject ue_PyFMenuBuilderType = {
88+
PyVarObject_HEAD_INIT(NULL, 0)
89+
"unreal_engine.FMenuBuilder", /* tp_name */
90+
sizeof(ue_PyFMenuBuilder), /* tp_basicsize */
91+
0, /* tp_itemsize */
92+
(destructor)ue_py_fmenu_builder_dealloc, /* tp_dealloc */
93+
0, /* tp_print */
94+
0, /* tp_getattr */
95+
0, /* tp_setattr */
96+
0, /* tp_reserved */
97+
0, /* tp_repr */
98+
0, /* tp_as_number */
99+
0, /* tp_as_sequence */
100+
0, /* tp_as_mapping */
101+
0, /* tp_hash */
102+
0, /* tp_call */
103+
(reprfunc)ue_PyFMenuBuilder_str, /* tp_str */
104+
0, /* tp_getattro */
105+
0, /* tp_setattro */
106+
0, /* tp_as_buffer */
107+
Py_TPFLAGS_DEFAULT, /* tp_flags */
108+
"Unreal Engine FMenuBuilder", /* tp_doc */
109+
0, /* tp_traverse */
110+
0, /* tp_clear */
111+
0, /* tp_richcompare */
112+
0, /* tp_weaklistoffset */
113+
0, /* tp_iter */
114+
0, /* tp_iternext */
115+
ue_PyFMenuBuilder_methods, /* tp_methods */
116+
};
117+
118+
static int ue_py_fmenu_builder_init(ue_PyFMenuBuilder *self, PyObject *args, PyObject *kwargs) {
119+
self->menu_builder = new FMenuBuilder(true, nullptr);
120+
return 0;
121+
}
122+
123+
124+
void ue_python_init_fmenu_builder(PyObject *ue_module) {
125+
ue_PyFMenuBuilderType.tp_new = PyType_GenericNew;
126+
127+
ue_PyFMenuBuilderType.tp_init = (initproc)ue_py_fmenu_builder_init;
128+
129+
if (PyType_Ready(&ue_PyFMenuBuilderType) < 0)
130+
return;
131+
132+
Py_INCREF(&ue_PyFMenuBuilderType);
133+
PyModule_AddObject(ue_module, "FMenuBuilder", (PyObject *)&ue_PyFMenuBuilderType);
134+
}
135+
136+
#endif
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
#include "UnrealEnginePython.h"
4+
5+
#if WITH_EDITOR
6+
7+
#include "Runtime/Slate/Public/Framework/MultiBox/MultiBoxBuilder.h"
8+
9+
typedef struct {
10+
PyObject_HEAD
11+
/* Type-specific fields go here. */
12+
FMenuBuilder *menu_builder;
13+
} ue_PyFMenuBuilder;
14+
15+
void ue_python_init_fmenu_builder(PyObject *);
16+
17+
#endif

Source/UnrealEnginePython/Private/Slate/UEPySPythonShelf.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO
8585
return -1;
8686
}
8787

88+
if (py_callable_get_context_menu && !PyCallable_Check(py_callable_get_context_menu)) {
89+
PyErr_SetString(PyExc_Exception, "argument is not callable");
90+
return -1;
91+
}
92+
8893
FContentBrowserModule& module = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
8994

9095
FAssetPickerConfig asset_picker_config;
@@ -123,6 +128,15 @@ static int ue_py_spython_shelf_init(ue_PySPythonShelf *self, PyObject *args, PyO
123128
asset_picker_config.OnAssetDoubleClicked = handler;
124129
}
125130

131+
if (py_callable_get_context_menu) {
132+
FOnGetAssetContextMenu handler;
133+
UPythonSlateDelegate *py_delegate = NewObject<UPythonSlateDelegate>();
134+
py_delegate->SetPyCallable(py_callable_get_context_menu);
135+
py_delegate->AddToRoot();
136+
handler.BindUObject(py_delegate, &UPythonSlateDelegate::OnGetAssetContextMenu);
137+
138+
asset_picker_config.OnGetAssetContextMenu = handler;
139+
}
126140

127141
self->s_compound_widget.s_widget.s_widget_owned = module.Get().CreateAssetPicker(asset_picker_config);
128142
self->s_compound_widget.s_widget.s_widget = &self->s_compound_widget.s_widget.s_widget_owned.Get();

Source/UnrealEnginePython/Private/Slate/UEPySlate.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,52 @@ void UPythonSlateDelegate::OnAssetDoubleClicked(const FAssetData& AssetData) {
5555
Py_XDECREF(ret);
5656
}
5757

58+
TSharedPtr<SWidget> UPythonSlateDelegate::OnGetAssetContextMenu(const TArray<FAssetData>& SelectedAssets) {
59+
FScopePythonGIL gil;
60+
61+
PyObject *py_list = PyList_New(0);
62+
for (FAssetData asset : SelectedAssets) {
63+
PyList_Append(py_list, py_ue_new_fassetdata(&asset));
64+
}
65+
66+
PyObject *ret = PyObject_CallFunction(py_callable, (char *)"O", py_list);
67+
Py_DECREF(py_list);
68+
if (!ret) {
69+
unreal_engine_py_log_error();
70+
return nullptr;
71+
}
72+
73+
ue_PySWidget *s_widget = py_ue_is_swidget(ret);
74+
if (!s_widget) {
75+
Py_DECREF(ret);
76+
UE_LOG(LogPython, Error, TEXT("returned value is not a SWidget"));
77+
return nullptr;
78+
}
79+
TSharedPtr<SWidget> value = s_widget->s_widget_owned;
80+
Py_DECREF(ret);
81+
return value;
82+
}
83+
84+
void UPythonSlateDelegate::SimpleExecuteAction() {
85+
FScopePythonGIL gil;
86+
87+
PyObject *ret = PyObject_CallFunction(py_callable, nullptr);
88+
if (!ret) {
89+
unreal_engine_py_log_error();
90+
}
91+
Py_XDECREF(ret);
92+
}
93+
94+
void UPythonSlateDelegate::ExecuteAction(PyObject *py_obj) {
95+
FScopePythonGIL gil;
96+
97+
PyObject *ret = PyObject_CallFunction(py_callable, (char *)"O", py_obj);
98+
if (!ret) {
99+
unreal_engine_py_log_error();
100+
}
101+
Py_XDECREF(ret);
102+
}
103+
58104
TSharedRef<SDockTab> UPythonSlateDelegate::SpawnPythonTab(const FSpawnTabArgs &args) {
59105
TSharedRef<SDockTab> dock_tab = SNew(SDockTab).TabRole(ETabRole::NomadTab);
60106
PyObject *py_dock = (PyObject *)ue_py_get_swidget(dock_tab);
@@ -142,6 +188,7 @@ void ue_python_init_slate(PyObject *module) {
142188
ue_python_init_spython_shelf(module);
143189

144190
ue_python_init_ftab_spawner_entry(module);
191+
ue_python_init_fmenu_builder(module);
145192
}
146193

147194
PyObject *py_unreal_engine_get_editor_window(PyObject *self, PyObject *args) {

Source/UnrealEnginePython/Private/Slate/UEPySlate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "UEPySPythonShelf.h"
4141

4242
#include "UEPyFTabSpawnerEntry.h"
43+
#include "UEPyFMenuBuilder.h"
4344

4445
#include "UEPySlate.generated.h"
4546

@@ -86,6 +87,10 @@ class UPythonSlateDelegate : public UPythonDelegate
8687
TSharedRef<ITableRow> GenerateWidgetForList(TSharedPtr<PyObject> InItem, const TSharedRef<STableViewBase>& OwnerTable);
8788

8889
void OnAssetDoubleClicked(const FAssetData& AssetData);
90+
TSharedPtr<SWidget> OnGetAssetContextMenu(const TArray<FAssetData>& SelectedAssets);
91+
92+
void SimpleExecuteAction();
93+
void ExecuteAction(PyObject *py_obj);
8994
};
9095

9196
#endif

0 commit comments

Comments
 (0)