[FIXED] [#1549] [4.4.1] Desktop - after shortcut removal an error occures when adding a new one

  1. #1

    [FIXED] [#1549] [4.4.1] Desktop - after shortcut removal an error occures when adding a new one

    Hello support team,
    when dragging an item from the Start menu to desktop, I can create a new shortcut. This does not work if any of the existing shortcuts has been removed before dragging a new one: "Cannot read property 'insertBefore' of null" error occurs.

    Steps to reproduce:
    - drag a new shortcut on the desktop area
    - remove a shortcut from the desktop area (Start menu -> "Remove...")
    - drag again a new shortcut on the desktop area

    Thank you for your help.

    Dan

    @using Ext.Net;
    @using Ext.Net.MVC;
    
    @{
        ViewBag.Title = "Desktop Drag & Drop Example - FabrÃ*cio's Solution";
        Layout = null;
        var X = Html.X();
    
        List<MenuItem> startMenu = new List<Ext.Net.MenuItem>();
    
        var dragSourceList = new List<DragSource.Builder>();
    
        foreach (var menuEntry in new[]
        {
            new { id = "EU", text = "European Union", icon = Icon.FlagEuropeanunion },
            new { id = "US", text = "United States", icon = Icon.FlagUs }
        })
    
        {
            startMenu.Add(new MenuItem()
            {
                ID = menuEntry.id,
                Icon = menuEntry.icon,
                Text = menuEntry.text
            });
    
            dragSourceList.Add(X.DragSource()
                .Target(menuEntry.id)
                .IsTarget(false)
                .Group("desktopDD")
                .StartDrag(a => a.Fn = "App.ddOverrides.startDrag")
                .OnDragEnter(a => a.Fn = "App.ddOverrides.onDragEnter")
                .OnDragOut(a => a.Fn = "App.ddOverrides.onDragOut")
                .OnInvalidDrop(a => a.Fn = "App.ddOverrides.invalidateDrop")
                .EndDrag(a => a.Fn = "App.ddOverrides.endDrag")
            );
        }
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <title>Ext.NET MVC Test Case</title>
    
        <style>
            .x-default-shortcut {
                background: url("http://www.nlm.cz/img/logoNewlink.png");
                background-position: center;
                background-repeat: no-repeat;
                background-size: cover;
            }
    
            .dropOK {
                background-color: #99ff99 !important;
            }
    
            .dropNotOK {
                border: 1px solid #FF0000 !important;
            }
        </style>
    
        <script type="text/javascript">
            Ext.onReady(function () {
                //
            });
    
            function desktopReady() {
                //
            }
    
            function createModule(moduleId) {
                var eventCfg = {
                    url: "/Desktop/AddModule/", extraParams: { desktopId: "_nelisDesktop", moduleId: moduleId },
                    cleanRequest: true,
                    method: "POST",
                    userSuccess: function (result, response) {
                        console.log("direct event ok");
                    }
                };
                //Ext.net.DirectEvent.request(eventCfg);
                // OR
                var item = Ext.getCmp(moduleId);
                var newModuleId = "dynModule_" + moduleId;
    
                console.log("newModuleId", newModuleId);
    
                App._nelisDesktop.addModule(Ext.create("Ext.ux.desktop.Module", {
                    id: newModuleId,
                    shortcut: { name: item.text, module: newModuleId, IconCls: "x-default-shortcut", handler: function () { var item = Ext.getCmp(moduleId); console.log(item); } },
                    autoRun: true
                }));
    
                App._nelisDesktop.getModule(newModuleId).addLauncher({ xtype: "menuitem", text: item.text });
    
                App._nelisDesktop.getModule(newModuleId).addWindow(
                    function () {
                        App._nelisDesktop.getModule(newModuleId).setWindow(
                            {
                                autoRender: false,
                                height: 300,
                                width: 300,
                                xtype: "window",
                                title: item.text,
                                iconCls: "#ApplicationAdd",
                                defaultRenderTo: "form"
                            });
    
                    });
    
            }
    
            var removeShortcut = function (title) {
                var moduleId = "dynModule_" + (title == "EuropeanUnion" ? "EU" : "US");
                var shortcutId = title + "-shortcut";
    
                Ext.get(shortcutId).destroy();
                Ext.net.Desktop.removeModule(moduleId);
            }
    
            Ext.ns("App").ddOverrides = {
                // Called the instant the element is dragged.
                startDrag: function () {
                    console.log("startDrag");
                    // Cache the drag "phantom" element
                    if (!this.dEl) {
                        this.dEl = Ext.get(this.getDragEl());
                    }
    
                    // Cache the original element
                    if (!this.el) {
                        this.el = Ext.get(this.getEl());
                    }
    
                    // Make the original icon less visible to indicate it is really about to be moved
                    this.el.applyStyles({ opacity: 0.4 });
    
                    this.dEl.applyStyles({
                        border: "",
                        // Trim the sides to 8 pixels to have it match the icons
                        width: (this.el.getWidth() - 8) + "px",
                        height: (this.el.getHeight() - 8) + "px",
                        "z-index": 2000
                    });
    
                    // Create an exact copy of the element on the cursor
                    this.dEl.update(this.el.dom.innerHTML);
    
                    // adding this class is the trick to have drop areas to work
                    // If you just avoid this and then use this.el.dom.outerHTML above, the mouse will
                    // be always above the div and will never trigger an dropEnter event.
                    this.dEl.addCls(this.el.dom.className);
    
                    this.lastTarget = null;
                },
    
                invalidateDrop: function () {
                    this.invalidDrop = true;
                },
    
                // Called when the drag operation completes
                endDrag: function () {
                    console.log("endDrag");
                    // Create the animation configuration object
                    var animCfgObj = {
                        easing: 'ease',
                        duration: 500,
                        opacity: 1,
                        scope: this,
                    };
    
                    var success = !this.invalidDrop;
                    delete this.invalidDrop;
    
                    // Remove the drag/drop effects on the original element
                    this.el.removeCls('dropOK');
                    this.el.applyStyles({ opacity: 1 });
    
                    // Invoke the animation according to the drop success/failure
                    if (success) {
                        // After dragging, remove transparency effect of moved element.
                        this.el.animate(animCfgObj);
                        this.dEl.animate({
                            easing: 'ease',
                            duration: 2500,
                            opacity: 0.1, // left by purpose to see the hiding work
                            scope: this,
                            callback: function () { this.dEl.hide(); }
                        });
    
                        console.log('success end drag');
                    } else {
                        console.log('failure end drag');
    
                        this.dEl.hide();
                    }
    
                    return success;
                },
    
                // Only called when the drag element is dragged over the a drop target with the same ddgroup
                onDragEnter: function (evtObj, targetElId) {
                    console.log("onDragEnter");
                    // Colorize the drag target if the drag node's parent is not the same as the drop target
                    if (targetElId != this.el.dom.parentNode.id) {
                        this.el.addCls('dropOK');
                        this.dEl.addCls('dropOK');
                        this.lastTarget = Ext.get(targetElId);
                    } else {
                        // Remove the invitation
                        this.onDragOut();
                        this.lastTarget = null;
                    }
                },
    
                // Only called when element is dragged out of a dropzone with the same ddgroup
                onDragOut: function (evtObj, targetElId) {
                    console.log("onDragOut");
                    this.el.removeCls('dropOK');
                    this.dEl.removeCls('dropOK');
                    this.lastTarget = null;
                },
    
                notifyDrop: function (dd, e, data) {
                    console.log('notifyDrop');
                    console.log(dd);
                    console.log(e);
                    console.log(data);
                    createModule(dd.id);
                    return true;
                }
            };
        </script>
    </head>
    
    <body>
        @Html.X().ResourceManager()
    
        @(Html.X().Desktop()
            .ID("_nelisDesktop")
            .Listeners(l => l.Ready.Fn = "desktopReady")
            .DesktopConfig(
                Html.X().DesktopConfig()
                .ID("_nelisDesktop_Area")
                .SortShortcuts(true)
                .ShortcutDefaults(defaults => defaults.IconCls = "x-default-shortcut")
                .WallpaperStretch(true)
                .ShortcutDragSelector(true)
            )
            .StartMenu(
                Html.X().DesktopStartMenu()
                    .ID("_nelisDesktop_StartMenu")
                    .Title("Allegro Desktop")
                    .Icon(Icon.Application)
                    .MenuItems(startMenu)
                    .Width(400)
                    .Height(150)
                    .ToolConfig(
                        Html.X().Toolbar()
                            .Width(200)
                            .Items(
                                Html.X().Button()
                                .Icon(Icon.Note)
                                .Text("Remove Notepad Shortcut")
                                .OnClientClick("removeShortcut(\"Notepad\")"),
                                Html.X().Button()
                                .Icon(Icon.FlagEuropeanunion)
                                .Text("Remove EU Shortcut")
                                .OnClientClick("removeShortcut(\"EuropeanUnion\")"),
                                Html.X().Button()
                                .Icon(Icon.FlagUs)
                                .Text("Remove US Shortcut")
                                .OnClientClick("removeShortcut(\"UnitedStates\")")
                            )
                    )
            )
            .TaskBar(
                Html.X().DesktopTaskBar()
                    .TrayWidth(40)
                    .QuickStartWidth(75)
                    .QuickStart(
                        Html.X().Toolbar()
                            .Items(
                                Html.X().Button()
                                    .Icon(Icon.ApplicationTileVertical)
                                    .ToolTip("Tile windows")
                                    .OverflowText("Tile windows"),
                                Html.X().Button()
                                    .Icon(Icon.ApplicationCascade)
                                    .ToolTip("Cascade windows")
                                    .OverflowText("Cascade windows"),
                                Html.X().Button()
                                    .Icon(Icon.ApplicationViewIcons)
                                    .ToolTip("Default icons positions")
                            )
                    )
            )
            .Modules(
                Html.X().DesktopModule()
                    .ModuleID("Notepad")
                    .Shortcut(new DesktopShortcut {
                        Name = "Notepad",
                        IconCls = "x-default-shortcut",
                        X = "250",
                        Y = "250",
                        Handler = "console.log('notepad_clicked')"
                    })
            )
        )
    
        @{
            foreach (var dds in dragSourceList) {
                @(dds)
            }
        }
    
        @(X.DropZone()
            .Target("_nelisDesktop_Area-body")
            .Group("desktopDD")
            .IsTarget(true)
            .OnContainerDrop(a => a.Fn = "App.ddOverrides.onDrop")
            .NotifyDrop(a => a.Fn = "App.ddOverrides.notifyDrop")
            .OnNodeDrop(a => a.Fn = "App.ddOverrides.nodeDrop")
        )
    </body>
    </html>
    Last edited by fabricio.murta; Sep 22, 2017 at 2:58 AM.
  2. #2
    Hello @NewLink!

    Thanks for reporting the issue, and sorry for the problem in the application development. In fact, you only should need to be calling Ext.net.Desktop.removeModule(moduleId); in order to remove the module and its bound desktop icon.

    So for that bug, we've created the issue #1549 on github to track & fix this. We'll post again here as soon as we fixed this for good in Ext.NET source base.

    But I'm sure you're not willing to just wait the fix to be done, as it will still need more investigation for the best implementation to fix the issue for good.

    Thinking that I'm providing you the removal method that would work for you. Notice you'll have to ensure no icons will have the same name in the desktop, or the wrong icon will potentially be removed from the desktop when removing a module (be it a dynamic or a static one).

    Here's the reviewed code:

    var removeShortcut = function (title) {
        var moduleId = "dynModule_" + (title == "EuropeanUnion" ? "EU" : "US"),
            module = App._nelisDesktop.getModule(moduleId),
            desktop = App._nelisDesktop.desktop,
            shortcuts = desktop.shortcuts,
            sc_count = shortcuts.getCount(),
            shortcut, i;
    
        for (i = 0; i < sc_count; i++) {
            shortcut = shortcuts.getAt(i);
            if (shortcut.data.name == module.shortcut.name) {
                shortcuts.removeAt(i);
                break;
            }
        }
    
        Ext.net.Desktop.removeModule(moduleId);
    }
    This just loops thru the list of present icons and then delete the one matching the name of the icon defined directly in the module data. Only after removing that icon (deletes the first matching the name) it then will wipe out the module (which fails at detecting present icons pertaining that module at the moment).

    Please notice the above code is minimalist and depending on your scenario you may want to check, for example, if the module has a shortcut defined at all, avoiding undefined reference on module.shortcut.name.

    I hope this helps!
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Hi FabrÃ*cio,
    thank you very much for your help. The workaround does the job and as a temporary solution is perfectly suitable.

    Dan
  4. #4
    Hello FabrÃ*cio,
    I have encountered still one problem. If the MessageBox dialogue is added to removal procedure and the "notepad" window is open, the Start menu stops working correctly after removing the shortcut. If the "notepad" is minimized, everything is fine.

    The code above has been modified as follows (we do not need module launcher and window, we are interested only in a shortcut):

    var createModule = function (moduleId) {
        var item = Ext.getCmp(moduleId);
        var newModuleId = "dynModule_" + moduleId;
    
        App._nelisDesktop.addModule(Ext.create("Ext.ux.desktop.Module", {
            id: newModuleId,
            shortcut: { name: item.text, module: newModuleId, IconCls: "x-default-shortcut" },
            autoRun: true
        }));
    }
    
    var removeShortcut = function (title) {
        Ext.MessageBox.show({
            title: "Remove shortcut from desktop",
            msg: "Shortcut will be removed from desktop. Are you sure?",
            buttons: Ext.MessageBox.YESNO,
            icon: Ext.MessageBox.WARNING,
            fn: function (btn) {
                if (btn == "yes") {
                    var moduleId = "dynModule_" + (title == "EuropeanUnion" ? "EU" : "US"),
                        module = App._nelisDesktop.getModule(moduleId),
                        desktop = App._nelisDesktop.desktop,
                        shortcuts = desktop.shortcuts,
                        sc_count = shortcuts.getCount(),
                        shortcut, i;
    
                    for (i = 0; i < sc_count; i++) {
                        shortcut = shortcuts.getAt(i);
                        if (shortcut.data.name == module.shortcut.name) {
                            shortcuts.removeAt(i);
                            break;
                        }
                    }
    
                    Ext.net.Desktop.removeModule(moduleId);
                } else {
                    //
                }
            }
        });
    }
    ...
    .Modules(
        Html.X().DesktopModule()
            .ModuleID("Notepad")
            .Shortcut(new DesktopShortcut
            {
                Name = "Notepad",
                IconCls = "x-default-shortcut",
                X = "250",
                Y = "250",
                Handler = "var win = App._nelisDesktop.desktop.createWindow({ autoRender: false, height: 300, width: 300, xtype: 'window', title: 'Notepad', iconCls: '#ApplicationAdd', defaultRenderTo: 'form' }); win.show();"
            })
    )
    ...
    Steps to reproduce:
    A.
    - open a notepad
    - drag a new shortcut
    - remove that shortcut
    - start menu stops to work correctly
    B.
    - open a notepad and minimize the window
    - drag a new shortcut
    - remove that shortcut
    - start menu works correctly

    Also, everything goes well if we for module define also launcher and window (which is, however, something we do not need when creating just a shortcut). Though, the option "no" in dialogue still causes the same issue.

    Thank you for your help.

    Dan
  5. #5
    Hello @Newlink!

    Thanks for the feedback! This is really strange. Even more if you add a Ext.toast(title); to the callback fn.

    With that, in the attempts here, it worked fine.

    The problem basically is, that fn callback is called asynchronously to the click event, and then some sort of race condition is happening to break the start menu.

    We're investigating that issue and we'll get back to you soon. I'm shooting to an usage issue at the moment. If you just remove the confirmation dialog (merge back the function, without the wait for button click), then it works fine.
    Fabrício Murta
    Developer & Support Expert
  6. #6
    Hello again, @NewLink!

    Just reduced your issue down to a simple example containing a window, a menu button and a menu entry triggering an Ext.Msg.alert().

    So, this is not related to the desktop component at all. Would you mind opening a new thread for us to handle this? I'm going to create a new issue now, but it would be confusing to link two different github issues to a same forum thread.

    Unfortunately, the very fact of showing a modal (window with that mask around it) triggers this issue.

    Fortunately, I could find an extremely easy (and pragmatic) workaround to avoid this bug. Just force-set the start menu's hidden property to false every time you call the removeShortcut() method and you should be good. I.e.:

    App._nelisDesktop_StartMenu.hidden = false;
    I hope this helps!
    Fabrício Murta
    Developer & Support Expert
  7. #7
    Hello FarÃ*cio,
    thank you for a prompt reply. To set the start menu's hidden property to false helps only partially. The start menu is not broken, but you have to click twice in order to open it (which is no big deal). However, Ext.toast(title) works just fine. So, for "yes" option I use toast, for "no" I set App._nelisDesktop_StartMenu.hidden to false. As workaround more than enough.

    I also opened a new thread for this issue, as you sugggested.

    Thank you for your help. I very much appreciate that if it is only a little possible, you come up with a (temporary) solution, so development can continue.

    Dan
    Last edited by NewLink; Sep 21, 2017 at 9:18 AM.
  8. #8
    Hello @NewLink!

    Thanks for opening the new thread. Anything related to the "stuck" start menu will be handled there from this point on.

    Well, and as far as the shortcut removal is concerned, it has now been fixed. Calling the desktop's removeModule() method will reliably remove also the modules with the latest fix we just applied to our github repository.

    This fix is available right now if you grab Ext.NET sources from github, and will also be publicly available once we release Ext.NET 4.4.1, which we are planning to do in the next few days.

    If you get to test the fix from Ext.NET built from sources, please let us know whether the fix really does perform well on your scenario. As far as the test case is concerned, it should work just fine.

    Thanks again for reporting the issues!
    Fabrício Murta
    Developer & Support Expert
  9. #9
    Hi FabrÃ*cio,
    just installed Ext.NET 4.4.1 and... everything works like a charm. Thank you for your support and assistance.

    Dan
  10. #10
    Hello Dan!

    Thanks for the feedback, glad it really works fine after the upgrade!
    Fabrício Murta
    Developer & Support Expert

Similar Threads

  1. [CLOSED] Set x, y position shortcut from desktop (with usercontrols)
    By CarWise in forum 3.x Legacy Premium Help
    Replies: 2
    Last Post: Feb 03, 2017, 12:28 AM
  2. Desktop shortcut handler error
    By sercanisik in forum 3.x Help
    Replies: 1
    Last Post: Aug 21, 2015, 11:01 AM
  3. Can we Remove Desktop Shortcut.
    By nagesh in forum 2.x Help
    Replies: 16
    Last Post: Jan 18, 2013, 1:50 PM
  4. Desktop Shortcut BUG on IE
    By joao.msdn in forum 1.x Help
    Replies: 8
    Last Post: Jun 27, 2011, 6:46 AM
  5. [CLOSED] Desktop Shortcut
    By schellappa in forum 1.x Legacy Premium Help
    Replies: 3
    Last Post: Mar 14, 2011, 12:08 PM

Posting Permissions