Blog
Scribe: Installer script hooks
Date: 14/7/2016
Tags: scribe scripting
So you know those red bars that appear in Scribe when it wants to install something? I've just added the spell check dictionaries to that so you have to confirm there install. This allows you to make sure you have a working internet connection before download a dictionary. But I thought it would be cool to add some scripting hooks for the install bar and the actual install itself. So take an example script like this:
function BeforeInstall(App, Msg, Actions)
{
    Actions.Add("Scare");
    Msg = Msg + " (BeforeInstall was here)";
    return true;
}

function Install(App, Action)
{
    if (Action == "Scare")
    {
        MsgBox(App, "Boo!");
        return false;
    }
    
    return true;
}

function Main(App)
{
    if (!AddCallback("OnBeforeInstallBar", "BeforeInstall"))
        MsgBox(App, "Couldn't add BeforeInstall");
    if (!AddCallback("OnInstallComponent", "Install"))
        MsgBox(App, "Couldn't add Install");

    return 1;
}
What it does is install 2 callbacks, one for the missing capability bar, and one for the install itself. It messes with the message and buttons available to the install bar. Then adds a new "action" that is caught later in the "Install" function to put up a dialog box. Might be useful for managed installs of Scribe.
(1) Comment | Add Comment

The week of Mac bugs
Date: 7/7/2016
Tags: mac carbon
This week has been mostly about making the Mac build of Scribe more polished. It's always lagged behind the Windows build. And now it's catching up fast:

1) HIViewSetFrame inside kEventControlDraw is bad

For many years I couldn't work out why some calls to HIViewSetFrame wouldn't work inside some application I was writing. It turns out that called that inside the kEventControlDraw callback is actually bad. It leaves lots of the controls half painted of showing the previous contents of the screen. And no amount of calling HIViewSetNeedsDisplay is going fix it. The compositor thinks the old position of the control is up to date and won't repaint it.

Ultimately if you find you need to move a control during the re-paint you have to send a message to yourself and do it after that particular repaint has finished.

2) Apple Event handling

After 4 years I finally found the reason why Lgi apps wouldn't respond to AppleEvents that were passed to them on startup. I answered my own stack overflow question today.

3) Editing the Application Menu

The Scribe Application menu, that sits between the Apple menu and File menu has never had the normal "About" and "Preferences" items. And while there is a lot of discussion on how to do that for Cocoa apps, there is little to no information for Carbon apps. And yes, Scribe is still a Carbon app, although it does use a bunch of modern APIs for text and so on. I tried mixing some Cocoa calls to get at the right menu, but that didn't work. Then after hours of searching the internet I finally stumbled on to a mailing list post that describes how to do it. So it seems it's possible, just really obscure.
(0) Comments | Add Comment

Drawing windows BUTTONs on coloured backgrounds
Date: 4/7/2016
Tags: windows
The default windows BUTTON drawing behaviour draws a 1px grey border around buttons. If you place the button on a parent window that is a different colour you get an ugly border like this:



So I decided to see what I could do about that. It turns out that there is no "setting" that can fix that. You either draw the whole control yourself or let the system draw it. Now I want the system look without the wrong coloured border. So I reimplemented the WM_PAINT like so:
case WM_PAINT:
{
    if (!GetCss())
        break;

    // This is the effective background of the parent window:
    GCss::ColorDef bk = GetCss()->NoPaintColor();

    // If it's not the default...
    if (!bk.IsValid())
        break;

    // Then create a screen device context for painting
    GScreenDC dc(this);
    
    // Get the control to draw itself into a bitmap
    GRect c = GetPos();
    
    // Create a HBITMAP in the same size as the control 
    // and the same bit depth as the screen
    GMemDC m(c.X(), c.Y(), GdcD->GetColourSpace());
    // Create a HDC for the bitmap
    HDC hdc = m.StartDC();					
    // Ask the control to draw itself into the memory bitmap
    SendMessage(_View, WM_PRINT, (WPARAM)hdc, PRF_ERASEBKGND|PRF_CLIENT);
    // End the HDC
    m.EndDC();

    // Draw correct background
    m.Colour(bk.Rgb32, 32);
    // The outside 1px border (unfilled rect)
    m.Box(0, 0, c.X()-1, c.Y()-1);
    // The 4 pixels at the corners
    m.Set(1, 1);
    m.Set(c.X()-2, 1);
    m.Set(1, c.Y()-2);
    m.Set(c.X()-2, c.Y()-2);

    // Now stick it on the screen
    dc.Blt(0, 0, &m);

    // Skip over calling the parent class' callback procedure.
    return true;
    break;
}
What it does is get the system to draw the button into a memory bitmap and then "fix" the border, before blting the whole thing to the screen. Now it's implemented with LGI types and classes, but the whole idea is fairly straight forward that you could implement in raw Win32 API calls. This fixes the border and still looks native:



The key is re-purposing the WM_PRINT message to get a copy of the control's native look. For the moment I've limited this to just when running on Windows 7, because I think Windows 8 and 10 use flat colour controls and [possibly] don't need fixing like this.
(0) Comments | Add Comment