Difference between revisions of "Talk:LlDialog"
Omei Qunhua (talk | contribs) |
Void Singer (talk | contribs) |
||
(21 intermediate revisions by 10 users not shown) | |||
Line 1: | Line 1: | ||
== Button Length Issues == | |||
Telling an end user that the max length of a button label is 24 bytes ... well, not overly helpful. But there is the problem of what what characters translate to how many bytes. Still, is there any way we can give examples of characters? | Telling an end user that the max length of a button label is 24 bytes ... well, not overly helpful. But there is the problem of what what characters translate to how many bytes. Still, is there any way we can give examples of characters? | ||
Line 4: | Line 5: | ||
: Can you use a function like this to find long strings? Then the user will not have to guess what button is bad Somebody check my work please, I am sleepy as always. | : Can you use a function like this to find long strings? Then the user will not have to guess what button is bad Somebody check my work please, I am sleepy as always. | ||
< | <source lang="lsl2">integer GetStringBytes(string s) { | ||
list chopped = llParseString2List(llEscapeURL("*" + s), ["%"], []); | list chopped = llParseString2List(llEscapeURL("*" + s), ["%"], []); | ||
return llStringLength((string)chopped) - llGetListLength(chopped) - 1; | return llStringLength((string)chopped) - llGetListLength(chopped) - 1; | ||
}</ | }</source> | ||
:--[[User:Cerise Sorbet|Cerise Sorbet]] 10:03, 27 November 2009 (UTC) | :--[[User:Cerise Sorbet|Cerise Sorbet]] 10:03, 27 November 2009 (UTC) | ||
:: I was too sleepy. Maybe this time. --[[User:Cerise Sorbet|Cerise Sorbet]] 11:06, 27 November 2009 (UTC) | :: I was too sleepy. Maybe this time. --[[User:Cerise Sorbet|Cerise Sorbet]] 11:06, 27 November 2009 (UTC) | ||
:: Maybe this one will be more nice to memory? --[[User:Cerise Sorbet|Cerise Sorbet]] 13:03, 27 November 2009 (UTC) | :: Maybe this one will be more nice to memory? --[[User:Cerise Sorbet|Cerise Sorbet]] 13:03, 27 November 2009 (UTC) | ||
< | <source lang="lsl2">integer GetStringBytes2(string s) { | ||
s = llEscapeURL(s); | s = llEscapeURL(s); | ||
integer i = 0; | integer i = 0; | ||
Line 18: | Line 19: | ||
i += (llGetSubString(s, j, j) == "%"); | i += (llGetSubString(s, j, j) == "%"); | ||
return llStringLength(s) - i - i; | return llStringLength(s) - i - i; | ||
}</ | }</source> | ||
---- | ---- | ||
Strife, Chaz here. Most ordinary users in SL, and most user documentation in SL, refer to dialog boxes in SL as menus. While that may be as technically inaccurate as referring to a tomato as a vegetable, people don't look for tomatoes in between the apples and bananas at their green grocer (grin), and they are going to come here looking for info on menus, not dialogue boxes. I think we need to at least acknowledge the word menu, even if we do correct them about its usage right away. (I also dislike how people use the word less when they should be using fewer, but that is also just the way it is, sadly :} )[[User:Chaz Longstaff|Chaz Longstaff]] 18:17, 15 July 2008 (PDT) | Strife, Chaz here. Most ordinary users in SL, and most user documentation in SL, refer to dialog boxes in SL as menus. While that may be as technically inaccurate as referring to a tomato as a vegetable, people don't look for tomatoes in between the apples and bananas at their green grocer (grin), and they are going to come here looking for info on menus, not dialogue boxes. I think we need to at least acknowledge the word menu, even if we do correct them about its usage right away. (I also dislike how people use the word less when they should be using fewer, but that is also just the way it is, sadly :} )[[User:Chaz Longstaff|Chaz Longstaff]] 18:17, 15 July 2008 (PDT) | ||
Line 34: | Line 35: | ||
::I agree that the actual explanation shouldn't be here, but a link should be fairly prominent, since this is the most likely place people will run into the difference. [[User:Tali Rosca|Tali Rosca]] 12:03, 15 October 2008 (PDT) | ::I agree that the actual explanation shouldn't be here, but a link should be fairly prominent, since this is the most likely place people will run into the difference. [[User:Tali Rosca|Tali Rosca]] 12:03, 15 October 2008 (PDT) | ||
the current suggested function for "safely" truncating button text length is not safe... it can break across a multibyte character<br/>-- '''[[User:Void_Singer|Void]]''' <sup><small>([[User_talk:Void_Singer|talk]]|[[Special:Contributions/Void_Singer|contribs]])</small></sup> | |||
: It says it "can be used to truncate the string without giving an error". Can you produce any errors due to button length or otherwise, when using it? Usually, if LSL in general, and llBase64ToString in particular in this case, finds an incorrect or incomplete UTF-8 character, it will replace that character with a question mark, and no error will be given. Is that not what you see? | |||
: Thanks for fixing the number, by the way. --[[User:Pedro Oval|Pedro Oval]] ([[User talk:Pedro Oval|talk]]) 14:13, 2 July 2016 (PDT) | |||
I'd have to dig around the unicode code pages, but I do remember it being an issue with name or description field when the truncation resulted in an invalid utf8 sequence or control character. unable to check inworld right now as I'm on a backup video card =S <br/>-- '''[[User:Void_Singer|Void]]''' <sup><small>([[User_talk:Void_Singer|talk]]|[[Special:Contributions/Void_Singer|contribs]])</small></sup> 19:58, 2 July 2016 (PDT) | |||
== Multiple dialogs tips == | == Multiple dialogs tips == | ||
Line 59: | Line 67: | ||
:: Wow, you type fast! I couldn't even move my mouse onto the dialog window that fast. Anyway I changed it. Example #2 had the same remote possibility of accumulating active listeners, I believe. [[User:Omei Qunhua|Omei Qunhua]] 08:10, 16 January 2013 (PST) | :: Wow, you type fast! I couldn't even move my mouse onto the dialog window that fast. Anyway I changed it. Example #2 had the same remote possibility of accumulating active listeners, I believe. [[User:Omei Qunhua|Omei Qunhua]] 08:10, 16 January 2013 (PST) | ||
::: Admittedly it's unlikely to be an issue with that actual example but it's possible that a dialog that spawns a dialog that spawns a dialog... (etc) will have someone camping on a button and a reply can get missed. So I was just thinking it might be an idea to encourage starting the listen fist. [[User:Antony Fairport|Antony Fairport]] 08:25, 16 January 2013 (PST) | |||
== Where's order_buttons? == | |||
Didn't there used to be an example of that very useful helper function, order_buttons(), on this page? Unless there was a particular reason for removing it, I think it would be good to have it back. [[User:Innula Zenovka|Innula Zenovka]] 11:27, 18 February 2013 (PST) | |||
: It's back, a name collision hid it. --[[User:ObviousAltIsObvious Resident|ObviousAltIsObvious Resident]] 12:11, 18 February 2013 (PST) | |||
== Concurreny issue in first code sample (edited) == | |||
The first code sample has the following fundamental problem (as can be easily demonstrated): at any point in time there can be '''multiple''' listeners attached because any number of users can click on the prim. But the script sample has only '''one''' global variable to keep track of it. Result: The last user to click on the prim "wins" in the sense that whoever closes the dialog first will remove his listener. All other listeners will remain active. | |||
The obvious mediation is | |||
# either to introduce a more sophisticated listener tracking (and most likely some form of polling in a regular timer event) | |||
# or to keep it simple and introduce a single static listener, omitting the need for a timer event altogether. | |||
I personally opt for the latter. If there are no objections I can make those changes. Proposal: | |||
<source lang="lsl2"> | |||
// When the prim is touched, give the toucher the option of killing the prim. | |||
integer gListener; // Identity of the listener associated with the dialog, so we can clean up when not needed | |||
default | |||
{ | |||
state_entry() | |||
{ | |||
// Set up a listener for potential llDialog() calls | |||
// it might seem like a waste at this stage, but overall it is way more efficient | |||
// than trying to track listeners on a per-user basis | |||
gListener = llListen(-99, "", NULL_KEY, ""); | |||
} | |||
touch_start(integer total_number) | |||
{ | |||
// get the UUID of the person touching this prim | |||
key user = llDetectedKey(0); | |||
// Send a dialog to that person. We'll use a fixed negative channel number for simplicity | |||
llDialog(user, "\nDo you wish this prim to die?", ["Yes", "No" ] , -99); | |||
} | |||
listen(integer chan, string name, key id, string msg) | |||
{ | |||
// If the user clicked the "Yes" button, kill this prim. | |||
if (msg == "Yes") | |||
llDie(); | |||
} | |||
} | |||
</source> | |||
Btw, the same fundamental flaw is present in the tutorial https://wiki.secondlife.com/wiki/Dialog_Menus . | |||
Edit: the second code sample contains the flaw, too | |||
--[[User:LovingAndRejected Resident|LovingAndRejected Resident]] 05:37, 13 November 2013 (PST) | |||
: I'm not seeing where the multiple listens will come from given that the touch event (in the first sample on this page) removes any previous listen. [[User:Antony Fairport|Antony Fairport]] 08:42, 13 November 2013 (PST) | |||
: Agreed. llListenRemove(gListener) will close any pre-existing instance of gListener. Because a dialog box will never self-cancel and we have no way to detect whether a user has clicked "Ignore", it is necessary to include a timer that will remove the listen handle as well.[[User:Rolig Loon|Rolig Loon]] 09:06 13 November 2013 (PST) | |||
: Your understanding of the examples is incomplete. As written, they do close any possible open listeners before registering another. Note the first line within the touch_start() event handler of the first example, which is commented to that effect, and the call to close_menu() in the second. | |||
: Your proposed change goes against the preferred practice of only opening a listener when required and then closing it immediately afterwards. The (still present under Mono) overhead involved with each and every open listener on a sim should be considered by the scripter. [[User:LepreKhaun Resident|LepreKhaun Resident]] 09:24, 13 November 2013 (PST) | |||
:Ahhh, I now see that there '''is''' a problem with the page though! It does imply that listeners are being registered by the '''llDialog();''' call by not explicitly stating that the function merely opens a dialog menu and it is '''llListen();''' which opens the listener. That is confusing and should be corrected imo. [[User:LepreKhaun Resident|LepreKhaun Resident]] 10:38, 13 November 2013 (PST) | |||
I agree that my initial analysis was wrong in that the wiki code will remove the listener for a previously created listener belonging to another user in the touch event. Therefore the issue of multiple open listeners will not occur. Instead the dialog will go dumb on the other users. If people think that is acceptable for sample code I retract my motion to change it. [[User:LovingAndRejected Resident|LovingAndRejected Resident]] 11:40, 13 November 2013 (PST) | |||
Multi-user concurrency is a big annoying issue complicated by the limit on resources (listens & memory), it's not easy to do so most systems just don't allow it. If you don't mind insecurity and instability, concurrency can be quite easy. Listen-concurrency is really too specialized and complex a topic for llDialog, it really needs it's own page. It might be fun, I'll look into writing about how to do it. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 23:54, 13 November 2013 (PST) | |||
:what strife said as far as concurrency being hairy. If you check out the helper function [[User:Void_Singer/Functions#uDlgBtnPagList]] that I added to the page a while back, you'll see that one way to avoid the concurrency issue is to ignore the conventional wisdom about closing listens, and fitering them in the listen, and instead use a single always on listen, and filter by user key as provided from within the listen event... it's not ideal in all ways (I don't recommend it for mass use of multiple objects in a single area) but it does make unlimited concurrency possible on a single listen. It also results in needing to do more script work per request to filter, but if you need concurrency, it is an option. knowing Strife, it'll be torn apart and something even better will be created from it = D <br/>-- '''[[User:Void_Singer|Void]]''' <sup><small>([[User_talk:Void_Singer|talk]]|[[Special:Contributions/Void_Singer|contribs]])</small></sup> 14:21, 14 November 2013 (PST) | |||
::That's a great solution for static content. I really like it. You could use something besides spaces to store more complex state info. I was thinking something more complex, tailored listens and release them as needed. The advantage of tailored listens is that you don't have to worry about parsing 3rd party messages but it comes at a slight performance cost of having to track listen handles. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 21:14, 14 November 2013 (PST) | |||
== False ignore buttons == | |||
The article says, "If a button is named <code>["Ignore"]</code>, it will behave like the small <code>["Ignore"]</code> button of the menu". I just tested that, and it didn't happen for me. Is it viewer-dependent? I'm on Firestorm 4.6.5. --[[User:Brilliand Resident|Brilliand Resident]] 20:06, 18 August 2014 (PDT) | |||
: This was fixed in 2012, with [[Release Notes/Second Life Release/3.3.1|SL viewer 3.3.1]] and third party viewers based on it. The issue was {{Jira|STORM-1718}}. Some viewers, especially those that keep a V1-style interface (Singularity, Cool VL), could still have the old behavior and should be tested. --[[User:ObviousAltIsObvious Resident|ObviousAltIsObvious Resident]] 20:35, 18 August 2014 (PDT) | |||
:: Documented. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 13:21, 20 August 2014 (PDT) |
Latest revision as of 18:58, 2 July 2016
Button Length Issues
Telling an end user that the max length of a button label is 24 bytes ... well, not overly helpful. But there is the problem of what what characters translate to how many bytes. Still, is there any way we can give examples of characters?
Chaz Longstaff 05:18, 27 November 2009 (UTC)
- Can you use a function like this to find long strings? Then the user will not have to guess what button is bad Somebody check my work please, I am sleepy as always.
integer GetStringBytes(string s) {
list chopped = llParseString2List(llEscapeURL("*" + s), ["%"], []);
return llStringLength((string)chopped) - llGetListLength(chopped) - 1;
}
- --Cerise Sorbet 10:03, 27 November 2009 (UTC)
- I was too sleepy. Maybe this time. --Cerise Sorbet 11:06, 27 November 2009 (UTC)
- Maybe this one will be more nice to memory? --Cerise Sorbet 13:03, 27 November 2009 (UTC)
integer GetStringBytes2(string s) {
s = llEscapeURL(s);
integer i = 0;
integer j;
for (j = llStringLength(s); j > -1; j--)
i += (llGetSubString(s, j, j) == "%");
return llStringLength(s) - i - i;
}
Strife, Chaz here. Most ordinary users in SL, and most user documentation in SL, refer to dialog boxes in SL as menus. While that may be as technically inaccurate as referring to a tomato as a vegetable, people don't look for tomatoes in between the apples and bananas at their green grocer (grin), and they are going to come here looking for info on menus, not dialogue boxes. I think we need to at least acknowledge the word menu, even if we do correct them about its usage right away. (I also dislike how people use the word less when they should be using fewer, but that is also just the way it is, sadly :} )Chaz Longstaff 18:17, 15 July 2008 (PDT)
- Very good point. I'll also keep a lookout for improper uses of "less". -- Strife Onizuka 18:32, 15 July 2008 (PDT)
The Wiki mentions that clicking on a button causes the value to be chatted on the specified channel, but doesn't clarify whether it's simply said(llSay) or shouted(llShout). It does mention that errors will be shouted on the Debug Channel, but I think it's important to clarify this within the Wiki. --Deimos Damone
- I have reworded the applicable caveat to clarify the functionality. -- Strife (talk|contribs) 11:49, 15 October 2008 (PDT)
It may be worth including some explicit explanation of the difference between bytes and unicode characters, especially since the inworld error messages somewhat ambiguously talk about characters, when in fact referring to bytes. -- Tali Rosca 10:45, 15 October 2008 (PDT)
- Many functions use byte limits and not character limits. I think it would be better to make the word "byte" a link to a (new?) subsection of the string article where that can be better addressed. It would be inappropriate to duplicate this information on multiple articles, it would end up clogging the articles. That said, I'll try to find the best method for presenting this information; as you point out the current caveat is inadequate and more information is needed. -- Strife (talk|contribs) 11:49, 15 October 2008 (PDT)
- I agree that the actual explanation shouldn't be here, but a link should be fairly prominent, since this is the most likely place people will run into the difference. Tali Rosca 12:03, 15 October 2008 (PDT)
the current suggested function for "safely" truncating button text length is not safe... it can break across a multibyte character
-- Void (talk|contribs)
- It says it "can be used to truncate the string without giving an error". Can you produce any errors due to button length or otherwise, when using it? Usually, if LSL in general, and llBase64ToString in particular in this case, finds an incorrect or incomplete UTF-8 character, it will replace that character with a question mark, and no error will be given. Is that not what you see?
- Thanks for fixing the number, by the way. --Pedro Oval (talk) 14:13, 2 July 2016 (PDT)
I'd have to dig around the unicode code pages, but I do remember it being an issue with name or description field when the truncation resulted in an invalid utf8 sequence or control character. unable to check inworld right now as I'm on a backup video card =S
-- Void (talk|contribs) 19:58, 2 July 2016 (PDT)
Multiple dialogs tips
It may worth mention in tips.
Sending multiple dialogs in a row to one avatar may be inappropriate. In old viewer it creates stack of messages in notification area, which complicates reading other things. In viewer version 2 (as of 16/Sep/2010) newer dialogs replace unanswered dialogs from the same object, so any information and chance to react is lost.
If you need to present more than 12 options, add buttons for browsing option pages (see SimpleDialogMenuSystem).
If dialogs are not requested by user, but rather are triggered by some events, you may want to either queue further events or discard them until the first dialog will be answered, or at least throttle them. In any case, if you are sending many dialogs, there should be an option to stop them. (Making user mute your object is bad because he may then forget to unmute it when he will want to interact with the object again).
If you just need to report something that does not require an answer, consider using other means of communication. Though, you may still want to send one dialog to draw user's attention.
Wicked Winslet 12:25, 16 September 2010 (UTC)
Example
the first example is very likely to stop working over time with a "too many listens" error in a multi-user environment. Cause: any user that clicks on the object will generate a new listen handle, but only the last one is saved, so only the last one is closed. any user (including the current) that touches the prim before the current user has clicked on a dialog response (or within 60seconds if they do not, or click cancel on the dialog) will cause the current open listen handle to advance, and the previous one will never be closed.
-- Void (talk|contribs) 12:03, 13 January 2013 (PST)
- I suspect the prim will have died long before that LOL. But you have a point Omei Qunhua 13:47, 13 January 2013 (PST)
- In fact it would have to be clicked by 65 different users all choosing to ignore the dialog. A subsequent listen using the same filters as a previous is assigned to the same handle. (Thanks Pedro and Innula) Omei Qunhua 10:30, 14 January 2013 (PST)
Also in the first example, the call to llDialog() happens before the listen is created. In my experience it's possible for this to result in a situation where the dialog is shown to the user and, if they click fast enough, the reply is lost. Might it be a good idea to do it the other way round? Antony Fairport 05:29, 16 January 2013 (PST)
- Wow, you type fast! I couldn't even move my mouse onto the dialog window that fast. Anyway I changed it. Example #2 had the same remote possibility of accumulating active listeners, I believe. Omei Qunhua 08:10, 16 January 2013 (PST)
- Admittedly it's unlikely to be an issue with that actual example but it's possible that a dialog that spawns a dialog that spawns a dialog... (etc) will have someone camping on a button and a reply can get missed. So I was just thinking it might be an idea to encourage starting the listen fist. Antony Fairport 08:25, 16 January 2013 (PST)
Where's order_buttons?
Didn't there used to be an example of that very useful helper function, order_buttons(), on this page? Unless there was a particular reason for removing it, I think it would be good to have it back. Innula Zenovka 11:27, 18 February 2013 (PST)
- It's back, a name collision hid it. --ObviousAltIsObvious Resident 12:11, 18 February 2013 (PST)
Concurreny issue in first code sample (edited)
The first code sample has the following fundamental problem (as can be easily demonstrated): at any point in time there can be multiple listeners attached because any number of users can click on the prim. But the script sample has only one global variable to keep track of it. Result: The last user to click on the prim "wins" in the sense that whoever closes the dialog first will remove his listener. All other listeners will remain active.
The obvious mediation is
- either to introduce a more sophisticated listener tracking (and most likely some form of polling in a regular timer event)
- or to keep it simple and introduce a single static listener, omitting the need for a timer event altogether.
I personally opt for the latter. If there are no objections I can make those changes. Proposal:
// When the prim is touched, give the toucher the option of killing the prim.
integer gListener; // Identity of the listener associated with the dialog, so we can clean up when not needed
default
{
state_entry()
{
// Set up a listener for potential llDialog() calls
// it might seem like a waste at this stage, but overall it is way more efficient
// than trying to track listeners on a per-user basis
gListener = llListen(-99, "", NULL_KEY, "");
}
touch_start(integer total_number)
{
// get the UUID of the person touching this prim
key user = llDetectedKey(0);
// Send a dialog to that person. We'll use a fixed negative channel number for simplicity
llDialog(user, "\nDo you wish this prim to die?", ["Yes", "No" ] , -99);
}
listen(integer chan, string name, key id, string msg)
{
// If the user clicked the "Yes" button, kill this prim.
if (msg == "Yes")
llDie();
}
}
Btw, the same fundamental flaw is present in the tutorial https://wiki.secondlife.com/wiki/Dialog_Menus .
Edit: the second code sample contains the flaw, too
--LovingAndRejected Resident 05:37, 13 November 2013 (PST)
- I'm not seeing where the multiple listens will come from given that the touch event (in the first sample on this page) removes any previous listen. Antony Fairport 08:42, 13 November 2013 (PST)
- Agreed. llListenRemove(gListener) will close any pre-existing instance of gListener. Because a dialog box will never self-cancel and we have no way to detect whether a user has clicked "Ignore", it is necessary to include a timer that will remove the listen handle as well.Rolig Loon 09:06 13 November 2013 (PST)
- Your understanding of the examples is incomplete. As written, they do close any possible open listeners before registering another. Note the first line within the touch_start() event handler of the first example, which is commented to that effect, and the call to close_menu() in the second.
- Your proposed change goes against the preferred practice of only opening a listener when required and then closing it immediately afterwards. The (still present under Mono) overhead involved with each and every open listener on a sim should be considered by the scripter. LepreKhaun Resident 09:24, 13 November 2013 (PST)
- Ahhh, I now see that there is a problem with the page though! It does imply that listeners are being registered by the llDialog(); call by not explicitly stating that the function merely opens a dialog menu and it is llListen(); which opens the listener. That is confusing and should be corrected imo. LepreKhaun Resident 10:38, 13 November 2013 (PST)
I agree that my initial analysis was wrong in that the wiki code will remove the listener for a previously created listener belonging to another user in the touch event. Therefore the issue of multiple open listeners will not occur. Instead the dialog will go dumb on the other users. If people think that is acceptable for sample code I retract my motion to change it. LovingAndRejected Resident 11:40, 13 November 2013 (PST)
Multi-user concurrency is a big annoying issue complicated by the limit on resources (listens & memory), it's not easy to do so most systems just don't allow it. If you don't mind insecurity and instability, concurrency can be quite easy. Listen-concurrency is really too specialized and complex a topic for llDialog, it really needs it's own page. It might be fun, I'll look into writing about how to do it. -- Strife (talk|contribs) 23:54, 13 November 2013 (PST)
- what strife said as far as concurrency being hairy. If you check out the helper function User:Void_Singer/Functions#uDlgBtnPagList that I added to the page a while back, you'll see that one way to avoid the concurrency issue is to ignore the conventional wisdom about closing listens, and fitering them in the listen, and instead use a single always on listen, and filter by user key as provided from within the listen event... it's not ideal in all ways (I don't recommend it for mass use of multiple objects in a single area) but it does make unlimited concurrency possible on a single listen. It also results in needing to do more script work per request to filter, but if you need concurrency, it is an option. knowing Strife, it'll be torn apart and something even better will be created from it = D
-- Void (talk|contribs) 14:21, 14 November 2013 (PST)
- That's a great solution for static content. I really like it. You could use something besides spaces to store more complex state info. I was thinking something more complex, tailored listens and release them as needed. The advantage of tailored listens is that you don't have to worry about parsing 3rd party messages but it comes at a slight performance cost of having to track listen handles. -- Strife (talk|contribs) 21:14, 14 November 2013 (PST)
False ignore buttons
The article says, "If a button is named ["Ignore"]
, it will behave like the small ["Ignore"]
button of the menu". I just tested that, and it didn't happen for me. Is it viewer-dependent? I'm on Firestorm 4.6.5. --Brilliand Resident 20:06, 18 August 2014 (PDT)
- This was fixed in 2012, with SL viewer 3.3.1 and third party viewers based on it. The issue was STORM-1718. Some viewers, especially those that keep a V1-style interface (Singularity, Cool VL), could still have the old behavior and should be tested. --ObviousAltIsObvious Resident 20:35, 18 August 2014 (PDT)