forked from Mirrors/openclonk
Qt Editor: Shape properties
parent
f594737d7e
commit
881534bee0
|
@ -811,6 +811,8 @@ if(WITH_QT_EDITOR)
|
|||
src/editor/C4ConsoleQt.h
|
||||
src/editor/C4ConsoleQtState.cpp
|
||||
src/editor/C4ConsoleQtState.h
|
||||
src/editor/C4ConsoleQtShapes.cpp
|
||||
src/editor/C4ConsoleQtShapes.h
|
||||
src/editor/C4ConsoleQtPropListViewer.cpp
|
||||
src/editor/C4ConsoleQtPropListViewer.h
|
||||
src/editor/C4ConsoleQtObjectListViewer.cpp
|
||||
|
|
|
@ -87,6 +87,10 @@ public:
|
|||
void OnStartGame();
|
||||
void ClearGamePointers();
|
||||
|
||||
// TODO some qt editor stuff is in state and needs to be public
|
||||
// Once other editors are removed, C4ConsoleGUI, C4ConsoleQt and C4ConsoleQtState should be reorganized
|
||||
State *GetState() const { return state; }
|
||||
|
||||
friend class C4ConsoleQtMainWindow;
|
||||
friend class C4ToolsDlg;
|
||||
#else
|
||||
|
|
|
@ -353,10 +353,10 @@ bool C4ConsoleGUI::CreateNewScenario(StdStrBuf *out_filename)
|
|||
state->SetObjectSelection(selection);
|
||||
}
|
||||
|
||||
void C4ConsoleGUI::ClearGamePointers()
|
||||
{
|
||||
void C4ConsoleGUI::ClearGamePointers()
|
||||
{
|
||||
state->ClearGamePointers();
|
||||
}
|
||||
}
|
||||
|
||||
void C4ToolsDlg::UpdateToolCtrls()
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "C4Include.h"
|
||||
#include "script/C4Value.h"
|
||||
#include "editor/C4ConsoleQtPropListViewer.h"
|
||||
#include "editor/C4ConsoleQtState.h"
|
||||
#include "editor/C4Console.h"
|
||||
#include "object/C4Object.h"
|
||||
#include "object/C4DefList.h"
|
||||
|
@ -127,7 +128,7 @@ void C4PropertyDelegateInt::SetEditorData(QWidget *editor, const C4Value &val) c
|
|||
spinBox->setValue(val.getInt());
|
||||
}
|
||||
|
||||
void C4PropertyDelegateInt::SetModelData(QWidget *editor, const C4PropertyPath &property_path) const
|
||||
void C4PropertyDelegateInt::SetModelData(QObject *editor, const C4PropertyPath &property_path) const
|
||||
{
|
||||
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
|
||||
spinBox->interpretText();
|
||||
|
@ -188,7 +189,7 @@ void C4PropertyDelegateColor::SetEditorData(QWidget *aeditor, const C4Value &val
|
|||
editor->last_value = val;
|
||||
}
|
||||
|
||||
void C4PropertyDelegateColor::SetModelData(QWidget *aeditor, const C4PropertyPath &property_path) const
|
||||
void C4PropertyDelegateColor::SetModelData(QObject *aeditor, const C4PropertyPath &property_path) const
|
||||
{
|
||||
Editor *editor = static_cast<Editor *>(aeditor);
|
||||
property_path.SetProperty(editor->last_value);
|
||||
|
@ -199,7 +200,7 @@ QWidget *C4PropertyDelegateColor::CreateEditor(const class C4PropertyDelegateFac
|
|||
Editor *editor;
|
||||
std::unique_ptr<Editor> peditor((editor = new Editor(parent)));
|
||||
connect(editor->button, &QPushButton::pressed, this, [editor, this]() {
|
||||
QColor clr = QColorDialog::getColor(QColor(editor->last_value.getInt()));
|
||||
QColor clr = QColorDialog::getColor(QColor(editor->last_value.getInt()), editor, QString(), QColorDialog::ShowAlphaChannel);
|
||||
editor->last_value.SetInt(clr.rgba());
|
||||
this->SetEditorData(editor, editor->last_value); // force update on display
|
||||
emit EditingDoneSignal(editor);
|
||||
|
@ -209,7 +210,7 @@ QWidget *C4PropertyDelegateColor::CreateEditor(const class C4PropertyDelegateFac
|
|||
|
||||
QString C4PropertyDelegateColor::GetDisplayString(const C4Value &v, C4Object *obj) const
|
||||
{
|
||||
return QString("#%1").arg(v.getInt(), 8, 16, QChar('0'));
|
||||
return QString("#%1").arg(uint32_t(v.getInt()), 8, 16, QChar('0'));
|
||||
}
|
||||
|
||||
QColor C4PropertyDelegateColor::GetDisplayTextColor(const C4Value &val, class C4Object *obj) const
|
||||
|
@ -329,8 +330,7 @@ void C4PropertyDelegateEnum::UpdateEditorParameter(C4PropertyDelegateEnum::Edito
|
|||
if (idx < 0 || idx >= options.size()) return;
|
||||
const Option &option = options[idx];
|
||||
// Lazy-resolve parameter delegate
|
||||
if (!option.adelegate && option.adelegate_val.GetType() != C4V_Nil)
|
||||
option.adelegate = factory->GetDelegateByValue(option.adelegate_val);
|
||||
EnsureOptionDelegateResolved(option);
|
||||
// Create editor if needed
|
||||
if (option.adelegate)
|
||||
{
|
||||
|
@ -375,7 +375,7 @@ void C4PropertyDelegateEnum::SetEditorData(QWidget *aeditor, const C4Value &val)
|
|||
editor->updating = false;
|
||||
}
|
||||
|
||||
void C4PropertyDelegateEnum::SetModelData(QWidget *aeditor, const C4PropertyPath &property_path) const
|
||||
void C4PropertyDelegateEnum::SetModelData(QObject *aeditor, const C4PropertyPath &property_path) const
|
||||
{
|
||||
// Fetch value from editor
|
||||
Editor *editor = static_cast<Editor*>(aeditor);
|
||||
|
@ -428,6 +428,13 @@ void C4PropertyDelegateEnum::UpdateOptionIndex(C4PropertyDelegateEnum::Editor *e
|
|||
emit EditorValueChangedSignal(editor);
|
||||
}
|
||||
|
||||
void C4PropertyDelegateEnum::EnsureOptionDelegateResolved(const Option &option) const
|
||||
{
|
||||
// Lazy-resolve parameter delegate
|
||||
if (!option.adelegate && option.adelegate_val.GetType() != C4V_Nil)
|
||||
option.adelegate = factory->GetDelegateByValue(option.adelegate_val);
|
||||
}
|
||||
|
||||
QString C4PropertyDelegateEnum::GetDisplayString(const C4Value &v, class C4Object *obj) const
|
||||
{
|
||||
// Display string from value
|
||||
|
@ -440,11 +447,10 @@ QString C4PropertyDelegateEnum::GetDisplayString(const C4Value &v, class C4Objec
|
|||
else
|
||||
{
|
||||
// Value found: Display option string plus parameter
|
||||
Option option = options[idx];
|
||||
const Option &option = options[idx];
|
||||
QString result = option.name->GetCStr();
|
||||
// Lazy-resolve parameter delegate
|
||||
if (!option.adelegate && option.adelegate_val.GetType() != C4V_Nil)
|
||||
option.adelegate = factory->GetDelegateByValue(option.adelegate_val);
|
||||
EnsureOptionDelegateResolved(option);
|
||||
if (option.adelegate)
|
||||
{
|
||||
C4Value param_val = v;
|
||||
|
@ -460,6 +466,23 @@ QString C4PropertyDelegateEnum::GetDisplayString(const C4Value &v, class C4Objec
|
|||
}
|
||||
}
|
||||
|
||||
const C4PropertyDelegateShape *C4PropertyDelegateEnum::GetShapeDelegate(const C4Value &val) const
|
||||
{
|
||||
// Does this delegate own a shape? Forward decision into selected option.
|
||||
int32_t option_idx = GetOptionByValue(val);
|
||||
if (option_idx < 0) return nullptr;
|
||||
const Option &option = options[option_idx];
|
||||
EnsureOptionDelegateResolved(option);
|
||||
if (!option.adelegate) return nullptr;
|
||||
C4Value param_val = val;
|
||||
if (option.value_key.Get())
|
||||
{
|
||||
C4PropList *vp = val.getPropList();
|
||||
if (vp) vp->GetPropertyByS(option.value_key, ¶m_val);
|
||||
}
|
||||
return option.adelegate->GetShapeDelegate(param_val);
|
||||
}
|
||||
|
||||
C4PropertyDelegateDef::C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, C4PropList *props)
|
||||
: C4PropertyDelegateEnum(factory, props)
|
||||
{
|
||||
|
@ -544,7 +567,7 @@ void C4PropertyDelegateC4ValueInput::SetEditorData(QWidget *aeditor, const C4Val
|
|||
editor->edit->setText(val.GetDataString().getData());
|
||||
}
|
||||
|
||||
void C4PropertyDelegateC4ValueInput::SetModelData(QWidget *aeditor, const C4PropertyPath &property_path) const
|
||||
void C4PropertyDelegateC4ValueInput::SetModelData(QObject *aeditor, const C4PropertyPath &property_path) const
|
||||
{
|
||||
// Only set model data when pressing Enter explicitely; not just when leaving
|
||||
Editor *editor = static_cast<Editor *>(aeditor);
|
||||
|
@ -578,6 +601,50 @@ QWidget *C4PropertyDelegateC4ValueInput::CreateEditor(const class C4PropertyDele
|
|||
}
|
||||
|
||||
|
||||
/* Areas shown in viewport */
|
||||
|
||||
C4PropertyDelegateShape::C4PropertyDelegateShape(const class C4PropertyDelegateFactory *factory, C4PropList *props)
|
||||
: C4PropertyDelegate(factory, props), clr(0xffff0000), can_move_center(false)
|
||||
{
|
||||
if (props)
|
||||
{
|
||||
shape_type = props->GetPropertyStr(P_Type);
|
||||
clr = props->GetPropertyInt(P_Color) | 0xff000000;
|
||||
can_move_center = props->GetPropertyBool(P_CanMoveCenter);
|
||||
}
|
||||
}
|
||||
|
||||
void C4PropertyDelegateShape::SetModelData(QObject *editor, const C4PropertyPath &property_path) const
|
||||
{
|
||||
C4ConsoleQtShape *shape = static_cast<C4ConsoleQtShape *>(editor);
|
||||
property_path.SetProperty(shape->GetValue());
|
||||
}
|
||||
|
||||
void C4PropertyDelegateShape::Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const
|
||||
{
|
||||
// Background color
|
||||
if (option.state & QStyle::State_Selected)
|
||||
painter->fillRect(option.rect, option.palette.highlight());
|
||||
else
|
||||
painter->fillRect(option.rect, option.palette.base());
|
||||
// Draw a frame in shape color
|
||||
painter->save();
|
||||
QColor frame_color = QColor(QRgb(clr & 0xffffff));
|
||||
int32_t width = Clamp<int32_t>(option.rect.height() / 8, 2, 6) &~1;
|
||||
QPen rect_pen(QBrush(frame_color), width, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
|
||||
painter->setPen(rect_pen);
|
||||
QRect inner_rect = option.rect.adjusted(width / 2, width / 2, -width / 2, -width / 2);
|
||||
if (shape_type && shape_type->GetData() == "circle")
|
||||
{
|
||||
painter->drawEllipse(inner_rect);
|
||||
if (can_move_center) painter->drawPoint(inner_rect.center());
|
||||
}
|
||||
else
|
||||
{
|
||||
painter->drawRect(inner_rect);
|
||||
}
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
|
||||
/* Delegate factory: Create delegates based on the C4Value type */
|
||||
|
@ -594,6 +661,7 @@ C4PropertyDelegate *C4PropertyDelegateFactory::CreateDelegateByString(const C4St
|
|||
if (str->GetData() == "bool") return new C4PropertyDelegateBool(this, props);
|
||||
if (str->GetData() == "has_effect") return new C4PropertyDelegateHasEffect(this, props);
|
||||
if (str->GetData() == "c4valueenum") return new C4PropertyDelegateC4ValueEnum(this, props);
|
||||
if (str->GetData() == "rect" || str->GetData() == "circle") return new C4PropertyDelegateShape(this, props);
|
||||
if (str->GetData() == "any") return new C4PropertyDelegateC4ValueInput(this, props);
|
||||
// unknown type
|
||||
return NULL;
|
||||
|
@ -679,18 +747,23 @@ void C4PropertyDelegateFactory::setModelData(QWidget *editor, QAbstractItemModel
|
|||
C4PropertyDelegate *d = GetDelegateByIndex(index);
|
||||
if (!d) return;
|
||||
C4ConsoleQtPropListModel::Property *prop = static_cast<C4ConsoleQtPropListModel::Property *>(index.internalPointer());
|
||||
C4PropList *props = prop->parent_proplist.getPropList();
|
||||
if (props)
|
||||
{
|
||||
// Compose set command
|
||||
C4PropertyPath path(prop->parent_proplist.GetDataString().getData());
|
||||
C4PropertyPath subpath;
|
||||
if (d->GetSetFunction())
|
||||
subpath = C4PropertyPath(path, d->GetSetFunction(), C4PropertyPath::PPT_SetFunction);
|
||||
else
|
||||
subpath = C4PropertyPath(path, prop->key->GetCStr());
|
||||
d->SetModelData(editor, subpath);
|
||||
}
|
||||
SetPropertyData(d, editor, prop);
|
||||
}
|
||||
|
||||
void C4PropertyDelegateFactory::SetPropertyData(const C4PropertyDelegate *d, QObject *editor, C4ConsoleQtPropListModel::Property *editor_prop) const
|
||||
{
|
||||
// Safety: Ensure target properties still exist
|
||||
C4PropList *target_props = editor_prop->parent_proplist.getPropList();
|
||||
if (!target_props) return;
|
||||
// Compose set command
|
||||
C4PropertyPath path(editor_prop->parent_proplist.GetDataString().getData());
|
||||
C4PropertyPath subpath;
|
||||
if (d->GetSetFunction())
|
||||
subpath = C4PropertyPath(path, d->GetSetFunction(), C4PropertyPath::PPT_SetFunction);
|
||||
else
|
||||
subpath = C4PropertyPath(path, editor_prop->key->GetCStr());
|
||||
// Set according to delegate
|
||||
d->SetModelData(editor, subpath);
|
||||
}
|
||||
|
||||
QWidget *C4PropertyDelegateFactory::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
|
@ -700,14 +773,17 @@ QWidget *C4PropertyDelegateFactory::createEditor(QWidget *parent, const QStyleOp
|
|||
C4ConsoleQtPropListModel::Property *prop = static_cast<C4ConsoleQtPropListModel::Property *>(index.internalPointer());
|
||||
prop->about_to_edit = true;
|
||||
QWidget *editor = d->CreateEditor(this, parent, option);
|
||||
// Connect value change signals
|
||||
// Connect value change signals (if editing is possible for this property)
|
||||
// For some reason, commitData needs a non-const pointer
|
||||
connect(d, &C4PropertyDelegate::EditorValueChangedSignal, editor, [editor, this](QWidget *signal_editor) {
|
||||
if (signal_editor == editor) const_cast<C4PropertyDelegateFactory *>(this)->EditorValueChanged(editor);
|
||||
});
|
||||
connect(d, &C4PropertyDelegate::EditingDoneSignal, editor, [editor, this](QWidget *signal_editor) {
|
||||
if (signal_editor == editor) const_cast<C4PropertyDelegateFactory *>(this)->EditingDone(editor);
|
||||
});
|
||||
if (editor)
|
||||
{
|
||||
connect(d, &C4PropertyDelegate::EditorValueChangedSignal, editor, [editor, this](QWidget *signal_editor) {
|
||||
if (signal_editor == editor) const_cast<C4PropertyDelegateFactory *>(this)->EditorValueChanged(editor);
|
||||
});
|
||||
connect(d, &C4PropertyDelegate::EditingDoneSignal, editor, [editor, this](QWidget *signal_editor) {
|
||||
if (signal_editor == editor) const_cast<C4PropertyDelegateFactory *>(this)->EditingDone(editor);
|
||||
});
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
|
@ -724,6 +800,26 @@ QSize C4PropertyDelegateFactory::sizeHint(const QStyleOptionViewItem &option, co
|
|||
return QSize(100, height);
|
||||
}
|
||||
|
||||
void C4PropertyDelegateFactory::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
// Delegate has custom painting?
|
||||
C4ConsoleQtPropListModel::Property *prop = static_cast<C4ConsoleQtPropListModel::Property *>(index.internalPointer());
|
||||
C4PropertyDelegate *d = GetDelegateByIndex(index);
|
||||
if (d && prop && d->HasCustomPaint())
|
||||
{
|
||||
C4Value val;
|
||||
C4PropList *props = prop->parent_proplist.getPropList();
|
||||
if (props)
|
||||
{
|
||||
d->GetPropertyValue(props, prop->key, &val);
|
||||
d->Paint(painter, option, val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Otherwise use default paint implementation
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
}
|
||||
|
||||
|
||||
/* Proplist table view */
|
||||
|
||||
|
@ -749,23 +845,41 @@ bool C4ConsoleQtPropListModel::AddPropertyGroup(C4PropList *add_proplist, int32_
|
|||
properties.props.resize(new_properties.size());
|
||||
for (int32_t i = 0; i < new_properties.size(); ++i)
|
||||
{
|
||||
properties.props[i].parent_proplist.SetPropList(target_proplist);
|
||||
properties.props[i].key = NULL;
|
||||
properties.props[i].display_name = NULL;
|
||||
properties.props[i].delegate_info.Set0(); // default C4Value delegate
|
||||
properties.props[i].delegate = NULL; // init when needed
|
||||
properties.props[i].group_idx = group_index;
|
||||
Property *prop = &properties.props[i];
|
||||
prop->parent_proplist.SetPropList(target_proplist);
|
||||
prop->key = NULL;
|
||||
prop->display_name = NULL;
|
||||
prop->delegate_info.Set0(); // default C4Value delegate
|
||||
prop->group_idx = group_index;
|
||||
C4Value published_prop_val;
|
||||
add_proplist->GetPropertyByS(new_properties[i], &published_prop_val);
|
||||
C4PropList *published_prop = published_prop_val.getPropList();
|
||||
if (published_prop)
|
||||
{
|
||||
properties.props[i].key = published_prop->GetPropertyStr(P_Key);
|
||||
properties.props[i].display_name = published_prop->GetPropertyStr(P_Name);
|
||||
properties.props[i].delegate_info.SetPropList(published_prop);
|
||||
prop->key = published_prop->GetPropertyStr(P_Key);
|
||||
prop->display_name = published_prop->GetPropertyStr(P_Name);
|
||||
prop->delegate_info.SetPropList(published_prop);
|
||||
}
|
||||
if (!prop->key) properties.props[i].key = ::Strings.RegString(new_properties[i]->GetCStr() + strlen(editor_prop_prefix));
|
||||
if (!prop->display_name) properties.props[i].display_name = ::Strings.RegString(new_properties[i]->GetCStr() + strlen(editor_prop_prefix));
|
||||
prop->delegate = delegate_factory->GetDelegateByValue(prop->delegate_info);
|
||||
C4Value v;
|
||||
prop->delegate->GetPropertyValue(target_proplist, prop->key, &v);
|
||||
// Connect editable shape to property
|
||||
const C4PropertyDelegateShape *new_shape_delegate = prop->delegate->GetShapeDelegate(v);
|
||||
if (new_shape_delegate != prop->shape_delegate)
|
||||
{
|
||||
prop->shape_delegate = new_shape_delegate;
|
||||
if (new_shape_delegate)
|
||||
{
|
||||
C4ConsoleQtShape *shape = ::Console.EditCursor.GetShapes()->CreateShape(target_proplist->GetObject(), published_prop, v);
|
||||
C4PropertyDelegateFactory *factory = this->delegate_factory;
|
||||
connect(shape, &C4ConsoleQtShape::ShapeDragged, new_shape_delegate, [factory, new_shape_delegate, shape, prop]() {
|
||||
factory->SetPropertyData(new_shape_delegate, shape, prop);
|
||||
});
|
||||
prop->shape.Set(shape);
|
||||
}
|
||||
}
|
||||
if (!properties.props[i].key) properties.props[i].key = ::Strings.RegString(new_properties[i]->GetCStr() + strlen(editor_prop_prefix));
|
||||
if (!properties.props[i].display_name) properties.props[i].display_name = ::Strings.RegString(new_properties[i]->GetCStr() + strlen(editor_prop_prefix));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -821,6 +935,8 @@ void C4ConsoleQtPropListModel::SetPropList(class C4PropList *new_proplist)
|
|||
internal_properties.props[i].delegate_info.Set0(); // default C4Value delegate
|
||||
internal_properties.props[i].delegate = NULL; // init when needed
|
||||
internal_properties.props[i].group_idx = num_groups;
|
||||
internal_properties.props[i].shape.Clear();
|
||||
internal_properties.props[i].shape_delegate = nullptr;
|
||||
}
|
||||
++num_groups;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,12 @@
|
|||
#include "C4Include.h" // needed for automoc
|
||||
#include "editor/C4ConsoleGUI.h" // for glew.h
|
||||
#include "editor/C4ConsoleQt.h"
|
||||
#include "editor/C4ConsoleQtShapes.h"
|
||||
#include "script/C4Value.h"
|
||||
|
||||
class C4ConsoleQtPropListModel;
|
||||
struct C4ConsoleQtPropListModelProperty;
|
||||
|
||||
// Path to a property, like e.g. Object(123).foo.bar[456].baz
|
||||
// Used to allow proper synchronization of property setting
|
||||
class C4PropertyPath
|
||||
|
@ -64,15 +68,17 @@ public:
|
|||
virtual ~C4PropertyDelegate() { }
|
||||
|
||||
virtual void SetEditorData(QWidget *editor, const C4Value &val) const = 0;
|
||||
virtual void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const = 0;
|
||||
virtual void SetModelData(QObject *editor, const C4PropertyPath &property_path) const = 0;
|
||||
virtual QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const = 0;
|
||||
virtual void UpdateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option) const;
|
||||
virtual bool GetPropertyValue(C4PropList *props, C4String *key, C4Value *out_val) const;
|
||||
virtual QString GetDisplayString(const C4Value &val, class C4Object *obj) const;
|
||||
virtual QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const;
|
||||
virtual QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const;
|
||||
|
||||
const char *GetSetFunction() const { return set_function.Get() ? set_function->GetCStr() : NULL; } // get name of setter function for this property
|
||||
const char *GetSetFunction() const { return set_function.Get() ? set_function->GetCStr() : nullptr; } // get name of setter function for this property
|
||||
virtual const class C4PropertyDelegateShape *GetShapeDelegate(const C4Value &val) const { return nullptr; }
|
||||
virtual bool HasCustomPaint() const { return false; }
|
||||
virtual void Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const { }
|
||||
|
||||
signals:
|
||||
void EditorValueChangedSignal(QWidget *editor) const;
|
||||
|
@ -87,7 +93,7 @@ public:
|
|||
C4PropertyDelegateInt(const class C4PropertyDelegateFactory *factory, C4PropList *props);
|
||||
|
||||
void SetEditorData(QWidget *editor, const C4Value &val) const override;
|
||||
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
|
||||
void SetModelData(QObject *editor, const C4PropertyPath &property_path) const override;
|
||||
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const override;
|
||||
};
|
||||
|
||||
|
@ -113,7 +119,7 @@ public:
|
|||
C4PropertyDelegateColor(const class C4PropertyDelegateFactory *factory, C4PropList *props);
|
||||
|
||||
void SetEditorData(QWidget *editor, const C4Value &val) const override;
|
||||
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
|
||||
void SetModelData(QObject *editor, const C4PropertyPath &property_path) const override;
|
||||
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const override;
|
||||
QString GetDisplayString(const C4Value &v, C4Object *obj) const override;
|
||||
QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const override;
|
||||
|
@ -175,13 +181,15 @@ public:
|
|||
void AddConstOption(C4String *name, const C4Value &val);
|
||||
|
||||
void SetEditorData(QWidget *editor, const C4Value &val) const override;
|
||||
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
|
||||
void SetModelData(QObject *editor, const C4PropertyPath &property_path) const override;
|
||||
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const override;
|
||||
QString GetDisplayString(const C4Value &val, class C4Object *obj) const override;
|
||||
const class C4PropertyDelegateShape *GetShapeDelegate(const C4Value &val) const override; // Forward to parameter of selected option
|
||||
|
||||
private:
|
||||
int32_t GetOptionByValue(const C4Value &val) const;
|
||||
void UpdateEditorParameter(C4PropertyDelegateEnum::Editor *editor) const;
|
||||
void EnsureOptionDelegateResolved(const Option &option) const;
|
||||
|
||||
public slots:
|
||||
void UpdateOptionIndex(Editor *editor, int idx) const;
|
||||
|
@ -244,10 +252,27 @@ public:
|
|||
C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory *factory, C4PropList *props) : C4PropertyDelegate(factory, props) { }
|
||||
|
||||
void SetEditorData(QWidget *editor, const C4Value &val) const override;
|
||||
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
|
||||
void SetModelData(QObject *editor, const C4PropertyPath &property_path) const override;
|
||||
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const override;
|
||||
};
|
||||
|
||||
// areas shown in viewport
|
||||
class C4PropertyDelegateShape : public C4PropertyDelegate
|
||||
{
|
||||
C4RefCntPointer<C4String> shape_type;
|
||||
uint32_t clr;
|
||||
bool can_move_center;
|
||||
public:
|
||||
C4PropertyDelegateShape(const class C4PropertyDelegateFactory *factory, C4PropList *props);
|
||||
|
||||
void SetEditorData(QWidget *editor, const C4Value &val) const override { } // TODO maybe implement update?
|
||||
void SetModelData(QObject *editor, const C4PropertyPath &property_path) const override;
|
||||
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const override { return NULL; }
|
||||
const C4PropertyDelegateShape *GetShapeDelegate(const C4Value &val) const override { return this; }
|
||||
bool HasCustomPaint() const override { return true; }
|
||||
void Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const override;
|
||||
};
|
||||
|
||||
class C4PropertyDelegateFactory : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -264,17 +289,41 @@ public:
|
|||
C4PropertyDelegate *GetDelegateByValue(const C4Value &val) const;
|
||||
|
||||
void ClearDelegates();
|
||||
void SetPropertyData(const C4PropertyDelegate *d, QObject *editor, C4ConsoleQtPropListModelProperty *editor_prop) const;
|
||||
|
||||
private:
|
||||
void EditorValueChanged(QWidget *editor);
|
||||
void EditingDone(QWidget *editor);
|
||||
|
||||
protected:
|
||||
// Model callbacks forwarded to actual delegates
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
// One property in the prop list model view
|
||||
struct C4ConsoleQtPropListModelProperty
|
||||
{
|
||||
C4PropertyPath property_path;
|
||||
C4Value parent_proplist;
|
||||
C4RefCntPointer<C4String> display_name;
|
||||
C4RefCntPointer<C4String> key;
|
||||
C4Value delegate_info;
|
||||
C4PropertyDelegate *delegate;
|
||||
bool about_to_edit;
|
||||
|
||||
// Parent group index
|
||||
int32_t group_idx;
|
||||
|
||||
// Each property may be connected to one shape shown in the viewport for editing
|
||||
C4ConsoleQtShapeHolder shape;
|
||||
const C4PropertyDelegate *shape_delegate;
|
||||
|
||||
C4ConsoleQtPropListModelProperty() : delegate(nullptr), about_to_edit(false), group_idx(-1), shape_delegate(nullptr) {}
|
||||
};
|
||||
|
||||
// Prop list view implemented as a model view
|
||||
|
@ -283,19 +332,7 @@ class C4ConsoleQtPropListModel : public QAbstractItemModel
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Property
|
||||
{
|
||||
C4PropertyPath property_path;
|
||||
C4Value parent_proplist;
|
||||
C4RefCntPointer<C4String> display_name;
|
||||
C4RefCntPointer<C4String> key;
|
||||
C4Value delegate_info;
|
||||
C4PropertyDelegate *delegate;
|
||||
bool about_to_edit;
|
||||
int32_t group_idx;
|
||||
|
||||
Property() : delegate(NULL), about_to_edit(false), group_idx(-1) {}
|
||||
};
|
||||
typedef C4ConsoleQtPropListModelProperty Property;
|
||||
struct PropertyGroup
|
||||
{
|
||||
QString name;
|
||||
|
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2013, The OpenClonk Team and contributors
|
||||
*
|
||||
* Distributed under the terms of the ISC license; see accompanying file
|
||||
* "COPYING" for details.
|
||||
*
|
||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
||||
* See accompanying file "TRADEMARK" for details.
|
||||
*
|
||||
* To redistribute this file separately, substitute the full license texts
|
||||
* for the above references.
|
||||
*/
|
||||
|
||||
/* Editable shapes in the viewports (like e.g. AI guard range rectangles) */
|
||||
|
||||
#include "C4Include.h"
|
||||
#include "editor/C4Console.h"
|
||||
#include "editor/C4ConsoleQtState.h"
|
||||
#include "editor/C4ConsoleQtShapes.h"
|
||||
#include "graphics/C4FacetEx.h"
|
||||
#include "object/C4Object.h"
|
||||
|
||||
/* Generic shape */
|
||||
|
||||
C4ConsoleQtShape::C4ConsoleQtShape(C4Object *for_obj, C4PropList *props)
|
||||
: is_relative(false), dragging_border(-1), border_color(0xffff0000)
|
||||
{
|
||||
rel_obj.SetPropList(for_obj);
|
||||
if (props)
|
||||
{
|
||||
is_relative = props->GetPropertyBool(P_Relative);
|
||||
border_color = props->GetPropertyInt(P_Color) | 0xff000000;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t C4ConsoleQtShape::GetBorderColor(int32_t border_index, bool dragging_border_is_bitmask) const
|
||||
{
|
||||
// Return shape color, or dragged border color if index is the border currently being dragged
|
||||
if (IsDragging())
|
||||
if ((dragging_border == border_index) || (dragging_border_is_bitmask && (dragging_border & border_index)))
|
||||
return 0xffffffff;
|
||||
return border_color;
|
||||
}
|
||||
|
||||
int32_t C4ConsoleQtShape::AbsX(int32_t rel_x) const
|
||||
{
|
||||
if (is_relative)
|
||||
{
|
||||
C4Object *obj = rel_obj.getObj();
|
||||
if (obj) rel_x += obj->GetX();
|
||||
}
|
||||
return rel_x;
|
||||
}
|
||||
|
||||
int32_t C4ConsoleQtShape::AbsY(int32_t rel_y) const
|
||||
{
|
||||
if (is_relative)
|
||||
{
|
||||
C4Object *obj = rel_obj.getObj();
|
||||
if (obj) rel_y += obj->GetY();
|
||||
}
|
||||
return rel_y;
|
||||
}
|
||||
|
||||
|
||||
/* Rectangular shape*/
|
||||
|
||||
C4ConsoleQtRect::C4ConsoleQtRect(C4Object *for_obj, C4PropList *props, const C4Value &val)
|
||||
: C4ConsoleQtShape(for_obj, props), left(0), top(0), right(10), bottom(10)
|
||||
{
|
||||
// Expect rect to be given as [left,top,width,height]
|
||||
C4ValueArray *varr = val.getArray();
|
||||
if (varr && varr->GetSize() >= 4)
|
||||
{
|
||||
left = varr->GetItem(0).getInt();
|
||||
top = varr->GetItem(1).getInt();
|
||||
right = left + varr->GetItem(2).getInt()-1; // right/bottom borders are drawn inclusively
|
||||
bottom = top + varr->GetItem(3).getInt()-1;
|
||||
}
|
||||
}
|
||||
|
||||
bool C4ConsoleQtRect::IsHit(int32_t x, int32_t y, int32_t hit_range, Qt::CursorShape *drag_cursor, int32_t *drag_border)
|
||||
{
|
||||
// Current border pos
|
||||
int32_t left = AbsX(this->left), top = AbsY(this->top);
|
||||
int32_t right = AbsX(this->right), bottom = AbsY(this->bottom);
|
||||
// Distance to each border
|
||||
int32_t dleft = Abs<int32_t>(left - x);
|
||||
int32_t dtop = Abs<int32_t>(top - y);
|
||||
int32_t dright = Abs<int32_t>(right - x);
|
||||
int32_t dbottom = Abs<int32_t>(bottom - y);
|
||||
// In box at all?
|
||||
if (x < left - hit_range || y < top - hit_range || x > right + hit_range || y > bottom + hit_range)
|
||||
return false;
|
||||
// Border hit?
|
||||
bool hit_left = (dleft <= hit_range && dleft < dright);
|
||||
bool hit_top = (dtop <= hit_range && dtop < dbottom);
|
||||
bool hit_right = (!hit_left && dright <= hit_range);
|
||||
bool hit_bottom = (!hit_top && dbottom <= hit_range);
|
||||
// Compose cursor and drag border
|
||||
int32_t idrag_border = (hit_left * CNAT_Left) + (hit_top * CNAT_Top) + (hit_right * CNAT_Right) + (hit_bottom * CNAT_Bottom);
|
||||
if (idrag_border) *drag_border = idrag_border;
|
||||
if (hit_left || hit_right)
|
||||
if (hit_top || hit_bottom)
|
||||
*drag_cursor = (hit_left == hit_top) ? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor;
|
||||
else
|
||||
*drag_cursor = Qt::SizeHorCursor;
|
||||
else if (hit_top || hit_bottom)
|
||||
*drag_cursor = Qt::SizeVerCursor;
|
||||
return !!idrag_border;
|
||||
}
|
||||
|
||||
void C4ConsoleQtRect::Draw(class C4TargetFacet &cgo, float line_width)
|
||||
{
|
||||
float left = float(AbsX(this->left)) + cgo.X - cgo.TargetX;
|
||||
float top = float(AbsY(this->top)) + cgo.Y - cgo.TargetY;
|
||||
float right = float(AbsX(this->right)) + cgo.X - cgo.TargetX;
|
||||
float bottom = float(AbsY(this->bottom)) + cgo.Y - cgo.TargetY;
|
||||
pDraw->DrawLineDw(cgo.Surface, left, top, right, top, GetBorderColor(CNAT_Top, true), line_width);
|
||||
pDraw->DrawLineDw(cgo.Surface, right, top, right, bottom, GetBorderColor(CNAT_Right, true), line_width);
|
||||
pDraw->DrawLineDw(cgo.Surface, right, bottom, left, bottom, GetBorderColor(CNAT_Bottom, true), line_width);
|
||||
pDraw->DrawLineDw(cgo.Surface, left, bottom, left, top, GetBorderColor(CNAT_Left, true), line_width);
|
||||
}
|
||||
|
||||
void C4ConsoleQtRect::Drag(int32_t x, int32_t y, int32_t dx, int32_t dy)
|
||||
{
|
||||
if (dragging_border & CNAT_Left) left += dx;
|
||||
if (dragging_border & CNAT_Top) top += dy;
|
||||
if (dragging_border & CNAT_Right) right += dx;
|
||||
if (dragging_border & CNAT_Bottom) bottom += dy;
|
||||
if (left > right) std::swap(left, right);
|
||||
if (top > bottom) std::swap(top, bottom);
|
||||
}
|
||||
|
||||
C4Value C4ConsoleQtRect::GetValue() const
|
||||
{
|
||||
// Return array: Convert left/top/right/bottom (inclusive) to left/top/width/height
|
||||
C4ValueArray *pos_array = new C4ValueArray(4);
|
||||
pos_array->SetItem(0, C4VInt(left));
|
||||
pos_array->SetItem(1, C4VInt(top));
|
||||
pos_array->SetItem(2, C4VInt(right - left + 1));
|
||||
pos_array->SetItem(3, C4VInt(bottom - top + 1));
|
||||
return C4VArray(pos_array);
|
||||
}
|
||||
|
||||
|
||||
/* Circle shape */
|
||||
|
||||
C4ConsoleQtCircle::C4ConsoleQtCircle(class C4Object *for_obj, C4PropList *props, const C4Value &val)
|
||||
: C4ConsoleQtShape(for_obj, props), radius(10), cx(0), cy(0), can_move_center(false)
|
||||
{
|
||||
if (props)
|
||||
{
|
||||
can_move_center = props->GetPropertyBool(P_CanMoveCenter);
|
||||
}
|
||||
// If center is moveable, expect value as [radius, center_x, center_y]
|
||||
// Otherwise just radius
|
||||
if (can_move_center)
|
||||
{
|
||||
C4ValueArray *aval = val.getArray();
|
||||
if (aval && aval->GetSize() == 3)
|
||||
{
|
||||
radius = aval->GetItem(0).getInt();
|
||||
cx = aval->GetItem(1).getInt();
|
||||
cy = aval->GetItem(2).getInt();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
radius = val.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
bool C4ConsoleQtCircle::IsHit(int32_t x, int32_t y, int32_t hit_range, Qt::CursorShape *drag_cursor, int32_t *drag_border)
|
||||
{
|
||||
// Get relative circle center pos
|
||||
x -= AbsX(cx);
|
||||
y -= AbsY(cy);
|
||||
int32_t r = x*x + y*y;
|
||||
// Is on circle border? (Higher priority than center to allow resizing circle from 0 radius)
|
||||
if (Inside<int32_t>(r, (radius - hit_range)*(radius - hit_range), (radius + hit_range)*(radius + hit_range)))
|
||||
{
|
||||
// Cursor by position on 60 deg circle segments
|
||||
if (x * 58 / 100 / (y+!y)) // tan(30) ~= 0.58
|
||||
*drag_cursor = Qt::CursorShape::SizeHorCursor;
|
||||
else if (y * 58 / 100 / (x+!x))
|
||||
*drag_cursor = Qt::CursorShape::SizeVerCursor;
|
||||
else if (x*y > 0)
|
||||
*drag_cursor = Qt::CursorShape::SizeFDiagCursor;
|
||||
else
|
||||
*drag_cursor = Qt::CursorShape::SizeBDiagCursor;
|
||||
*drag_border = 0;
|
||||
return true;
|
||||
}
|
||||
// Circle center?
|
||||
if (can_move_center && r <= hit_range*hit_range)
|
||||
{
|
||||
*drag_cursor = Qt::CursorShape::SizeAllCursor;
|
||||
*drag_border = 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void C4ConsoleQtCircle::Draw(class C4TargetFacet &cgo, float line_width)
|
||||
{
|
||||
// Circle
|
||||
pDraw->DrawCircleDw(cgo.Surface, AbsX(cx) + cgo.X - cgo.TargetX, AbsY(cy) + cgo.Y - cgo.TargetY, radius, GetBorderColor(0, false), line_width);
|
||||
// Center if moveable
|
||||
if (can_move_center)
|
||||
pDraw->DrawCircleDw(cgo.Surface, AbsX(cx) + cgo.X - cgo.TargetX, AbsY(cy) + cgo.Y - cgo.TargetY, line_width*3, GetBorderColor(1, false), line_width);
|
||||
}
|
||||
|
||||
void C4ConsoleQtCircle::C4ConsoleQtCircle::Drag(int32_t x, int32_t y, int32_t dx, int32_t dy)
|
||||
{
|
||||
if (dragging_border == 0)
|
||||
{
|
||||
x -= AbsX(cx);
|
||||
y -= AbsY(cy);
|
||||
radius = int32_t(sqrt(double(x*x + y*y)));
|
||||
}
|
||||
else if (dragging_border == 1)
|
||||
{
|
||||
cx += dx;
|
||||
cy += dy;
|
||||
}
|
||||
}
|
||||
|
||||
C4Value C4ConsoleQtCircle::GetValue() const
|
||||
{
|
||||
// Return single value for non-center-adjustable circles; return [radius, cx, cy] otherwise
|
||||
if (can_move_center)
|
||||
{
|
||||
C4ValueArray *pos_array = new C4ValueArray(3);
|
||||
pos_array->SetItem(0, C4VInt(radius));
|
||||
pos_array->SetItem(1, C4VInt(cx));
|
||||
pos_array->SetItem(2, C4VInt(cy));
|
||||
return C4VArray(pos_array);
|
||||
}
|
||||
else
|
||||
{
|
||||
return C4VInt(radius);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Shape list */
|
||||
|
||||
C4ConsoleQtShape *C4ConsoleQtShapes::CreateShape(class C4Object *for_obj, C4PropList *props, const C4Value &val)
|
||||
{
|
||||
C4String *type = props->GetPropertyStr(P_Type);
|
||||
if (!type) return nullptr;
|
||||
C4ConsoleQtShape *shape = nullptr;
|
||||
if (type->GetData() == "rect") shape = new C4ConsoleQtRect(for_obj, props, val);
|
||||
else if (type->GetData() == "circle") shape = new C4ConsoleQtCircle(for_obj, props, val);
|
||||
return shape;
|
||||
}
|
||||
|
||||
void C4ConsoleQtShapes::AddShape(C4ConsoleQtShape *shape)
|
||||
{
|
||||
if (shape) shapes.emplace_back(shape);
|
||||
}
|
||||
|
||||
void C4ConsoleQtShapes::RemoveShape(C4ConsoleQtShape *shape)
|
||||
{
|
||||
// Remove from list and currently moving shape
|
||||
shapes.remove_if([shape](auto &it) { return it.get() == shape; });
|
||||
if (dragging_shape == shape) dragging_shape = NULL;
|
||||
}
|
||||
|
||||
void C4ConsoleQtShapes::ClearShapes()
|
||||
{
|
||||
shapes.clear();
|
||||
dragging_shape = NULL;
|
||||
drag_cursor = Qt::CursorShape::ArrowCursor;
|
||||
}
|
||||
|
||||
void C4ConsoleQtShapes::Draw(C4TargetFacet &cgo)
|
||||
{
|
||||
// Draw all shapes with at least 1px line width
|
||||
ZoomDataStackItem zdsi(cgo.X, cgo.Y, cgo.Zoom);
|
||||
float line_width = std::max<float>(1.0f, 1.0f / cgo.Zoom);
|
||||
for (auto &shape : shapes) shape->Draw(cgo, line_width);
|
||||
}
|
||||
|
||||
bool C4ConsoleQtShapes::MouseDown(float x, float y, float hit_range)
|
||||
{
|
||||
// Check for shape hit and start dragging if a shape is in hit range
|
||||
int32_t hit_range_int = std::max(int32_t(hit_range + 0.5f), 1); // Using integer hit ranges for now
|
||||
// Ensure no leftover other shape
|
||||
if (dragging_shape) MouseUp(x, y);
|
||||
int32_t drag_border=-1;
|
||||
for (auto &shape : shapes)
|
||||
{
|
||||
if (shape->IsHit(x, y, hit_range_int, &drag_cursor, &drag_border))
|
||||
{
|
||||
dragging_shape = shape.get();
|
||||
dragging_shape->StartDragging(drag_border);
|
||||
drag_x = x;
|
||||
drag_y = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void C4ConsoleQtShapes::MouseMove(float x, float y, bool left_down, float hit_range)
|
||||
{
|
||||
// Check for shape hit and start dragging if a shape is in hit range
|
||||
int32_t hit_range_int = std::max(int32_t(hit_range + 0.5f), 1); // Using integer hit ranges for now
|
||||
// move down move: Execute shape dragging (full pixels only)
|
||||
if (dragging_shape && left_down)
|
||||
{
|
||||
int32_t dx = int32_t(round(x - drag_x)),
|
||||
dy = int32_t(round(y - drag_y));
|
||||
if (dx || dy)
|
||||
{
|
||||
drag_x += dx;
|
||||
drag_y += dy;
|
||||
dragging_shape->Drag(drag_x, drag_y, dx, dy);
|
||||
}
|
||||
}
|
||||
else if (!left_down)
|
||||
{
|
||||
// Just moving around: Update cursor
|
||||
drag_cursor = Qt::CursorShape::ArrowCursor;
|
||||
int32_t ignored;
|
||||
for (auto &shape : shapes) if (shape->IsHit(x, y, hit_range_int, &drag_cursor, &ignored)) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular move: Reset drag cursor
|
||||
drag_cursor = Qt::CursorShape::ArrowCursor;
|
||||
}
|
||||
}
|
||||
|
||||
void C4ConsoleQtShapes::MouseUp(float x, float y)
|
||||
{
|
||||
// Stop dragging
|
||||
if (dragging_shape)
|
||||
{
|
||||
dragging_shape->emit ShapeDragged();
|
||||
dragging_shape->StopDragging();
|
||||
dragging_shape = NULL;
|
||||
drag_cursor = Qt::CursorShape::ArrowCursor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Shape pointer holder class */
|
||||
|
||||
void C4ConsoleQtShapeHolder::Clear()
|
||||
{
|
||||
if (shape)
|
||||
{
|
||||
::Console.EditCursor.GetShapes()->RemoveShape(shape);
|
||||
shape = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void C4ConsoleQtShapeHolder::Set(C4ConsoleQtShape *new_shape)
|
||||
{
|
||||
Clear();
|
||||
shape = new_shape;
|
||||
if (shape) ::Console.EditCursor.GetShapes()->AddShape(shape);
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2013, The OpenClonk Team and contributors
|
||||
*
|
||||
* Distributed under the terms of the ISC license; see accompanying file
|
||||
* "COPYING" for details.
|
||||
*
|
||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
||||
* See accompanying file "TRADEMARK" for details.
|
||||
*
|
||||
* To redistribute this file separately, substitute the full license texts
|
||||
* for the above references.
|
||||
*/
|
||||
|
||||
/* Editable shapes in the viewports (like e.g. AI guard range rectangles) */
|
||||
|
||||
#ifndef INC_C4ConsoleQtShapes
|
||||
#define INC_C4ConsoleQtShapes
|
||||
#ifdef WITH_QT_EDITOR
|
||||
|
||||
#include "editor/C4ConsoleGUI.h" // for glew.h
|
||||
#include "editor/C4ConsoleQt.h"
|
||||
#include "script/C4Value.h"
|
||||
|
||||
// Shape base class
|
||||
class C4ConsoleQtShape : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
C4Value rel_obj; // Object relative to which shape is defined
|
||||
bool is_relative;
|
||||
int32_t dragging_border;
|
||||
uint32_t border_color;
|
||||
|
||||
protected:
|
||||
// Return shape color, or dragged border color if index is the border currently being dragged
|
||||
uint32_t GetBorderColor(int32_t border_index, bool dragging_border_is_bitmask) const;
|
||||
public:
|
||||
C4ConsoleQtShape(class C4Object *for_obj, C4PropList *props);
|
||||
|
||||
virtual bool IsHit(int32_t x, int32_t y, int32_t hit_range, Qt::CursorShape *drag_cursor, int32_t *drag_border) = 0;
|
||||
virtual void Draw(class C4TargetFacet &cgo, float line_width) = 0;
|
||||
|
||||
// Coordinate transform: Add object
|
||||
int32_t AbsX(int32_t rel_x) const;
|
||||
int32_t AbsY(int32_t rel_x) const;
|
||||
|
||||
// Start/stop dragging
|
||||
void StartDragging(int32_t border) { dragging_border = border; }
|
||||
void StopDragging() { dragging_border = -1; }
|
||||
virtual void Drag(int32_t x, int32_t y, int32_t dx, int32_t dy) = 0;
|
||||
bool IsDragging() const { return dragging_border != -1; }
|
||||
|
||||
// Return current shape as C4Value to be stored back to property
|
||||
virtual C4Value GetValue() const = 0;
|
||||
|
||||
signals:
|
||||
void ShapeDragged();
|
||||
};
|
||||
|
||||
// Rectangular shape
|
||||
class C4ConsoleQtRect : public C4ConsoleQtShape
|
||||
{
|
||||
private:
|
||||
int32_t left, top, right, bottom;
|
||||
public:
|
||||
C4ConsoleQtRect(class C4Object *for_obj, C4PropList *props, const C4Value &val);
|
||||
|
||||
bool IsHit(int32_t x, int32_t y, int32_t hit_range, Qt::CursorShape *drag_cursor, int32_t *drag_border) override;
|
||||
void Draw(class C4TargetFacet &cgo, float line_width) override;
|
||||
void Drag(int32_t x, int32_t y, int32_t dx, int32_t dy) override;
|
||||
|
||||
C4Value GetValue() const override;
|
||||
};
|
||||
|
||||
// Circular shape
|
||||
class C4ConsoleQtCircle : public C4ConsoleQtShape
|
||||
{
|
||||
private:
|
||||
int32_t radius;
|
||||
int32_t cx, cy;
|
||||
bool can_move_center;
|
||||
public:
|
||||
C4ConsoleQtCircle(class C4Object *for_obj, C4PropList *props, const C4Value &val);
|
||||
|
||||
bool IsHit(int32_t x, int32_t y, int32_t hit_range, Qt::CursorShape *drag_cursor, int32_t *drag_border) override;
|
||||
void Draw(class C4TargetFacet &cgo, float line_width) override;
|
||||
void Drag(int32_t x, int32_t y, int32_t dx, int32_t dy) override;
|
||||
|
||||
C4Value GetValue() const override;
|
||||
};
|
||||
|
||||
/* List of all current editable Qt shapes */
|
||||
class C4ConsoleQtShapes
|
||||
{
|
||||
typedef std::list<std::unique_ptr<C4ConsoleQtShape> > ShapeList;
|
||||
ShapeList shapes;
|
||||
C4ConsoleQtShape *dragging_shape;
|
||||
Qt::CursorShape drag_cursor;
|
||||
float drag_x, drag_y;
|
||||
public:
|
||||
C4ConsoleQtShapes() : dragging_shape(nullptr), drag_x(0), drag_y(0), drag_cursor(Qt::CursorShape::ArrowCursor) { }
|
||||
|
||||
C4ConsoleQtShape *CreateShape(class C4Object *for_obj, C4PropList *props, const C4Value &val);
|
||||
void AddShape(C4ConsoleQtShape *shape);
|
||||
void RemoveShape(C4ConsoleQtShape *shape);
|
||||
void ClearShapes();
|
||||
|
||||
// Mouse callbacks from viewports to execute shape dragging
|
||||
bool MouseDown(float x, float y, float hit_range); // return true if a shape was hit
|
||||
void MouseMove(float x, float y, bool left_down, float hit_range); // move move: Execute shape dragging
|
||||
void MouseUp(float x, float y);
|
||||
|
||||
void Draw(C4TargetFacet &cgo);
|
||||
|
||||
// Dragging info
|
||||
bool HasDragCursor() const { return drag_cursor != Qt::CursorShape::ArrowCursor; }
|
||||
Qt::CursorShape GetDragCursor() const { return drag_cursor; }
|
||||
};
|
||||
|
||||
/* Shape holder class: Handles adding/removal of shape to shapes list */
|
||||
class C4ConsoleQtShapeHolder
|
||||
{
|
||||
C4ConsoleQtShape *shape;
|
||||
|
||||
public:
|
||||
C4ConsoleQtShapeHolder() : shape(nullptr) {}
|
||||
~C4ConsoleQtShapeHolder() { Clear(); }
|
||||
|
||||
void Clear();
|
||||
void Set(C4ConsoleQtShape *new_shape);
|
||||
C4ConsoleQtShape *Get() const { return shape; }
|
||||
};
|
||||
|
||||
|
||||
#endif // WITH_QT_EDITOR
|
||||
#endif // INC_C4ConsoleQtShapes
|
|
@ -23,6 +23,7 @@
|
|||
#include "editor/C4ConsoleQtDefinitionListViewer.h"
|
||||
#include "editor/C4ConsoleQtNewScenario.h"
|
||||
#include "editor/C4ConsoleQtViewport.h"
|
||||
#include "editor/C4ConsoleQtShapes.h"
|
||||
#include "editor/C4Console.h"
|
||||
#include "platform/StdRegistry.h"
|
||||
#include "landscape/C4Landscape.h"
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
C4ConsoleQtMainWindow(class C4AbstractApp *app, class C4ConsoleGUIState *state);
|
||||
|
||||
void closeEvent(class QCloseEvent *event) override;
|
||||
class C4ConsoleGUIState *GetConsoleState() const { return state; }
|
||||
|
||||
public slots:
|
||||
// Toolbar items
|
||||
|
@ -220,6 +221,8 @@ public:
|
|||
void HideWelcomeScreen();
|
||||
|
||||
void ClearGamePointers();
|
||||
|
||||
void Draw(C4TargetFacet &cgo);
|
||||
};
|
||||
|
||||
class C4ConsoleGUI::State : public C4ConsoleGUIState
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "script/C4Value.h"
|
||||
#include "editor/C4ConsoleQtViewport.h"
|
||||
#include "editor/C4ConsoleQtState.h"
|
||||
#include "editor/C4Console.h"
|
||||
#include "editor/C4ConsoleQtShapes.h"
|
||||
#include "game/C4Viewport.h"
|
||||
#include "editor/C4ViewportWindow.h"
|
||||
#include "editor/C4Console.h"
|
||||
|
@ -67,8 +69,8 @@ void C4ConsoleQtViewportView::mouseMoveEvent(QMouseEvent *eventMove)
|
|||
}
|
||||
else
|
||||
{
|
||||
this->setCursor(Qt::CrossCursor);
|
||||
cvp->pWindow->EditCursorMove(eventMove->x(), eventMove->y(), GetShiftWParam());
|
||||
this->setCursor(::Console.EditCursor.GetShapes()->HasDragCursor() ? ::Console.EditCursor.GetShapes()->GetDragCursor() : Qt::CrossCursor);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
#include "game/C4Game.h"
|
||||
#include "object/C4GameObjects.h"
|
||||
#include "control/C4GameControl.h"
|
||||
#ifdef WITH_QT_EDITOR
|
||||
#include "editor/C4ConsoleQtShapes.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "res/resource.h"
|
||||
|
@ -124,7 +127,7 @@ int32_t C4EditCursorSelection::ObjectCount() const
|
|||
}
|
||||
|
||||
|
||||
C4EditCursor::C4EditCursor()
|
||||
C4EditCursor::C4EditCursor() : shapes(new C4ConsoleQtShapes())
|
||||
{
|
||||
Default();
|
||||
}
|
||||
|
@ -223,8 +226,11 @@ bool C4EditCursor::Move(float iX, float iY, DWORD dwKeyState)
|
|||
{
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
case C4CNS_ModeEdit:
|
||||
#ifdef WITH_QT_EDITOR
|
||||
shapes->MouseMove(X, Y, Hold, 3.0f /* TODO: Depend on zoom */);
|
||||
#endif
|
||||
// Hold
|
||||
if (!DragFrame && Hold)
|
||||
if (!DragFrame && Hold && !DragShape)
|
||||
{
|
||||
MoveSelection(ftofix(xoff),ftofix(yoff));
|
||||
UpdateDropTarget(dwKeyState);
|
||||
|
@ -361,6 +367,14 @@ bool C4EditCursor::LeftButtonDown(DWORD dwKeyState)
|
|||
}
|
||||
else
|
||||
{
|
||||
// Click on shape?
|
||||
#ifdef WITH_QT_EDITOR
|
||||
if (shapes->MouseDown(X, Y, 3.0f /* TODO: Depend on zoom */))
|
||||
{
|
||||
DragShape = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
// Click on unselected: select single
|
||||
if (Target)
|
||||
{
|
||||
|
@ -475,9 +489,13 @@ bool C4EditCursor::LeftButtonUp(DWORD dwKeyState)
|
|||
}
|
||||
|
||||
// Release
|
||||
#ifdef WITH_QT_EDITOR
|
||||
shapes->MouseUp(X, Y);
|
||||
#endif
|
||||
Hold=false;
|
||||
DragFrame=false;
|
||||
DragLine=false;
|
||||
DragShape = false;
|
||||
DropTarget=NULL;
|
||||
// Update
|
||||
UpdateStatusBar();
|
||||
|
@ -596,6 +614,10 @@ void C4EditCursor::Draw(C4TargetFacet &cgo)
|
|||
{
|
||||
ZoomDataStackItem zdsi(cgo.X, cgo.Y, cgo.Zoom);
|
||||
float line_width = std::max<float>(1.0f, 1.0f / cgo.Zoom);
|
||||
#ifdef WITH_QT_EDITOR
|
||||
// Draw shapes of selection
|
||||
shapes->Draw(cgo);
|
||||
#endif
|
||||
// Draw selection marks
|
||||
for (C4Value &obj : selection)
|
||||
{
|
||||
|
@ -747,7 +769,7 @@ void C4EditCursor::Default()
|
|||
#ifdef USE_WIN32_WINDOWS
|
||||
hMenu=NULL;
|
||||
#endif
|
||||
Hold=DragFrame=DragLine=false;
|
||||
Hold=DragFrame=DragLine=DragShape=false;
|
||||
selection.clear();
|
||||
creator_def = NULL;
|
||||
creator_overlay = NULL;
|
||||
|
@ -765,6 +787,9 @@ void C4EditCursor::Clear()
|
|||
selection.clear();
|
||||
Console.PropertyDlgUpdate(selection, false);
|
||||
creator_overlay.reset(NULL);
|
||||
#ifdef WITH_QT_EDITOR
|
||||
shapes->ClearShapes(); // Should really be empty already
|
||||
#endif
|
||||
}
|
||||
|
||||
bool C4EditCursor::SetMode(int32_t iMode)
|
||||
|
|
|
@ -56,7 +56,7 @@ protected:
|
|||
bool has_mouse_hover;
|
||||
int32_t Mode;
|
||||
float X,Y,X2,Y2;
|
||||
bool Hold,DragFrame,DragLine;
|
||||
bool Hold,DragFrame,DragLine,DragShape;
|
||||
C4Object *Target,*DropTarget;
|
||||
class C4Def *creator_def;
|
||||
std::unique_ptr<C4GraphicsOverlay> creator_overlay;
|
||||
|
@ -82,6 +82,9 @@ protected:
|
|||
#endif
|
||||
// Selection may either be any number of objects or a single non-object prop list
|
||||
C4EditCursorSelection selection;
|
||||
#ifdef WITH_QT_EDITOR
|
||||
std::unique_ptr<class C4ConsoleQtShapes> shapes;
|
||||
#endif
|
||||
public:
|
||||
void Default();
|
||||
void Clear();
|
||||
|
@ -112,6 +115,7 @@ public:
|
|||
bool AltDown();
|
||||
bool AltUp();
|
||||
void SetMouseHover(bool h) { has_mouse_hover = h; }
|
||||
class C4ConsoleQtShapes *GetShapes() const { return shapes.get(); }
|
||||
protected:
|
||||
void UpdateStatusBar();
|
||||
void ApplyCreateObject(bool contained);
|
||||
|
|
|
@ -219,5 +219,5 @@ void C4ViewportWindow::Close()
|
|||
}
|
||||
void C4ViewportWindow::EditCursorMove(int X, int Y, uint32_t state)
|
||||
{
|
||||
Console.EditCursor.Move(cvp->GetViewX() + X / cvp->Zoom, cvp->GetViewY() + Y / cvp->Zoom, state);
|
||||
Console.EditCursor.Move(cvp->WindowToGameX(X), cvp->WindowToGameY(Y), state);
|
||||
}
|
||||
|
|
|
@ -347,7 +347,7 @@ void C4Viewport::Draw(C4TargetFacet &cgo0, bool fDrawGame, bool fDrawOverlay)
|
|||
// Draw overlay
|
||||
C4ST_STARTNEW(OvrStat, "C4Viewport::Draw: Overlay")
|
||||
|
||||
if (Application.isEditor) Console.EditCursor.Draw(cgo);
|
||||
if (Application.isEditor) ::Console.EditCursor.Draw(cgo);
|
||||
|
||||
// Game messages
|
||||
C4ST_STARTNEW(MsgStat, "C4Viewport::DrawOverlay: Messages")
|
||||
|
|
|
@ -77,6 +77,10 @@ public:
|
|||
/** Return y-position of the center of viewport in landscape coordinates */
|
||||
float GetViewCenterY() { return viewY + ViewHgt/Zoom/2; }
|
||||
|
||||
// Convert window coordinates to game coordinates
|
||||
float WindowToGameX(int32_t win_x) { return GetViewX() + float(win_x) / Zoom; }
|
||||
float WindowToGameY(int32_t win_y) { return GetViewY() + float(win_y) / Zoom; }
|
||||
|
||||
/** Scroll the viewport by x,y */
|
||||
void ScrollView(float byX, float byY);
|
||||
/** Set the view position. */
|
||||
|
|
|
@ -606,7 +606,7 @@ C4CustomKey::C4CustomKey(const CodeList &rDefCodes, const char *szName, C4KeySco
|
|||
}
|
||||
|
||||
C4CustomKey::C4CustomKey(const C4CustomKey &rCpy, bool fCopyCallbacks)
|
||||
: Codes(rCpy.Codes), DefaultCodes(rCpy.DefaultCodes), Scope(rCpy.Scope), Name(), uiPriority(rCpy.uiPriority), iRef(0)
|
||||
: Codes(rCpy.Codes), DefaultCodes(rCpy.DefaultCodes), Scope(rCpy.Scope), Name(), uiPriority(rCpy.uiPriority), iRef(0), is_down(false)
|
||||
{
|
||||
Name.Copy(rCpy.GetName());
|
||||
if (fCopyCallbacks)
|
||||
|
|
|
@ -5061,7 +5061,7 @@ void C4Object::ResetProperty(C4String * k)
|
|||
return C4PropListNumbered::ResetProperty(k);
|
||||
}
|
||||
|
||||
bool C4Object::GetPropertyByS(C4String *k, C4Value *pResult) const
|
||||
bool C4Object::GetPropertyByS(const C4String *k, C4Value *pResult) const
|
||||
{
|
||||
if (k >= &Strings.P[0] && k < &Strings.P[P_LAST])
|
||||
{
|
||||
|
|
|
@ -398,7 +398,7 @@ public:
|
|||
virtual C4Object const * GetObject() const { return this; }
|
||||
virtual void SetPropertyByS(C4String * k, const C4Value & to);
|
||||
virtual void ResetProperty(C4String * k);
|
||||
virtual bool GetPropertyByS(C4String *k, C4Value *pResult) const;
|
||||
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const;
|
||||
virtual C4ValueArray * GetProperties() const;
|
||||
};
|
||||
|
||||
|
|
|
@ -535,7 +535,7 @@ void C4Effect::ResetProperty(C4String * k)
|
|||
C4PropListNumbered::ResetProperty(k);
|
||||
}
|
||||
|
||||
bool C4Effect::GetPropertyByS(C4String *k, C4Value *pResult) const
|
||||
bool C4Effect::GetPropertyByS(const C4String *k, C4Value *pResult) const
|
||||
{
|
||||
if (k >= &Strings.P[0] && k < &Strings.P[P_LAST])
|
||||
{
|
||||
|
|
|
@ -130,7 +130,7 @@ public:
|
|||
virtual C4Effect * GetEffect() { return this; }
|
||||
virtual void SetPropertyByS(C4String * k, const C4Value & to);
|
||||
virtual void ResetProperty(C4String * k);
|
||||
virtual bool GetPropertyByS(C4String *k, C4Value *pResult) const;
|
||||
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const;
|
||||
virtual C4ValueArray * GetProperties() const;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -587,9 +587,8 @@ C4Effect * C4PropList::GetEffect()
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
template<> template<>
|
||||
unsigned int C4Set<C4Property>::Hash<C4String *>(C4String * const & e)
|
||||
unsigned int C4Set<C4Property>::Hash<const C4String *>(C4String const * const & e)
|
||||
{
|
||||
assert(e);
|
||||
unsigned int hash = 4, tmp;
|
||||
|
@ -606,6 +605,18 @@ unsigned int C4Set<C4Property>::Hash<C4String *>(C4String * const & e)
|
|||
return hash;
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
unsigned int C4Set<C4Property>::Hash<C4String *>(C4String * const & e)
|
||||
{
|
||||
return Hash<const C4String *>(e);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
bool C4Set<C4Property>::Equals<const C4String *>(C4Property const & a, C4String const * const & b)
|
||||
{
|
||||
return a.Key == b;
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
bool C4Set<C4Property>::Equals<C4String *>(C4Property const & a, C4String * const & b)
|
||||
{
|
||||
|
@ -618,7 +629,7 @@ unsigned int C4Set<C4Property>::Hash<C4Property>(C4Property const & p)
|
|||
return C4Set<C4Property>::Hash(p.Key);
|
||||
}
|
||||
|
||||
bool C4PropList::GetPropertyByS(C4String * k, C4Value *pResult) const
|
||||
bool C4PropList::GetPropertyByS(const C4String * k, C4Value *pResult) const
|
||||
{
|
||||
if (Properties.Has(k))
|
||||
{
|
||||
|
@ -723,6 +734,20 @@ C4PropertyName C4PropList::GetPropertyP(C4PropertyName n) const
|
|||
return P_LAST;
|
||||
}
|
||||
|
||||
int32_t C4PropList::GetPropertyBool(C4PropertyName n) const
|
||||
{
|
||||
C4String * k = &Strings.P[n];
|
||||
if (Properties.Has(k))
|
||||
{
|
||||
return Properties.Get(k).Value.getBool();
|
||||
}
|
||||
if (GetPrototype())
|
||||
{
|
||||
return GetPrototype()->GetPropertyBool(n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t C4PropList::GetPropertyInt(C4PropertyName n, int32_t default_val) const
|
||||
{
|
||||
C4String * k = &Strings.P[n];
|
||||
|
|
|
@ -92,7 +92,7 @@ public:
|
|||
|
||||
// These four operate on properties as seen by script, which can be dynamic
|
||||
// or reflect C++ variables
|
||||
virtual bool GetPropertyByS(C4String *k, C4Value *pResult) const;
|
||||
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const;
|
||||
virtual C4ValueArray * GetProperties() const;
|
||||
// not allowed on frozen proplists
|
||||
virtual void SetPropertyByS(C4String * k, const C4Value & to);
|
||||
|
@ -113,6 +113,7 @@ public:
|
|||
C4Value Call(C4String * k, C4AulParSet *pPars=0, bool fPassErrors=false);
|
||||
C4Value Call(const char * k, C4AulParSet *pPars=0, bool fPassErrors=false);
|
||||
C4PropertyName GetPropertyP(C4PropertyName k) const;
|
||||
int32_t GetPropertyBool(C4PropertyName n) const;
|
||||
int32_t GetPropertyInt(C4PropertyName k, int32_t default_val = 0) const;
|
||||
C4PropList *GetPropertyPropList(C4PropertyName k) const;
|
||||
bool HasProperty(C4String * k) const { return Properties.Has(k); }
|
||||
|
|
|
@ -257,6 +257,8 @@ C4StringTable::C4StringTable()
|
|||
P[P_Key] = "Key";
|
||||
P[P_Effect] = "Effect";
|
||||
P[P_AsyncGet] = "AsyncGet";
|
||||
P[P_Relative] = "Relative";
|
||||
P[P_CanMoveCenter] = "CanMoveCenter";
|
||||
P[DFA_WALK] = "WALK";
|
||||
P[DFA_FLIGHT] = "FLIGHT";
|
||||
P[DFA_KNEEL] = "KNEEL";
|
||||
|
|
|
@ -481,6 +481,8 @@ enum C4PropertyName
|
|||
P_Key,
|
||||
P_Effect,
|
||||
P_AsyncGet,
|
||||
P_Relative,
|
||||
P_CanMoveCenter,
|
||||
// Default Action Procedures
|
||||
DFA_WALK,
|
||||
DFA_FLIGHT,
|
||||
|
|
Loading…
Reference in New Issue