Drag and drop with TTreeView in Firemonkey - drag-and-drop

I am using C++ Builder 10.2.2 Tokyo with FireMonkey (FMX).
I want to add drag and drop functionality to a TTreeView, so a user can rearrange the order of the tree items. I have added a handler to the TTreeView.OnMouseDown event, based on this Drag and Drop sample project.
With this, the program can now drag and drop to rearrange items, but it seems that there is some default behavior to move a TTreeViewItem to be a child of the TTreeViewItem it is dropped onto, instead of inserting after that item.
How can I override this default behavior, so that a TTreeViewItem is inserted at the same level in the TTreeView, and at an index 1 greater than the TTreeViewItem it is dropped onto?

Following on Abdullah's suggestion, you can achieve this by creating a custom component. Directions to create a custome component in general are here. I recommend installing it in Standard on the Tool Palette, as that's where TTreeView is.
The custom component, here called TMyTreeView, has this in the header in particular:
class PACKAGE TMyTreeView : public TTreeView
{
private:
bool IsAncestor (TTreeViewItem* oItem, TTreeViewItem* oTargetItem);
protected:
int DragDelta;
void StartDrag ();
void __fastcall DragDrop (const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point);
void __fastcall MouseDown (System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, float X, float Y);
void __fastcall MouseMove (System::Classes::TShiftState Shift, float X, float Y);
public:
__fastcall TMyTreeView(TComponent* Owner);
__fastcall ~TMyTreeView ();
TBitmap* DragBmp;
TPointF MouseDownPoint;
TTreeViewItem* DragStartItem;
__published:
};
//---------------------------------------------------------------------------
where the functions are as follows in the corresponding cpp file:
__fastcall TMyTreeView::TMyTreeView(TComponent* Owner)
: TTreeView(Owner)
{
DragBmp = NULL;
DragStartItem = NULL;
DragDelta = 5;
}
//---------------------------------------------------------------------------
__fastcall TMyTreeView::~TMyTreeView ()
{
if (DragBmp)
delete DragBmp;
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::MouseMove (System::Classes::TShiftState Shift, float X, float Y)
{
TTreeView::MouseMove (Shift, X, Y);
if ((abs (X-MouseDownPoint.X) > DragDelta) || (abs (Y-MouseDownPoint.Y) > DragDelta))
StartDrag ();
}
//---------------------------------------------------------------------------
void TMyTreeView::StartDrag ()
{
if (!AllowDrag)
return;
if (!DragStartItem)
return;
if (DragBmp)
delete DragBmp;
_di_IFMXDragDropService service;
if ((TPlatformServices::Current->SupportsPlatformService (__uuidof(IFMXDragDropService)) &&
(service = TPlatformServices::Current->GetPlatformService (__uuidof(IFMXDragDropService)))))
{
TDragObject dragData;
if (!DragStartItem)
return;
dragData.Source = DragStartItem;
DragBmp = DragStartItem->MakeScreenshot ();
dragData.Data = DragBmp;
service->BeginDragDrop ((TForm*)this->Owner, dragData, DragBmp);
DragStartItem = NULL;
}
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::MouseDown (System::Uitypes::TMouseButton Button, System::Classes::TShiftState Shift, float X, float Y)
{
TTreeView::MouseDown (Button, Shift, X, Y);
if (AllowDrag)
{
DragStartItem = ItemByPoint (X, Y);
MouseDownPoint.X = X;
MouseDownPoint.Y = Y;
}
else
DragStartItem = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TMyTreeView::DragDrop (const Fmx::Types::TDragObject &Data, const System::Types::TPointF &Point)
{
TTreeViewItem* item = ItemByPoint (Point.X, Point.Y);
if (!item)
return;
TTreeViewItem* srcItem = (TTreeViewItem*)Data.Source;
if (!srcItem)
return;
if (IsAncestor (srcItem, item))
return;
if (item->ParentItem ())
item->ParentItem ()->InsertObject (item->Index, srcItem);
else
this->InsertObject (item->Index, srcItem);
//TTreeView::DragDrop (Data, Point);
}
//---------------------------------------------------------------------------
bool TMyTreeView::IsAncestor (TTreeViewItem* oItem, TTreeViewItem* oTargetItem)
{
for (int i=0; i<oItem->Count; i++)
{
TTreeViewItem* item = oItem->Items [i];
if (item == oTargetItem)
return true;
if (IsAncestor (item, oTargetItem))
return true;
}
return false;
}
//---------------------------------------------------------------------------
After installing the custom component to your Tool Palette, you can then add it to your form as you would any other component.
Special thanks to Mike Sutton, who had code to modify an earlier version of TTreeView here.
Once added to a form, set the TMyTreeView control's AllowDrag to true.

Related

Make 2 Method with One (same parameter)

I think this is beginner stuff, but I still need to find a way to solve this.
I want to make the bool logic reference of the bool logar. The invisiblearrow(), I applied in toggle button in world space Unity thus I want to pass the logar to logic bool.(same bool).
private void invisiblearrow(bool logar) {
if (logar == false) {
GameObject[] arrow = GameObject.FindGameObjectsWithTag("arrow");
foreach(GameObject ar in arrow) {
ar.GetComponent < Renderer > ().enabled = logar;
Debug.Log(logar);
}
}
if (logar == true) {
GameObject[] arrow = GameObject.FindGameObjectsWithTag("arrow");
foreach(GameObject ar in arrow) {
ar.GetComponent < Renderer > ().enabled = logar;
Debug.Log(logar);
}
}
}
public void arrow(float[, ] arrowdata, bool logic) {
for (int x = 0; x < arrowdata.GetLength(0); x++) {
for (int y = 0; y < arrowdata.GetLength(1); y++) {
if (grid[x, y] == 1) {
if (arrowdata[x, y] == 5) {
GameObject referenceArrow = Instantiate(Resources.Load("down")) as GameObject;
float posY = shipvalue.transform.position.y - 9f;
referenceArrow.transform.position = new Vector3(shipvalue.transform.position.x, posY);
referenceArrow.GetComponent < Renderer > ().enabled = logic;
}
if (arrowdata[x, y] == 4) {
GameObject referenceArrow = Instantiate(Resources.Load("top left")) as GameObject;
float posY = shipvalue.transform.position.y - 9f;
referenceArrow.transform.position = new Vector3(shipvalue.transform.position.x, posY);
referenceArrow.GetComponent < Renderer > ().enabled = logic;
}
if (arrowdata[x, y] == 3) {
GameObject referenceArrow = Instantiate(Resources.Load("top right")) as GameObject;
float posY = shipvalue.transform.position.y - 9f;
referenceArrow.transform.position = new Vector3(shipvalue.transform.position.x, posY);
referenceArrow.GetComponent < Renderer > ().enabled = logic;
}
}
}
}
}
You can't because both bools are local bool (at the level of the function only), but you can:
Declare a private bool outside the two function (global), and at the end of invisiblearrow(bool logar) function, assign logar bool value to the new private bool (the new bool = logar), then you can access the new private bool in arrow(float[,] arrowdata, bool logic) function.

Sync Text objects in Unity

I have a prefab that has a text object on it and that text represents a counter for the prefab lifespan that increases every second. The script works fine on the human vs computer mode, but when I try to make a host client the numbers are always different on the clients side. How can I let the numbers displayed on them sync together?
public class Timer : MonoBehaviour
{
public Planet pl;
private bool hasDone = false;
public int timeleft =6;
public Text countdownText;
// Use this for initialization
void Start()
{
timeleft = Random.Range (0, 31);
}
// Update is called once per frame
void Update()
{
countdownText.text = ("" + timeleft);
if (pl.get_owner () != null && hasDone == false) {
StartCoroutine("LoseTime");
hasDone = true;
}
if (pl.get_owner () == "Human") {
countdownText.color = new Color (0f, 0.5f, 1f, 1f);
}
if (pl.get_owner () == "Computer") {
countdownText.color = new Color (1f, 0.5f, 0f, 1f);
}
if (pl.get_collision () > 0) {
timeleft -= 1;
pl.decrease_collision ();
}
}
IEnumerator LoseTime()
{
if (pl.get_owner () != null) {
while (true)
{
yield return new WaitForSeconds(1);
timeleft++;
}
}
}
}
I think you are looking for the syncvar attribute: https://docs.unity3d.com/ScriptReference/Networking.SyncVarAttribute.html
[SyncVar]
public int timeleft =6;
Additionally only the server should change the value. Add the following code to all locations where you would alter timeleft.
if (!isServer)
return;

Unity 5 Inventory system not working

Hello programmers all around the world. I have made myself an inventory system for my game. Only problem is that when I click on item and then drag it to and empty slot it doesn't move and I kinda don't see the error which I am having and I have tried to debug it but without success any help? Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class Inventory : MonoBehaviour {
private RectTransform inventoryRect;
private float inventoryWidth;
private float inventoryHeight;
public int slots;
public int rows;
public float slotPaddingLeft;
public float slotPaddingTop;
public float slotSize;
public GameObject slotPrefab;
private static Slot from;
private static Slot to;
private List<GameObject> allslots;
public GameObject iconPrefab;
private static GameObject hoverObject;
private static int emptySlots;
public Canvas canvas;
private float hoverYOffset;
private bool isPressed;
public EventSystem eventSystem;
public static int EmptySlots{
get{ return emptySlots;}
set{ emptySlots = value;}
}
// Use this for initialization
void Start () {
CreateLayout ();
canvas.enabled = false;
isPressed = false;
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown (KeyCode.I)) {
if (Input.GetKeyDown (KeyCode.I)) {
canvas.enabled = false;
}
canvas.enabled = true;
}
if (Input.GetMouseButtonUp (0)) {
if (!eventSystem.IsPointerOverGameObject (-1) && from != null) {
from.GetComponent<Image> ().color = Color.white;
from.ClearSlot ();
Destroy (GameObject.Find ("Hover"));
to = null;
from = null;
hoverObject = null;
}
}
if (hoverObject != null) {
Vector2 position;
RectTransformUtility.ScreenPointToLocalPointInRectangle (canvas.transform as RectTransform, Input.mousePosition, canvas.worldCamera, out position);
position.Set (position.x, position.y - hoverYOffset);
hoverObject.transform.position = canvas.transform.TransformPoint (position);
}
}
private void CreateLayout(){
allslots = new List<GameObject> ();
hoverYOffset = slotSize * 0.01f;
emptySlots = slots;
inventoryWidth = (slots / rows) * (slotSize + slotPaddingLeft) + slotPaddingLeft;
inventoryHeight = rows * (slotSize + slotPaddingTop) + slotPaddingTop;
inventoryRect = GetComponent<RectTransform> ();
inventoryRect.SetSizeWithCurrentAnchors (RectTransform.Axis.Horizontal, inventoryWidth);
inventoryRect.SetSizeWithCurrentAnchors (RectTransform.Axis.Vertical, inventoryHeight);
int colums = slots / rows;
for (int y = 0; y < rows; y++) {
for (int x = 0; x < colums; x++) {
GameObject newSlot = (GameObject)Instantiate (slotPrefab);
RectTransform slotRect = newSlot.GetComponent<RectTransform> ();
newSlot.name = "Slot";
newSlot.transform.SetParent (this.transform.parent);
slotRect.localPosition = inventoryRect.localPosition + new Vector3 (slotPaddingLeft * (x + 1) + (slotSize * x), -slotPaddingTop * (y + 1) - (slotSize * y));
slotRect.SetSizeWithCurrentAnchors (RectTransform.Axis.Horizontal, slotSize);
slotRect.SetSizeWithCurrentAnchors (RectTransform.Axis.Vertical, slotSize);
allslots.Add (newSlot);
}
}
}
public bool AddItem(Item item){
if (item.maxSize == 1) {
PlaceEmpty (item);
return true;
}
else {
foreach (GameObject slot in allslots) {
Slot temporary = slot.GetComponent<Slot> ();
if (!temporary.IsEmpty) {
if (temporary.CurrentItem.type == item.type && temporary.IsAvailable) {
temporary.AddItem (item);
return true;
}
}
}
if (emptySlots > 0) {
PlaceEmpty (item);
}
}
return false;
}
private bool PlaceEmpty(Item item){
if (emptySlots > 0) {
foreach (GameObject slot in allslots) {
Slot temporary = slot.GetComponent<Slot> ();
if (temporary.IsEmpty) {
temporary.AddItem (item);
emptySlots--;
return true;
}
}
}
return false;
}
public void MoveItem(GameObject clicked){
if (from == null) {
if (!clicked.GetComponent<Slot> ().IsEmpty) {
from = clicked.GetComponent<Slot> ();
from.GetComponent<Image> ().color = Color.gray;
hoverObject = (GameObject)Instantiate (iconPrefab);
hoverObject.GetComponent<Image> ().sprite = clicked.GetComponent<Image> ().sprite;
hoverObject.name = "Hover";
RectTransform hoverTransform = hoverObject.GetComponent<RectTransform> ();
RectTransform clickedTransform = clicked.GetComponent<RectTransform> ();
hoverTransform.SetSizeWithCurrentAnchors (RectTransform.Axis.Horizontal, clickedTransform.sizeDelta.x);
hoverTransform.SetSizeWithCurrentAnchors (RectTransform.Axis.Vertical, clickedTransform.sizeDelta.y);
hoverObject.transform.SetParent (GameObject.Find ("Canvas").transform, true);
hoverObject.transform.localScale = from.gameObject.transform.localScale;
}
}
else if (to = null) {
to = clicked.GetComponent<Slot> ();
Destroy (GameObject.Find ("Hover"));
}
if (to != null && from != null) {
Stack<Item> tmpTo = new Stack<Item> (to.Items);
to.AddItems (from.Items);
if (tmpTo.Count == 0) {
from.ClearSlot ();
}
else {
from.AddItems (tmpTo);
}
from.GetComponent<Image> ().color = Color.white;
to = null;
from = null;
hoverObject = null;
}
}
}
The method which is causing the problem is the MoveItem() sadly it is not a nullreference or nullpointer and I simply am out of ideas been strugling with it for a couple of days... Any advice on how to fix this would be helpfull and much welcomed indeed. Thanks in advance!
I haven't taken a long look at your code but right away I saw this issue:
else if (to = null) {
to = clicked.GetComponent<Slot> ();
Destroy (GameObject.Find ("Hover"));
}
This is causing the end location to be set to null. To fix this, change to double equals like so:
else if (to == null) {
to = clicked.GetComponent<Slot> ();
Destroy (GameObject.Find ("Hover"));
}
If this does not solve your problem, let me know and I'll look at your code harder.

Switching active states of two GameObjects in Unity3D

With Unity3D I am trying to create a scene with an alpha texture as a silhouette, which upon looking up is added, then looking down removes.
Currently I have the exposure of an equirectangular image changing on look up, but my silhouette object says I have not assigned it to an instance:
As you can see from the console, it is eventualy recognised, but I cannot set the active state. This is the current state of my code being applied to the scene:
using UnityEngine;
using System.Collections;
public class switchScript : MonoBehaviour {
public Cardboard cb;
public Renderer leftEyeDay;
public Renderer rightEyeDay;
private GameObject[] dayObjects;
public GameObject nightObject;
void Start () {
MeshFilter filter = GetComponent(typeof (MeshFilter)) as MeshFilter;
if (filter != null) {
Mesh mesh = filter.mesh;
Vector3[] normals = mesh.normals;
for (int i=0;i<normals.Length;i++)
normals[i] = -normals[i];
mesh.normals = normals;
for (int m=0;m<mesh.subMeshCount;m++)
{
int[] triangles = mesh.GetTriangles(m);
for (int i=0;i<triangles.Length;i+=3)
{
int temp = triangles[i + 0];
triangles[i + 0] = triangles[i + 1];
triangles[i + 1] = temp;
}
mesh.SetTriangles(triangles, m);
}
}
}
// Update is called once per frame
void Update () {
float xAngle = cb.HeadPose.Orientation.eulerAngles.x;
if (isLookingUp (xAngle)) {
var exposureValue = getExposureValue (xAngle);
leftEyeDay.material.SetFloat ("_Exposure", exposureValue);
rightEyeDay.material.SetFloat ("_Exposure", exposureValue);
toggleNight ();
} else {
leftEyeDay.material.SetFloat ("_Exposure", 1F);
rightEyeDay.material.SetFloat ("_Exposure", 1F);
toggleNight ();
}
}
public bool isLookingUp (float xAngle) {
return xAngle > 270 && xAngle < 340;
}
public float getExposureValue (float xAngle) {
var _xAngle = Mathf.Clamp (xAngle, 320, 340);
return ScaleValue (320.0F, 340.0F, 0.3F, 1.0F, _xAngle);
}
public float ScaleValue (float from1, float to1, float from2, float to2, float v) {
return from2 + (v - from1) * (to2 - from2) / (to1 - from1);
}
void toggleDay() {
print (nightObject);
nightObject = GameObject.FindGameObjectWithTag ("Night");
nightObject.SetActive (false);
}
void toggleNight() {
print (nightObject);
nightObject = GameObject.FindGameObjectWithTag ("Night");
nightObject.SetActive (true);
}
}
GameObject.FindGameObjectWithTag ("Night") returns null when the whole object is set to !active. Just toggle the scripts on that object instead of the whole GO.
Ok, if anyone needs this, this is an easy way to create scene switch:
void Awake() {
silhouetteObject = GameObject.FindGameObjectWithTag ("Night");
silhouetteObject.GetComponent<Renderer>().enabled = false;
}

LibGDX: How to keep displaying actor after drag (drag off)

I have implemented drag and drop, but the problem is after user releases the drag. The dragged actor is dissapear from the screen. How to move actor to its new location or return to its location if it is dropped not in its proper locations?
Thanks for your answers.
Regards,
Alfa
'
final DragAndDrop dragAndDrop = new DragAndDrop();
final Box sourceBox = boxList.get(0);
final Box targetBox = boxHolderList.get(0);
Source source = new Source(sourceBox) {
#Override
public void dragStop(InputEvent event, float x, float y,
int pointer, Payload payload, Target target) {
Box sourceBox = (Box) payload.getDragActor();
if (target == null){
sourceBox.setPosition(sourceBox.getX(), sourceBox.getY());
}
else{
Box targetBox = (Box) target.getActor();
sourceBox.setPosition(targetBox.getX(), targetBox.getY());
}
}
#Override
public Payload dragStart(InputEvent event, float x, float y, int pointer) {
dragAndDrop.setDragActorPosition(-1*sourceBox.getWidth()/2, 1*sourceBox.getHeight()/2);
Payload payload = new Payload();
payload.setDragActor(sourceBox);
Label validLabel = new Label("Valid!", skin);
validLabel.setColor(0, 1, 0, 1);
payload.setValidDragActor(validLabel);
Label invalidLabel = new Label("Invalid!", skin);
invalidLabel.setColor(1, 0, 0, 1);
payload.setInvalidDragActor(invalidLabel);
return payload;
}
};
this drag and drop method is quite mesh, you may try this
yourActor.addListener(new DragListener(){
public void touchDragged(InputEvent event, float x, float y, int pointer) {
yourActor.moveBy(x-yourActor.getWidth()/2,y-yourActor.getHeight()/2);
}});