I have created a simple custom editor tool, which allows me to keep mouse position in a straight line. I require this to draw texture on a terrain in a straight line. Unfortunately, when I enable "Paint texture" tool in the terrain editor in inspector, my custom tool gets disabled and vice-versa. How can I keep both my custom tool and terrain paint tool enabled at once?
Custom tool selected but paint texture is deactivated-
Custom tool got deselected on paint texture selection-
Following is the OnToolGUI method
public override void OnToolGUI(EditorWindow window)
{
HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
Event e = Event.current;
if (!(window is SceneView))
return;
if (!ToolManager.IsActiveTool(this))
return;
if (e.shift)
{
if (e.type == EventType.MouseDown)
{
if (e.button == 0)
{
downY = e.mousePosition.y;
}
}
if (e.type == EventType.MouseDrag)
{
if (e.button == 0)
{
e.mousePosition = new Vector2(e.mousePosition.x, downY);
Debug.Log("Mouse Position: " + e.mousePosition);
}
}
}
As mentioned in the comments I guess it simply is the nature of the tools that they are exclusive and you can only have one active at a time.
As alternative I would rather simply
enable/disable this via a general header menu entry
(optionally) store that decision persistent in EditorPrefs (pretty much like PlayerPrefs but for the editor itself)
and accordingly attach a listener to SceneView.duringSceneGui
This could look somewhat like e.g.
public static class StraightLineTool
{
// Used for the displayed menu labels
// and also simply (ab)used as the unique key for the EditorPrefs
private const string k_MenuName = "My Tools/Straight Line Tool";
private static float downY;
// Property for simplifying access and setting more centralized
private static bool IsEnabled
{
get => EditorPrefs.GetBool(k_MenuName, false);
set => EditorPrefs.SetBool(k_MenuName, value);
}
// method to be called when clicking the menu button
[MenuItem(k_MenuName)]
private static void ToggleEnabled()
{
IsEnabled = !IsEnabled;
ApplySettings();
}
// adding a checkmark when is enabled and simply always allow to click it
[MenuItem(k_MenuName, true)]
private static bool ToggleEnabledValidate()
{
Menu.SetChecked(k_MenuName, IsEnabled);
return true;
}
// Called on every project loading or code recompilation
[InitializeOnLoadMethod]
private static void Initialize()
{
EditorApplication.delayCall -= ApplySettings;
EditorApplication.delayCall += ApplySettings;
}
private static void ApplySettings()
{
// remove so only happening once
EditorApplication.delayCall -= ApplySettings;
SceneView.duringSceneGui -= OnSceneGUI;
if (IsEnabled)
{
// if enabled start listening
SceneView.duringSceneGui += OnSceneGUI;
}
}
// Callback listening to any SceneView.duringSceneGui
private static void OnSceneGUI(SceneView sceneView)
{
// Not sure tbh what this does or if you need it still in this approach
// HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
var currentEvent = Event.current;
if (currentEvent.shift)
{
if (currentEvent.type == EventType.MouseDown)
{
if (currentEvent.button == 0)
{
downY = currentEvent.mousePosition.y;
}
}
if (currentEvent.type == EventType.MouseDrag)
{
if (currentEvent.button == 0)
{
currentEvent.mousePosition = new Vector2(currentEvent.mousePosition.x, downY);
Debug.Log("Mouse Position: " + currentEvent.mousePosition);
}
}
}
}
}
=> using this the tool will be enabled/disabled persistent even when restarting Unity
Then using this as start point you can probably still try to somehow integrate this somewhere more nicely into the SceneView menus - but maybe that is also overkill ;)
I'm currently working on a method that gives the user the possibility to add a handscanner to a dicitionary in order to scan some barcodes with it. (before i started the scanners were hardcoded in the dictionary). my colleague from which i got this project, implemented the rawinput_dll in order to get all of the necessary data from the barcode scanner. The method to get the data is shown below:
private void OnKeyPressed(object sender, RawInputEventArg e)
{
if (!Scanners.ContainsKey(e.KeyPressEvent.DeviceName))
{
return;
}
else if (Scanners.ContainsKey(e.KeyPressEvent.DeviceName))
{
if (e.KeyPressEvent.KeyPressState == "MAKE")
{
return;
}
if (e.KeyPressEvent.VKeyName != "\n")
{
scanNumber += e.KeyPressEvent.VKeyName;
return;
}
devID = e.KeyPressEvent.DeviceName;
Debug.Print(devID);
Aufrufen(scanNumber);
scanNumber = "";
}
}
Basically there are three classes in this program (FrmMenu, FrmSettings and a Class for the Scanner itself). If you want to add settings for the program you click on a button that opens up a new instance of FrmSettings
private void BtnSettings_Click(object sender, EventArgs e)
{
FrmSettings settings = new FrmSettings();
settings.ShowDialog();
settings.BtnSave_Click(sender, e);
settings.Dispose();
}
In this form there 2 buttons where you can choose if you want to add a scanner that scans even numbers or one that scans odd ones. If you press one of the buttons you need to scan a barcode in order to get the information (VID of Scanner) which is used as key to add the new scanner to the dictionary.
private void OnKeyPressed(object sender, RawInputEventArg e)
{
if (newScanner == true)
{
devIDnew = e.KeyPressEvent.DeviceName;
scannerAnlegen(devIDnew);
}
}
scannerAnlegen is the methode that adds the scanner to the dict.
public void scannerAnlegen(string devIDnew)
{
if(EvenOrOdd == true)
{
Scanner ger = new Scanner("dev3", "even");
FrmMenu.Scanners.Add(devIDnew, ger);
newScanner = false;
}
else
{
Scanner ug = new Scanner("dev4", "odd");
FrmMenu.Scanners.Add(devIDnew, ug);
newScanner = false;
}
}
my problem rn is, that it seems like i cant get out of this OneKeyPressed method of the Settings class. the logic of the OneKeyPressed method of the FrmMenu Class is that it can only proceed if the scanner is in the dictionary. Adding the scanner seems to work because when i debug and try to add one scanner the second time it throws and exception and says something like "element with this key already added". But why does this code doesn't continue then?
I'm using C# Xamarin with an azure database.
I'm trying to only show outputs based on whats in my spinner/dropdown.
I followed the xamarin docs on how to output a toast on whats in the spinner and had all the events displaying. They were not connected at the time.
Then I went to only output events based on whats in the dropdown box(code below) but says values not used and I can't see where the error is.
eItemsSP is not used. Whenever I run the app and open the page, it crashes giving a null exception.
Any help is appreciated!
Edit 1: I changed the second line to this
private List<EventsDB> eItemsSP = new List<EventsDB>();
It no longer crashes and I can use the dropdown box but doesn't display anything.
private List<EventsDB> eItems;
private List<EventsDB> eItemsSP;
private ListView eListView;
protected async override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
ISharedPreferences pref = Application.Context.GetSharedPreferences("UserInfo", FileCreationMode.Private);
SetContentView(Resource.Layout.Search_Event);
eListView = FindViewById<ListView>(Resource.Id.myListView);
#region setUpDropdowns
Spinner spinner = FindViewById<Spinner>(Resource.Id.spnSearchEvents);
spinner.ItemSelected += new EventHandler<AdapterView.ItemSelectedEventArgs>(spinner_ItemSelected);
var adapter = ArrayAdapter.CreateFromResource(
this, Resource.Array.professions, Android.Resource.Layout.SimpleSpinnerItem);
adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
spinner.Adapter = adapter;
#endregion
}
private async void spinner_ItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
Spinner spinner = (Spinner)sender;
string spnCat = string.Format("{0}", spinner.GetItemAtPosition(e.Position));
eItems = await Client.GetTable<EventsDB>().ToListAsync();
EventsDB eItemsSP = eItems.FirstOrDefault(x => x.Category == spnCat);
ListViewAdapter adapterLV = new ListViewAdapter(this, this.eItemsSP);
eListView.Adapter = adapterLV;
//Toast.MakeText(this, spnCat, ToastLength.Long).Show();
}
In one stage of my app (Android & iOS are the ones we care about) we've got three pages which take in details and then open a webView for the user to input their card details to take a payment - this can't be done in the app due to Apple's guidelines.
I need to format the navigation in a way that when the user has finished in the webView it closes and then closes the 3 previous modals to get back to the original page. I've got it all working with the Appearing event so each page just closes itself:
this.Appearing += async (s, e) =>
{
await Navigation.PopModalAsync();
};
The issue I'm now having is that when the user presses the back button on the phone, it closes all of the pages that they've been through already & back to the original. I thought about implementing a custom nav bar and disabling the back button on the hardware but this would cause the same problem with the Appearing event.
Is there any easy way to solve this?
EDIT: Relevant code;
async void OnButtonClicked(object sender, EventArgs eventArgs)
{
if (IsConnected)
{
ActivityIndicator.IsVisible = true;
var button = (Button) sender;
button .IsEnabled = false;
await Navigation.PushModalAsync(new Page());
this.Appearing += (s, e) =>
{
ActivityIndicator.IsVisible = false;
button.IsEnabled = true;
RefreshPage();
};
}
else
{
NoInternetLabel.IsVisible = true;
}
}
Use this:
YourButton.Clicked += OpenPage;
OpenPage looks like this:
async public void OpenPage(object sender, EventArgs args)
{
await Navigation.PushAsync(new PageToShow());
}
You don't have to do anything to handle the PageToShow() closing, that happens by itself when the user presses the back button.
Managed to solve this by using Actions. In each new Page() we passed up an async method to close it once the one after had completed;
var nextPage = new Page(async () =>
{
await Navigation.PopModalAsync();
_completedSuccessfully();
});
await Navigation.PushModalAsync(nextPage);
And in the new page class;
private readonly Action _completedSuccessfully;
public Page(Action completedSuccessfully)
{
_completedSuccessfully = completedSuccessfully;
}
This meant that when the webView closed it called the completedSuccessfully() action and then chained all of them to the original page.
Code for creating the CustomMessageBox:
CustomMessageBox is a property, and not a reference to the C# Class in the Toolkit.
CustomMessageBox.Dismissed += (dismissSender, dismissedEvent) =>
{
switch (dismissedEvent.Result)
{
case CustomMessageBoxResult.LeftButton:
PlaceCall(clickedFavorite.Name, clickedFavorite.PhoneNo);
break;
case CustomMessageBoxResult.RightButton:
HERE ---> SendText(clickedFavorite.PhoneNo);
break;
}
};
Code for SendText() method:
private void SendText(String phoneNo)
{
var smsTask = new SmsComposeTask
{
To = phoneNo
};
smsTask.Show();
}
Thing is when the SmsComposeTask has started, the Phone navigates to the SMS application, which is correct.
If the user then decides to go back, with the Hardware Back Button, the SMS application closes and the phone shows my app again - but immediately closes, caused by a NullPointerException:
at Microsoft.Phone.Controls.CustomMessageBox.ClosePopup(Boolean restoreOriginalValues)
at Microsoft.Phone.Controls.CustomMessageBox.<>c__DisplayClass4.<Dismiss>b__1(Object s, EventArgs e)
at Microsoft.Phone.Controls.Transition.OnCompleted(Object sender, EventArgs e)
at MS.Internal.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
I have also tried to override the OnBackKeyPress event, like this:
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
if (CustomMessageBox != null && CustomMessageBox.IsEnabled)
{
e.Cancel = true;
}
else
{
base.OnBackKeyPress(e);
}
}
Does anyone know what to do?
I have found a solution to my own problem. Instead of using the faulty CustomMessageBox, I found Coding4Fun Windows Phone Toolkit which provides a by far, more stable message box called MessagePrompt - here's how to use it.
Create buttons
var smsButton = new Button { Content = "SMS" };
smsButton.Click += (o, args) =>
{
// do something
};
var buttonList = new List<Button>
{
smsButton
};
Create the actual message prompt
var msgPrompt = new MessagePrompt
{
Title = "Message Prompt Title",
Body = new TextBlock { Text = "Text for the Body", FontSize = 25, TextWrapping = TextWrapping.Wrap },
ActionPopUpButtons = buttonList
};
Show it
msgPrompt.Show()
No bullocks
The good thing, which I have experienced with this MessagePrompt is that you are not bound to two static Left and Right buttons like with CustomMessageBox.
And if you want, you can set the Body property to a whole new XAML page, which makes this control flexible.
Reference: Coding4Fun WP7 Message Prompt in depth
Doesn't this problem has something to do with Windows Phone Application lifecycle. As can be found here, figure 6. When activiting another program when your program is active you should save all application data so when a reactivating event ,such as navigating with your back button back to your application, starts your program again you can load the user's data again.
I'm not sure what's happening, but you can just delay the SMS task to avoid the issue:
CustomMessageBox.Dismissed += (dismissSender, dismissedEvent) =>
{
switch (dismissedEvent.Result)
{
case CustomMessageBoxResult.LeftButton:
PlaceCall(clickedFavorite.Name, clickedFavorite.PhoneNo);
break;
case CustomMessageBoxResult.RightButton:
Dispatcher.BeginInvoke(new Action(() => SendText(clickedFavorite.PhoneNo)));
break;
}
};
My 0.02$: this is a bug in the CustomMessageBox. They're keeping lots of singletons alive there and a good timing bug doesn't do that a world of good. Agreed with KooKiz that you can't work around with that without either fixing CustomMessageBox or waiting until the CustomMessageBox finishes its thing. From my ad-hoc testing it requires anywhere between 2-6 Dispatcher.BeginInvoke() until those actions finish. Instead, maybe consider using DispatcherTimer and wait 256MS which should be enough time.
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var msgBox = new CustomMessageBox()
{
Caption = "foo",
Message = "bar",
LeftButtonContent = "baz",
RightButtonContent = "goo",
IsFullScreen = false,
};
msgBox.Dismissed += (s, args) =>
{
DispatcherTimerHelper.InvokeReallySoon(() =>
{
new SmsComposeTask()
{
Body = "foo",
To = "bar"
}.Show();
});
};
msgBox.Show();
}
public static class DispatcherTimerHelper
{
public static void InvokeReallySoon(Action action)
{
var t = new DispatcherTimer() {Interval = TimeSpan.FromMilliseconds(256)};
t.Tick += (s, args) => action();
t.Start();
}
}
The problem just happen in wp8.
I use the same code in wp7, nothing wrong happens.
Use Code4fun messagebox is a good choice,but is Button Click handler you need to call
MessagePrompt.Hide();
to close the MessagePrompt.
used a boolean on the dismissed event to define which button had been pressed. I then implemented the code I would of implemented in the dismissed event in the Unloaded event instead. This seemed to solve the issue.
i.e
messageBox.Dismissed += (s1, e1) =>
{
switch (e1.Result)
{
case CustomMessageBoxResult.LeftButton:
{
delete = true ;
}
break;
case CustomMessageBoxResult.RightButton:
break;
case CustomMessageBoxResult.None:
break;
default:
break;
}
};
messageBox.Unloaded += (s1, e1) =>
{
if (delete)
DeleteWorkout();
};
This is a known bug.
It was fixed in the latest version.
Remove the reference and install the toolkit again.