• Voyager 1 is, as of now, 22 billion kilometers away from home. One day, we’ll be a space-faring species and we’ll have Voyager 1 in a museum. It’ll be trivial for us to go and reach Voyager but we won’t retrieve it. What’s wonderful about Voyager 1 is not only the amazing science and engineering that we can see on the metal, plastics, cables, circuits, panels, batteries, etc. What’s amazing is it’s vector: direction and speed.

    We’ll build a museum around Voyager 1 for people to visit and see it travel. The museum will have to be built very carefully, bringing materials from all directions at the same time, in a balanced way, to avoid affecting Voyager’s trip. Even the visitors will have to be controlled to avoid affecting it.

    We’ll marvel at what once was the man-made object furthest away from earth, from home, from the cradle back when all of mankind lived there. Billions of minds will visit it and marvel through the millennia. The museum will act as beacon for commerce and science to stay away of its path… until that day.

    One day, Voyager 1’s path will intersect with something else. It might be a planet, an asteroid, a star, a black hole. The odds are astronomical you might think, but so is, well, space and time.

    That day unrecognizable humanity will gather to decide what to do. We’ll be mature enough to not need that piece of metal somewhere safe and instead we’ll say good bye.

    Part of humanity will gather around, for weeks, maybe even months. We’ll have a festival in space about the 20th century, about how fucked it was, marveling at how close humanity came to self destruction and still produced Voyager 1. We’ll watch movies, attend concerts, both old and new. And eventually, the museum we’ll retreat and so we’ll we. We’ll all watch is silence as a relic of our infancy reaches the end of its life, as it collides and disintegrates. We’ll celebrate it, we’ll mourn it.

  • Update: this list is now being maintained on it’s own page: Finding a co-founder

    Someone recently contacted me at MicroMentor with a few questions about their startup, including, how to proceed without a business partner. I told him he should probably find one and then I discovered I had more co-founder search resources than I thought:

    Any other resources you are aware of?

  • This probably exist but I couldn’t find it. I wanted to export a bunch of data from a Python/Django application into something a non-coder could understand. The data was not going to be a plain CSV, but a document, with various tables and explanations of what each table is. Because ReStructured Text seems to be the winning format in the Python world I decided to go with that.

    Generating the text part was easy and straightforward. The question was how to export tables. I decided to represent tables as lists of dicts and thus, I ended up building this little module:

    def dict_to_rst_table(data):
        field_names, column_widths = _get_fields(data)
        with StringIO() as output:
            output.write(_generate_header(field_names, column_widths))
            for row in data:
                output.write(_generate_row(row, field_names, column_widths))
            return output.getvalue()
    
    
    def _generate_header(field_names, column_widths):
        with StringIO() as output:
            for field_name in field_names:
                output.write(f"+-{'-' * column_widths[field_name]}-")
            output.write("+\n")
            for field_name in field_names:
                output.write(
                    f"| {field_name} {' ' * (column_widths[field_name] - len(field_name))}"
                )
            output.write("|\n")
            for field_name in field_names:
                output.write(f"+={'=' * column_widths[field_name]}=")
            output.write("+\n")
            return output.getvalue()
    
    
    def _generate_row(row, field_names, column_widths):
        with StringIO() as output:
            for field_name in field_names:
                output.write(
                    f"| {row[field_name]}{' ' * (column_widths[field_name] - len(str(row[field_name])))} "
                )
            output.write("|\n")
            for field_name in field_names:
                output.write(f"+-{'-' * column_widths[field_name]}-")
            output.write("+\n")
            return output.getvalue()
    
    
    def _get_fields(data):
        field_names = []
        column_widths = defaultdict(lambda: 0)
        for row in data:
            for field_name in row:
                if field_name not in field_names:
                    field_names.append(field_name)
                column_widths[field_name] = max(
                    column_widths[field_name], len(field_name), len(str(row[field_name]))
                )
    return field_names, column_widths

    It’s straightforward and simple. It currently cannot deal very well with cases in which dicts have different set of columns.

    Should this be turned into a reusable library?

  • I like my text properly formatted and with pretty much every CMS editor out there I always have some confusion when it comes to titles. The post or page has a title and then sections inside it also have titles and they are second level titles. On most CMS you have the option of titles or headers starting at level 1 through 6 (that maps to h1, h2, through h6 in HTML).

    For example, this is Confluence, a tool that I really like:

    The confusion that I get here is whether a subsection to this page should have Heading 1 or Heading 2. Sometimes Heading 1 will be displayed with the same font, size, etc as the title of the page, so, by using Heading 1 you are almost creating two pages in one. But in other CMS, Heading 1 is the top level section heading and the title of the page is a special title that will always sit above it.

    The new WordPress.com editor does this correctly by being very clear that your first option for section headers, after setting the title of the page or post, is h2:

    I think that was a very neat solution to the problem. Bravo WordPress.

  • I recently found myself having to prepare a report of some mortgage calculations so that non-technical domain experts could read it, evaluate it, and tell me whether my math and the way I was using certain APIs was correct.

    Since I’m using Python, I decided to go as native as possible and make my little script generate a ReStructured Text file that I would then convert into HTML, PDFs, whatever. The result of certain calculations ended up looking like a data table expressed as list of dicts all with the same keys. I wrote a function that would turn that list of dicts into the appropriately formatted ReStructured Text.

    For example, given this data:

    creators = [{"name": "Guido van Rossum", "language": "Python"}, 
                {"name": "Alan Kay", "language": "Smalltalk"},
                {"name": "John McCarthy", "language": "Lisp"}]

    when you call it with:

    dict_to_rst_table(creators)

    it produces:

    +------------------+-----------+
    | name             | language  |
    +==================+===========+
    | Guido van Rossum | Python    |
    +------------------+-----------+
    | Alan Kay         | Smalltalk |
    +------------------+-----------+
    | John McCarthy    | Lisp      |
    +------------------+-----------+

    The full code for this is:

    from collections import defaultdict
    
    from io import StringIO
    
    
    def dict_to_rst_table(data):
        field_names, column_widths = _get_fields(data)
        with StringIO() as output:
            output.write(_generate_header(field_names, column_widths))
            for row in data:
                output.write(_generate_row(row, field_names, column_widths))
            return output.getvalue()
    
    
    def _generate_header(field_names, column_widths):
        with StringIO() as output:
            for field_name in field_names:
                output.write(f"+-{'-' * column_widths[field_name]}-")
            output.write("+\n")
            for field_name in field_names:
                output.write(f"| {field_name} {' ' * (column_widths[field_name] - len(field_name))}")
            output.write("|\n")
            for field_name in field_names:
                output.write(f"+={'=' * column_widths[field_name]}=")
            output.write("+\n")
            return output.getvalue()
    
    
    def _generate_row(row, field_names, column_widths):
        with StringIO() as output:
            for field_name in field_names:
                output.write(f"| {row[field_name]}{' ' * (column_widths[field_name] - len(str(row[field_name])))} ")
            output.write("|\n")
            for field_name in field_names:
                output.write(f"+-{'-' * column_widths[field_name]}-")
            output.write("+\n")
            return output.getvalue()
    
    
    def _get_fields(data):
        field_names = []
        column_widths = defaultdict(lambda: 0)
        for row in data:
            for field_name in row:
                if field_name not in field_names:
                    field_names.append(field_name)
                column_widths[field_name] = max(column_widths[field_name], len(field_name), len(str(row[field_name])))
        return field_names, column_widths

    Feel free to use it as you see fit, and if you’d like this to be a nicely tested reusable pip package, let me know and I’ll turn it to one. One thing that I would need to add is making it more robust to malformed data and handle more cases of data that looks differently.

    If I turn it into a pip package, it would be released from Eligible, as I wrote this code while working there and we are happy to contribute to open source.

  • I’m referring to the new series, the Netflix one. I’ve never really watched the old one but I’ve seen enough clips to know: “Danger Will Robinson”. There are several reasons why I stopped watching even though I’m starving for non-pessimistic future space science fiction.

    The first one is that I don’t enjoy seeing people make obvious bad decisions. When it was the kids making bad decisions it was annoying but it made sense, they are kids after all. But when it’s adults, I can’t stand it. These are supposed to be a selection of the most intelligent and capable adults on earth, and yet, they constantly make bad decisions.

    Some spoilers ahead. Stop reading here if you don’t want to be exposed to them.

    For example, Maureen Robinson decided to go do an experiment with a high altitude balloon, on her own, without telling anybody, on a planet of unknown levels danger. She should have taken a few people with her and notified other people where she was going to be. This is what we do on earth when we go hiking or some other wilderness adventure. At the very least her ex-husband would have jumped into the opportunity as he’s hungry for her acceptance but also being a former military man, he’s a good asset. This almost cost her life in a very stupid way.

    Ignacio Serricchio, the mechanic, not notifying everybody what happened with Dr. Smith. Judy Robinson not notifying everybody what happened with Dr. Smith. See a pattern? There’s a lot of information hoarding. When you are in a life-or-death situation in such a small community of survivals, you don’t hoard information. You share it so that if they are part of a bigger puzzle, someone can put it together.

    I had to stop watching when Maureen Robinson discovered their impending doom and didn’t immediately tell everybody. I don’t know if she’ll tell them afterwards but just considering not doing was the straw that broke the camel’s back. I sort of understand keeping doom-predicting information from the general population of Earth, to avoid panic, specially if the average person can’t do anything about it. This is not the case. They are a elite, they are a small community of highly capable people that should know if they are doomed to work their asses off to solve the problem, using resources that maybe they would have been saving for the future otherwise. A future that doesn’t exist.

    Oh… and electricity, physics, astrophysics and medicine are all horribly wrong. Not a little wrong, not a little exaggerated: horribly wrong.

  • Disclaimer: I don’t know what I’m talking about, I’ve done little Win API (Win32) development and I only have a few years of Java development of which maybe 2 or 3 are developing desktop applications with JavaFX (Dashman being my first fully fledged out JavaFX app).

    Disclaimer 2: I have only tested this on my own computer, running Microsoft Windows 10. I hope to soon test it in many others and over time we’ll see whether my solution was correct or not. I’ll update this blog post accordingly (or link to a newer version if necessary).

    I started taking the quality of Dashman very seriously and one of the problems I found was that the running instances wouldn’t exit properly during uninstall or upgrades. And as I expected, this turned out into a head-bashing-into-brick-wall task. My solution was for a JavaFX app, but this should work for a Swing or any other kind of apps.

    It all started with learning about Windows Restart Manager, something I didn’t know it even existed until a week ago. This is what allows Windows to close applications on uninstall, on reboots, etc. In the Guidelines for Applications, the crucial bit is this:

    The Restart Manager queries GUI applications for shutdown by sending a WM_QUERYENDSESSION notification that has the lParam parameter set to ENDSESSION_CLOSEAPP (0x1). Applications should not shut down when they receive a WM_QUERYENDSESSION message because another application may not be ready to shut down. GUI applications should listen for the WM_QUERYENDSESSION message and return a value of TRUE if the application is prepared to shut down and restart. If no application returns a value of FALSE, the Restart Manager sends a WM_ENDSESSION message with the lParam parameter set to ENDSESSION_CLOSEAPP (0x1) and the wparam parameter set to TRUE. Applications should shut down only when they receive the WM_ENDSESSION message. The Restart Manager also sends a WM_CLOSE message for GUI applications that do not shut down on receiving WM_ENDSESSION. If any GUI application responds to a WM_QUERYENDSESSION message by returning a value of FALSE, the shutdown is canceled. However, if the shutdown is forced, the application is terminated regardless.

    Simplifying it: when Windows needs your app to close, it will send a message asking if you are ready to close. Your application might respond negatively and then no application will be closed. This could happen for example if there’s some unsaved work and the app needs the consent from the user to either save or discard. This is what happens when you try to shut down your computer and Microsoft Word stops it asking whether you want to save the file or not.

    After that your application can receive a message asking it to please close or telling it to close now. I’m not sure what the nuances are between these two. For Dashman I decided to just save the config and close in either of these instances.

    Receiving these messages requires interfacing with Windows DLLs, for which I’m using JNA. I don’t know how JNA works, I read the code, sort-of understood it, copied and pasted it. What I think is going on is that you open the user32.dll like this:

    User32 user32 = Native.loadLibrary("user32", User32.class, Collections.unmodifiableMap(options))

    User32 is an interface that contains all the methods with the proper signatures to be able to call them from Java. options just makes sure we are using the Unicode version of the Win32 API calls. You can see that and all the other missing pieces on the full example at the end of the blog post.

    I need a Win32 API callback that will receive the messages and actually implement the guidelines previously quoted:

    StdCallLibrary.StdCallCallback proc = new StdCallLibrary.StdCallCallback() {
        public WinDef.LRESULT callback(WinDef.HWND hwnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
            if (uMsg == WM_QUERYENDSESSION && lParam.intValue() == ENDSESSION_CLOSEAPP) {
                return new WinDef.LRESULT(WIN_TRUE);
            } else if ((uMsg == WM_ENDSESSION && lParam.intValue() == ENDSESSION_CLOSEAPP && wParam.intValue() == WIN_TRUE) || uMsg == WM_CLOSE) {
                Application.exit();
                return new WinDef.LRESULT(WIN_FALSE); 
            }
            return user32.DefWindowProc(hwnd, uMsg, wParam, lParam);
     
        }
    };

    Oh! Lot’s of constants! What are they? I define them in the full example at the bottom of this post. They should be mostly self-evident what they stand for, their actual values are not that important.

    Now things get tricky. Apparently Microsoft Windows send these messages to windows, not processes. Dashman can run in the tray bar, with no active window. And even if it had an active window, getting the HWND pointer for that window in JavaFX doesn’t seem trivial (I couldn’t get it to work). So, I create a size 0 invisible window to receive the message:

    WinDef.HWND window = user32.CreateWindowEx(0, "STATIC", "Dashman Win32 Restart Manager Window.", WS_MINIMIZE, 0, 0, 0, 0, null, null, null, null);

    Then I need to connect that window to the callback:

    try {
        user32.SetWindowLongPtr(window, GWL_WNDPROC, proc);
    } catch (UnsatisfiedLinkError e) {
        user32.SetWindowLong(window, GWL_WNDPROC, proc);
    }

    The callback is not magic though, and requires an event loop that will constantly check if there’s a message and trigger the processing when that happens:

    WinUser.MSG msg = new WinUser.MSG();
    while (user32.GetMessage(msg, null, 0, 0) > 0) {
        user32.TranslateMessage(msg);
        user32.DispatchMessage(msg);
    }

    Of course, that means you want this to run as its own daemon thread. The reason to make it a daemon thread is so that it won’t hang around preventing the JVM from exiting. 

    One of my most useful sources of understanding and inspiration was the source code for Briar. I want to give credit where credit is due. I do think I spotted an issue with their source code in which they are not following the guidelines though. Also, they have a much more complex situation to handle.

    And now, the full example with all my comments including links to more information explaining where all the values for constants and logic is coming from:

    import com.sun.jna.Native;
    import com.sun.jna.Pointer;
    import com.sun.jna.platform.win32.WinDef;
    import com.sun.jna.platform.win32.WinUser;
    import com.sun.jna.win32.StdCallLibrary;
    import com.sun.jna.win32.W32APIFunctionMapper;
    import com.sun.jna.win32.W32APITypeMapper;
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    import static com.sun.jna.Library.OPTION_FUNCTION_MAPPER;
    import static com.sun.jna.Library.OPTION_TYPE_MAPPER;
    
    // Inspiration can be found at https://code.briarproject.org/akwizgran/briar
    public class RestartManager {
        // https://autohotkey.com/docs/misc/SendMessageList.htm
        private static final int WM_CLOSE = 0x10; // https://msdn.microsoft.com/en-us/library/windows/desktop/ms632617
        private static final int WM_QUERYENDSESSION = 0x11; // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376890
        private static final int WM_ENDSESSION = 0x16; // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376889
    
        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376890
        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376889
        private static final int ENDSESSION_CLOSEAPP = 0x00000001;
        private static final int ENDSESSION_CRITICAL = 0x40000000;
        private static final int ENDSESSION_LOGOFF = 0x80000000;
    
        // https://stackoverflow.com/questions/50409858/how-do-i-return-a-boolean-as-a-windef-lresult
        private static final int WIN_FALSE = 0;
        private static final int WIN_TRUE = 1;
    
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
        private static final int GWL_WNDPROC = -4;
    
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
        private static final int WS_MINIMIZE = 0x20000000;
    
        public static void enable() {
            Runnable evenLoopProc = () -> {
                // Load user32.dll usi the Unicode versions of Win32 API calls
                Map<String, Object> options = new HashMap<>();
                options.put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
                options.put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
                User32 user32 = Native.loadLibrary("user32", User32.class, Collections.unmodifiableMap(options));
    
                // Function that handles the messages according to the Restart Manager Guidelines for Applications.
                // https://msdn.microsoft.com/en-us/library/windows/desktop/aa373651
                StdCallLibrary.StdCallCallback proc = new StdCallLibrary.StdCallCallback() {
                    public WinDef.LRESULT callback(WinDef.HWND hwnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
                        if (uMsg == WM_QUERYENDSESSION && lParam.intValue() == ENDSESSION_CLOSEAPP) {
                            return new WinDef.LRESULT(WIN_TRUE); // Yes, we can exit whenever you want.
                        } else if ((uMsg == WM_ENDSESSION && lParam.intValue() == ENDSESSION_CLOSEAPP
                                && wParam.intValue() == WIN_TRUE) || uMsg == WM_CLOSE) {
                            Application.exit();
                            return new WinDef.LRESULT(WIN_FALSE); // Done... don't call user32.DefWindowProc.
                        }
                        return user32.DefWindowProc(hwnd, uMsg, wParam, lParam); // Pass the message to the default window procedure
    
                    }
                };
    
                // Create a native window that will receive the messages.
                WinDef.HWND window = user32.CreateWindowEx(0, "STATIC",
                        "Dashman Win32 Restart Manager Window.", WS_MINIMIZE, 0, 0, 0,
                        0, null, null, null, null);
    
                // Register the callback
                try {
                    user32.SetWindowLongPtr(window, GWL_WNDPROC, proc); // Use SetWindowLongPtr if available (64-bit safe)
                } catch (UnsatisfiedLinkError e) {
                    user32.SetWindowLong(window, GWL_WNDPROC, proc); // Use SetWindowLong if SetWindowLongPtr isn't available
                }
    
                // The actual event loop.
                WinUser.MSG msg = new WinUser.MSG();
                while (user32.GetMessage(msg, null, 0, 0) > 0) {
                    user32.TranslateMessage(msg);
                    user32.DispatchMessage(msg);
                }
            };
    
            Thread eventLoopThread = new Thread(evenLoopProc, "Win32 Event Loop");
            eventLoopThread.setDaemon(true); // Make the thread a daemon so it doesn't prevent Dashman from exiting.
            eventLoopThread.start();
        }
    
        private interface User32 extends StdCallLibrary {
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
            WinDef.HWND CreateWindowEx(int dwExStyle, String lpClassName, String lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, WinDef.HWND hWndParent, WinDef.HMENU hMenu, WinDef.HINSTANCE hInstance, Pointer lpParam);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633572(v=vs.85).aspx
            WinDef.LRESULT DefWindowProc(WinDef.HWND hWnd, int Msg, WinDef.WPARAM wParam, WinDef.LPARAM lParam);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
            WinDef.LRESULT SetWindowLong(WinDef.HWND hWnd, int nIndex, StdCallLibrary.StdCallCallback dwNewLong);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644898(v=vs.85).aspx
            WinDef.LRESULT SetWindowLongPtr(WinDef.HWND hWnd, int nIndex, StdCallLibrary.StdCallCallback dwNewLong);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
            int GetMessage(WinUser.MSG lpMsg, WinDef.HWND hWnd, int wMsgFilterMin, int wMsgFilterMax);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955(v=vs.85).aspx
            boolean TranslateMessage(WinUser.MSG lpMsg);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644934(v=vs.85).aspx
            WinDef.LRESULT DispatchMessage(WinUser.MSG lpmsg);
        }
    }

    And now, my usual question: do you think this should be a reusable open source library? would you use it?

  • A friend of my dad introduced me to ham radio when I was 7 years old. When I was 15 or so I passed my beginner’s exam and then I did nothing with it. I got my call sign when I was 24 years old and moving out of Argentina: LU5ARC. I never used it because Argentina is not part of CEPT (and I haven’t gone back except for short holidays).

    To get that Argentinean license, I had to take three months of two evenings a week of lessons on theory, Morse code and operating a radio (just making QSOs on 80 meters). I actually collected about 10 QSLs from that time (I wish I knew where they are).

    When I moved to the UK almost 7 years ago, I looked into transferring my license but I was told it was impossible. I wish they also told me how easy it was to get a license in the UK and I wouldn’t have waited so long to get started. Last year something else got me interested in radio and I decided to take the plunge and get licensed. I was delighted to see how easy it is.

    The hardest part of getting licensed was waiting for the two day course to happen (at that point, I didn’t know about ML&S running them). Because of my previous experience with radio and the fact that I studied electronics and electromechanics in school, there was little to nothing that I didn’t know for the foundation level. Without too much effort I got my first British call sign: M6UON.

    Then, I had to wait again and I was thrilled to find that ML&S run foundation and intermediate courses, as well as advanced exams so often. I took the course, pass the exam, and I got my intermediate license: 2E0GGE. A month after that, I took the advanced exam and I now have my full license M0ONP.

    Oh… even before there was a foundation course available, I went to the RSGB convention and I took the three exams in a row for the FCC (American) license, so, even before managing to get M6UON, I got an extra (full) one for the US as AC1DM. So ironic!

    2018-04-02-15-56-49.jpg

    And now the fun begins. I lifted all possible restrictions. I can use the full 100W of my Icom IC-7300 as well as take my Icom ID-51E PLUS2 abroad and use it. I can also supervise unlicensed people so I’ve been introducing all my friends to ham radio. I either have friends that are genuinely interested in this technical hobby that’s going without them knowing about or very good friends that humor me when I spend hours explaining frequency, modulation, SWR, antennas, bandwidth, etc.

  • Update 2018-05-23: Updated the code to my current version, which fixes a few bugs.

    When doing usability testing of an alpha version of Dashman, one thing that I was strongly asked was to have the windows remember their sizes when you re-open the application. The need was clear as it was annoying to have the window be a different size when re-started.

    The new version of Dashman is built using Java and JavaFX and thus I searched for how to do this, how to restore size. I found many posts, forums, questions, etc all with the same simplistic solution: restoring width and height, and maybe position.

    What those were missing was restoring whether the window was maximized (maximized is not the same as occupying all the available space, at least in Windows). But most important than that, none of the solutions took into consideration the fact that the resolutions and quantity of screens could be different than the last time the application run, thus, you could end up with a window completely out of bounds, invisible, immobile.

    I came up with this solution, a class that’s designed to be serializable to your config to store the values but also restore them and make sure the window is visible and if not, move it to a visible place:

    package tech.dashman.dashman;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import javafx.application.Platform;
    import javafx.geometry.Rectangle2D;
    import javafx.stage.Screen;
    import javafx.stage.Stage;
    import lombok.Data;
    import tech.dashman.common.Jsonable;
    @Data
    public class StageSizer implements Jsonable {
    private static double MINIMUM_VISIBLE_WIDTH = 100;
    private static double MINIMUM_VISIBLE_HEIGHT = 50;
    private static double MARGIN = 50;
    private static double DEFAULT_WIDTH = 800;
    private static double DEFAULT_HEIGHT = 600;
    private Boolean maximized = false;
    private Boolean hidden = false;
    private Double x = MARGIN;
    private Double y = MARGIN;
    private Double width = DEFAULT_WIDTH;
    private Double height = DEFAULT_HEIGHT;
    @JsonIgnore
    private Boolean hideable = true;
    @JsonIgnore
    public void setStage(Stage stage) {
    // First, restore the size and position of the stage.
    resizeAndPosition(stage, () -> {
    // If the stage is not visible in any of the current screens, relocate it to the primary screen.
    if (isWindowIsOutOfBounds(stage)) {
    moveToPrimaryScreen(stage);
    }
    // And now watch the stage to keep the properties updated.
    watchStage(stage);
    });
    }
    private void resizeAndPosition(Stage stage, Runnable callback) {
    Platform.runLater(() -> {
    if (getHidden() != null && getHidden() && getHideable()) {
    stage.hide();
    }
    if (getX() != null) {
    stage.setX(getX());
    }
    if (getY() != null) {
    stage.setY(getY());
    }
    if (getWidth() != null) {
    stage.setWidth(getWidth());
    } else {
    stage.setWidth(DEFAULT_WIDTH);
    }
    if (getHeight() != null) {
    stage.setHeight(getHeight());
    } else {
    stage.setHeight(DEFAULT_HEIGHT);
    }
    if (getMaximized() != null) {
    stage.setMaximized(getMaximized());
    }
    if (getHidden() == null || !getHidden() || !getHideable()) {
    stage.show();
    }
    new Thread(callback).start();
    });
    }
    public void setHidden(boolean value) {
    this.hidden = value;
    }
    private boolean isWindowIsOutOfBounds(Stage stage) {
    for (Screen screen : Screen.getScreens()) {
    Rectangle2D bounds = screen.getVisualBounds();
    if (stage.getX() + stage.getWidth() - MINIMUM_VISIBLE_WIDTH >= bounds.getMinX() &&
    stage.getX() + MINIMUM_VISIBLE_WIDTH <= bounds.getMaxX() &&
    bounds.getMinY() <= stage.getY() && // We want the title bar to always be visible.
    stage.getY() + MINIMUM_VISIBLE_HEIGHT <= bounds.getMaxY()) {
    return false;
    }
    }
    return true;
    }
    private void moveToPrimaryScreen(Stage stage) {
    Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
    stage.setX(bounds.getMinX() + MARGIN);
    stage.setY(bounds.getMinY() + MARGIN);
    stage.setWidth(DEFAULT_WIDTH);
    stage.setHeight(DEFAULT_HEIGHT);
    }
    private void watchStage(Stage stage) {
    // Get the current values.
    setX(stage.getX());
    setY(stage.getY());
    setWidth(stage.getWidth());
    setHeight(stage.getHeight());
    setMaximized(stage.isMaximized());
    setHidden(!stage.isShowing());
    // Watch for future changes.
    stage.xProperty().addListener((observable, old, x) -> setX((Double) x));
    stage.yProperty().addListener((observable, old, y) -> setY((Double) y));
    stage.widthProperty().addListener((observable, old, width) -> setWidth((Double) width));
    stage.heightProperty().addListener((observable, old, height) -> setHeight((Double) height));
    stage.maximizedProperty().addListener((observable, old, maximized) -> setMaximized(maximized));
    stage.showingProperty().addListener(observable -> setHidden(!stage.isShowing())); // Using an invalidation instead of a change listener due to this weird behaviour: https://stackoverflow.com/questions/50280052/property-not-calling-change-listener-unless-theres-an-invalidation-listener-as
    }
    }

    and the way you use it is quite simple. On your start method, you create or restore an instance of StageSizer and then do this:

    public void start(Stage stage) {
    StageSizer stageSizer = createOrRestoreStageSizerFromConfig();
    stageSizer.setStage(stage);
    }

    I haven’t put a lot of testing on this code yet but it seems to work. Well, at least on Windows. The problem is that this snippet is interacting with the reality of screen sizes, resolutions, adding and removing monitors, etc. If you find a bug, please, let me know and I might release this a library with the fix so we can keep on collectively improving this.f

  • Star Trek DiscoveryMy opinion of Star Trek: Discovery is positive, but there’s still something that annoys me and since it’s a bit of a spoiler, you should stop reading here until you watched season 1.

    Star Trek: Discovery shouldn’t have been a prequel. STD (oops, unfortunate acronym) should have been a sequel to all the other Star Treks we had. I don’t understand why they made it a prequel. It’s not trying to explain an origin story; if anything, it’s destroying Star Trek cannon.

    If it was a sequel, in the 25th century:

    • the uniforms wouldn’t be an issue
    • the introduction of new races wouldn’t be an issue
    • the introduction of a human that went through Vulcan academy wouldn’t be an issue (she could be Spock’s protege, instead of Spock’s father’s protege)
    • the Klingons looking different wouldn’t be an issue
    • flat screens and holograms wouldn’t be an issue
    • the use of a sort of holodeck wouldn’t be an issue
    • discovering a way to teleport through the galaxy without needing warp drives wouldn’t be an issue
    • we could have Star Trek: The Next Generation, Voyager and Deep Space 9 cameos.

    The BorgWhy make it a prequel then? There’s no advantage to having it be a prequel. You could still have a war with the Klingons if they wanted to bank on their fame (although a war with the Borg is much more frightening in my opinion, specially since peace with the Borg is impossible).

    They couldn’t have the flip phones, I mean, the communicators, which apparently are iconic enough to put on one of the posters, but aren’t the badge communicators also iconic? And if not, it feels like a small lose.

    I don’t understand this obsession with needles prequels, are people afraid of the future? of moving forward and seeing what happens next?