Select Git revision
CollisionHandlingMovement.cpp
-
David Gilbert authoredDavid Gilbert authored
SDasherWidget.cpp 13.64 KiB
#include "SDasherWidget.h"
#include "SlateOptMacros.h"
#include "Styling/CoreStyle.h"
#include "Brushes/SlateColorBrush.h"
#include "Rendering/DrawElements.h"
#include "DasherInterface.h"
#include "Components/SlateWrapperTypes.h"
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
// ++ This is needed in order to use the localization macro LOCTEXT
#define LOCTEXT_NAMESPACE "SStandardSlateWidget"
//Set the state if we're in the editor or not.
void SDasherWidget::SetEditor(bool EditorState)
{
IsEditor = EditorState;
}
//Event Handlers
//Mouse position saved for mouse Input
FReply SDasherWidget::HandleMouseMoveEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
{
if (CurrentlyUsingVectorInput) return FReply::Unhandled();
//The mouse event only contains the Screen Space Position
CursorPosition = Geometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
return FReply::Handled();
}
FReply SDasherWidget::HandleMouseDownEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
{
if (CurrentlyUsingVectorInput)
{
CurrentlyUsingVectorInput = false;
return FReply::Handled().LockMouseToWidget(AsShared());
}
DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
MouseDownListeners.ExecuteIfBound();
return FReply::Handled().LockMouseToWidget(AsShared());
}
FReply SDasherWidget::HandleMouseUpEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
{
if (CurrentlyUsingVectorInput)
{
CurrentlyUsingVectorInput = false;
return FReply::Handled().ReleaseMouseLock();
}
DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
MouseUpListeners.ExecuteIfBound();
return FReply::Handled().ReleaseMouseLock();
}
FReply SDasherWidget::HandleMouseDoubleClickEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
{
if(CurrentlyUsingVectorInput)
{
CurrentlyUsingVectorInput = false;
return FReply::Handled();
}
DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
return FReply::Handled();
}
//Set in the HandleMouseMoveEvent or via Set InputVector
//See tick for geometry things that could be use to modify this.
FVector2D SDasherWidget::GetCursorPosition()
{
return CursorPosition;
}
bool SDasherWidget::GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDasherView* pView)
{
const FVector2D Position = GetCursorPosition();
iX = Position.X;
iY = Position.Y;
return true;
}
void SDasherWidget::InputVector(const FVector2D InputVector)
{
if (!CurrentlyUsingVectorInput) return;
CursorPosition = DasherMainInterface->ConvertDasher2Screen(Dasher::CDasherModel::ORIGIN_X, Dasher::CDasherModel::ORIGIN_Y)
+ InputVector * FVector2D(1.0f,-1.0f) * (GetHeight() / 2);
}
void SDasherWidget::InputButton(bool Pressed)
{
CurrentlyUsingVectorInput = true;
if(Pressed)
{
DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
}
else {
DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
}
}
//The construction of the Dasher Widget, Dasher parameters are set here, a function to set them outside is not provided, but can be easily implemented
void SDasherWidget::Construct(const FArguments& InArgs)
{
//Initial resize, needed for setup
Width = InArgs._width;
Height = InArgs._height;
resize(Width, Height);
//initialize the font measuring service.
FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
//Setting up Dasher
static Dasher::XMLErrorDisplay display;
Dasher::XmlSettingsStore* Settings = new Dasher::XmlSettingsStore("Settings.xml", &display); //Gets deleted somewhere else
Settings->Load();
Settings->Save();
DasherMainInterface = MakeShared<Dasher::DasherInterface>(Settings);
DasherMainInterface->SetDefaultInputDevice(this);
//change dasher parameters
DasherMainInterface->SetBoolParameter(BP_AUTO_SPEEDCONTROL, false); //Auto Speed Control
DasherMainInterface->SetLongParameter(LP_DASHER_FONTSIZE, 18);
DasherMainInterface->SetLongParameter(LP_MAX_BITRATE, 400); //Maximum Speed
//DasherMainInterface->SetStringParameter(SP_INPUT_FILTER, "Stylus Control"); //On Hold
DasherMainInterface->SetStringParameter(SP_ALPHABET_ID, "German without punctuation");
DasherMainInterface->SetLongParameter(LP_MIN_NODE_SIZE, 15);
DasherMainInterface->SetScreen(this);
DasherMainInterface->SetBuffer(0);
DasherMainInterface->SetCharEnteredCallback([this](FString Char, FString Buffer){CharacterEntered.ExecuteIfBound(Char, Buffer);});
DasherMainInterface->SetCharDeletedCallback([this](FString Char, FString Buffer){CharacterDeleted.ExecuteIfBound(Char, Buffer);});
//Set up the Event Handlers for Mouse Movement etc.
SetOnMouseMove(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseMoveEvent(Geometry, MouseEvent) ; }));
SetOnMouseButtonDown(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseDownEvent(Geometry, MouseEvent); }));
SetOnMouseButtonUp(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseUpEvent(Geometry, MouseEvent); }));
SetOnMouseDoubleClick(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseDoubleClickEvent(Geometry, MouseEvent); })); //We treat a double click of the mouse as a lift of the mouse button.
//You could add Slate Code here for child slots etc.
}
void SDasherWidget::SetBoolParamter(FString ParameterName, bool Value)
{
for(Dasher::Settings::bp_table Setting : Dasher::Settings::boolparamtable)
{
if(FString(Setting.regName).Compare(ParameterName, ESearchCase::IgnoreCase))
{
DasherMainInterface->SetBoolParameter(Setting.key, Value);
return;
}
}
}
void SDasherWidget::SetLongParamter(FString ParameterName, int64 Value)
{
for(Dasher::Settings::bp_table Setting : Dasher::Settings::boolparamtable)
{
if(FString(Setting.regName).Compare(ParameterName, ESearchCase::IgnoreCase))
{
DasherMainInterface->SetLongParameter(Setting.key, Value);
return;
}
}
}
void SDasherWidget::SetStringParamter(FString ParameterName, FString Value)
{
for(Dasher::Settings::bp_table Setting : Dasher::Settings::boolparamtable)
{
if(FString(Setting.regName).Compare(ParameterName, ESearchCase::IgnoreCase))
{
DasherMainInterface->SetStringParameter(Setting.key, TCHAR_TO_UTF8(*Value));
return;
}
}
}
//Set the colour Scheme for dasher
void SDasherWidget::SetColourScheme(const Dasher::CColourIO::ColourInfo* pcolours) {
ColorPalette = pcolours;
}
//paints our stuff, then lets compoundwidget (super::) draw its stuff
//This draws from bottom to top rectangles->lines->labels, this is enough for our use, but for more complex scenarios a proper layering system might have to be implemented
int32 SDasherWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
//this doesn't draw anything red, we just need a brush. Could probably find a better "empty" brush
auto MyBrush = FSlateColorBrush(FColor::Red);
FFilledRect* RectObject;
FWriting* WritingObject;
FPolyLine* LineObject;
FVector2D Size;
FString Text;
FSlateFontInfo Font;
TArray<TUniquePtr<DasherDrawGeometry>>& GeometryBuffer = *FrontBuffer;
for(TUniquePtr<DasherDrawGeometry>& GeneralObject : GeometryBuffer) {
switch(GeneralObject->Type)
{
case Rectangle:
RectObject = static_cast<FFilledRect*>(GeneralObject.Get());
Size = {FMath::Abs(RectObject->top.X - RectObject->bottom.X), FMath::Abs(RectObject->top.Y - RectObject->bottom.Y)};
FSlateDrawElement::MakeBox(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(1, RectObject->top)), &MyBrush, ESlateDrawEffect::None, RectObject->color); //actually adds the box to the geometry
break;
case Writing:
WritingObject = static_cast<FWriting*>(GeneralObject.Get());
Text = FString(UTF8_TO_TCHAR(WritingObject->label->m_strText.c_str()));
Font = FCoreStyle::GetDefaultFontStyle("Roboto", WritingObject->size, FFontOutlineSettings::NoOutline); //get the font
FSlateDrawElement::MakeText(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(FSlateLayoutTransform(1, FVector2D(WritingObject->pos.X, WritingObject->pos.Y))), Text , Font, ESlateDrawEffect::None, WritingObject->color);
break;
case PolyLine:
LineObject = static_cast<FPolyLine*>(GeneralObject.Get());
FSlateDrawElement::MakeLines(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(), LineObject->points, ESlateDrawEffect::None, LineObject->color, true, LineObject->linewidth);
break;
default: break;
}
}
return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); //call the parent onPaint
}
//The tick function, we tick dasher in it and update the screen size for dasher
void SDasherWidget::Tick(const FGeometry& AllotedGeometry, const double InCurrentTime, const float InDeltaTime) {
//don't tick in the editor
if (!IsEditor) {
SCompoundWidget::Tick(AllotedGeometry, InCurrentTime, InDeltaTime);
DasherMainInterface->Tick(static_cast<unsigned long>(InCurrentTime * 1000.0)); //we need to provide ticks in milliseconds
//This probably doesn't have to be done every tick, but it does not seem to have a huge hit on performance
const FGeometry Geometry = GetTickSpaceGeometry();
const FIntPoint AbsoluteSize = Geometry.Size.IntPoint();
if (Width != AbsoluteSize.X || Height != AbsoluteSize.Y) {
Width = AbsoluteSize.X;
Height = AbsoluteSize.Y;
resize(Width, Height);
//tell dasher we resized the screen, but only if there's actually a screen
if (Width && Height) DasherMainInterface->ScreenResized(this);
};
}
}
std::pair<SDasherWidget::screenint, SDasherWidget::screenint> SDasherWidget::TextSize(CDasherScreen::Label* Label, unsigned Size)
{
const FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Roboto", Size, FFontOutlineSettings::NoOutline); //get the font
const FVector2D TextSize = FontMeasureService->Measure(FString(UTF8_TO_TCHAR(Label->m_strText.c_str())), Font, 1); //get the real size of the text, using the fontmeasuring service
return {static_cast<screenint>(TextSize.X), static_cast<screenint>(TextSize.Y)};
}
//Double Buffers are rotated here.
void SDasherWidget::Display() {
std::swap(FrontBuffer, BackBuffer);
BackBuffer->Empty();
}
//Functions for Drawing
void SDasherWidget::DrawRectangle(Dasher::screenint x1, Dasher::screenint y1, Dasher::screenint x2, Dasher::screenint y2, int colour, int iOutlineColour, int iThickness)
{
const FVector2D Top(x1, y1);
const FVector2D Bottom(x2, y2);
FLinearColor RectColor;
if (ColorPalette) {
RectColor = FLinearColor(ColorPalette->Colors[colour].Red/255.0, ColorPalette->Colors[colour].Green / 255.0, ColorPalette->Colors[colour].Blue / 255.0);
}
else {
RectColor = FLinearColor::Blue;
}
BackBuffer->Add(MakeUnique<FFilledRect>(Top, Bottom, RectColor));
}
void SDasherWidget::DrawString(CDasherScreen::Label* lab, screenint x1, screenint y1, unsigned int iSize, int colour) {
const FVector2D Pos(x1, y1);
FLinearColor TextColor;
if (ColorPalette) {
TextColor = FLinearColor(ColorPalette->Colors[colour].Red / 255.0, ColorPalette->Colors[colour].Green / 255.0, ColorPalette->Colors[colour].Blue / 255.0);
}
else {
TextColor = FLinearColor::Black;
}
BackBuffer->Add(MakeUnique<FWriting>(lab, Pos, static_cast<int>(iSize), TextColor));
}
void SDasherWidget::Polyline(CDasherScreen::point* points, int number, int iwidth, int colour) {
FLinearColor LinearColor;
if (ColorPalette) {
LinearColor = FLinearColor(ColorPalette->Colors[colour].Red / 255.0, ColorPalette->Colors[colour].Green / 255.0, ColorPalette->Colors[colour].Blue / 255.0);
}
else {
LinearColor = FLinearColor::Blue;
}
TArray<FVector2D> PointArray;
for (int i = 0; i < number; i++) {
FVector2D Point(points[i].x, points[i].y);
PointArray.Add(Point);
}
BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), LinearColor));
}
//techincally polygons are just multiple polylines. Dasher doesn't actually draw polygons in our case.
void SDasherWidget::Polygon(CDasherScreen::point* points, int number, int fillcolour, int outlinecolour, int iwidth) {
FLinearColor LinearColor;
if (ColorPalette) {
LinearColor = FLinearColor(ColorPalette->Colors[outlinecolour].Red / 255.0, ColorPalette->Colors[outlinecolour].Green / 255.0, ColorPalette->Colors[outlinecolour].Blue / 255.0);
}
else {
LinearColor = FLinearColor::Blue;
}
TArray<FVector2D> PointArray;
for (int i = 0; i < number; i++) {
FVector2D Point(points[i].x, points[i].y);
PointArray.Add(Point);
}
PointArray.Add(FVector2D(points[0].x, points[0].y));
BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), LinearColor));
}
//We pass through the contents of the dasher buffer
FString SDasherWidget::GetBuffer() const
{
return DasherMainInterface->GetBuffer();
}
void SDasherWidget::ResetBuffer()
{
DasherMainInterface->ResetBuffer();
}
void SDasherWidget::StartTraining(FString PathToTextFile)
{
DasherMainInterface->ImportTrainingFile(TCHAR_TO_UTF8(*FPaths::ConvertRelativePathToFull(PathToTextFile)));
}
// ++ We need to undefine this namespace after we finish creating the Slate widget
#undef LOCTEXT_NAMESPACE
END_SLATE_FUNCTION_BUILD_OPTIMIZATION