Scintilla v3.7.3 + MFC on Windows

67 views
Skip to first unread message

Stephane Charette

unread,
Apr 12, 2017, 7:23:35 PM4/12/17
to scintilla-interest
Working on a legacy Windows MFC projet. Replacing one editor with Scintilla.

I have Scintilla 3.7.3, which we're statically linking into our app. Took me a while to find that I have to call Scintilla_RegisterClasses() to get things working!

We have a window that inherits from CWnd. From that window, we call this to create the Scintilla child:

const CRect r;
const UINT id = 10000;
const DWORD exStyle = WS_EX_CLIENTEDGE;
const DWORD style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPCHILDREN;
const BOOL ok = CWnd::CreateEx(exStyle, L"Scintilla", L"", style, r, this, id);

While this seems to be mostly working, before I get too far I wanted to see if this is the right way to do things with Scintilla, or if there is something obvious I missed.

Stéphane Charette

Greg Smith

unread,
Apr 13, 2017, 12:06:03 PM4/13/17
to scintilla-interest
We use Scintilla in MFC (why does everyone always talk about legacy projects when mentioning MFC). We have the Scintilla code in a DLL and then wrap Scintilla in class:

class DllClass CSciCtrl : public CWnd
{
    DECLARE_DYNAMIC(CSciCtrl);
private:
static SciFnDirect m_pSciDirect;
static HMODULE m_hModScintilla;
HWND m_hWndScintilla;
sptr_t m_pSciData;
sptr_t SciCall(unsigned int iMessage, uptr_t wParam=0, sptr_t lParam=0) const
    {
        return m_pSciData ? m_pSciDirect(m_pSciData, iMessage, wParam, lParam) : 0;
    }
    sptr_t SciCallCatch(unsigned int iMessage, uptr_t wParam=0, sptr_t lParam=0);
    int m_nLineNumberDigits;    // number of digits in line number

// Construction
public:
CSciCtrl();

// Attributes
public:

// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSciCtrl)
public:
//}}AFX_VIRTUAL
    virtual BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwExStyle = 0, LPVOID lpParam = nullptr);

// Implementation (non-autogenerated functions)
public:
virtual ~CSciCtrl();
    static bool InitScintilla(LPCTSTR szPath = nullptr);
    static void UnloadScintilla();

... lots of methods for our use, then all the scintilla messages converted to calls...
};

The create call is:

// We create our scintilla control and save the handle in the standard window handle.
// This is basically the CreateEx call with fixed args omitted and rearranged so we
// can omit commonly unused arguments.
BOOL CSciCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID,
                                          DWORD dwExStyle, LPVOID lpParam) 
{
    if (!m_hModScintilla &&                                 // has user failed to initialise?
        !InitScintilla())                                   // try default locations
        return FALSE;
BOOL bOK = CreateEx(dwExStyle, _T("Scintilla"), NULL, dwStyle, rect, pParentWnd, nID, lpParam);
    if (bOK)            // if all was OK we can now send messages to the window
    {
        ASSERT(::IsWindow(m_hWnd));                         // moan if we failed
        m_pSciDirect = (SciFnDirect)SendMessage(SCI_GETDIRECTFUNCTION);  // get address to use
        m_pSciData = SendMessage(SCI_GETDIRECTPOINTER);     // get our instance data pointer

#ifdef _UNICODE
        // Although it is tempting to run in UTF8 mode, there will be folks out there
        // who depend on MBCS so that toolbars and buttons work. Until we enable UNICODE, if
        // we run in UTF8 mode, things are OK for scripts and text written to text views, but not
        // OK for any text that is displayed. So defer this until full UNICODE support.
        // GPS, Aug 2014.
        SetCodePage(SC_CP_UTF8);                            // Set UTF8 mode
#endif
        // Direct Write is a better text technology that is available in Vista and later. BUT...
        // Screen updates happen during frame flyback so if you are forcing updates in a script, the
        // updates will occur more slowly. There are race condition problems when dragging a
        // text view over another, resulting in overwrites. Use SC_TECHNOLOGY_DIRECTWRITEDC
        // to fix this.
#ifdef SCI_USED2D
        if (AfxGetMainWnd()->SendMessage(msgPrefOptions, ePO_UseD2D, -1))
#ifdef MFC_D2DSUPPORT
            EnableD2DSupport();
        if (IsD2DSupportEnabled())
#endif
            SetTechnology(SC_TECHNOLOGY_DIRECTWRITEDC);     // attempt to set direct write
        if (GetTechnology() != SC_TECHNOLOGY_DEFAULT)       // if using DirectWrite
        {
            SetBufferedDraw(false);                         // probably right
            SetTwoPhaseDraw(false);                         // remove if screen corruption
        }
#endif

        SetFontQuality(SC_EFF_QUALITY_LCD_OPTIMIZED);       // and best quality font

        SetMarginWidthN(SCMARGIN_MARKER, ScaleDPIx(16));    // Make sure margin is correct size
    }
    return bOK;
}

This looks very similar to what you are doing. Despite the comments, we now run all our applications that use this with _UNICODE defined and take care of the conversion between MFC CString wide characters and the UTF-8 used in scintilla in the interface methods of the class.

We have used Scintilla in this way for many years and have had very few problems. We did struggle with D2D. For this code to work well we have SCI_USED2D defined and MFC_D2DSUPPORT is not defined (or do not define SCI_USED2D.

To use this in a view, we have code like:

int CTextView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CMsgView::OnCreate(lpCreateStruct) == -1)
return -1;

    // Note that you MUST not attempt to use m_Edit until Create has been
    // called successfully.
    if (!m_Edit.Create(WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE|WS_TABSTOP, CRect(0, 0, 100, 100), this, 0))
        return -1;
...
}

We also use Scintilla controls in dialogs and so on with very similar code.

Best,

Greg Smith

Stephane Charette

unread,
Apr 13, 2017, 2:45:18 PM4/13/17
to scintilla-interest
We have a window that inherits from CWnd.  From that window, we call this to create the Scintilla child:

  const CRect r;
  const UINT  id      = 10000;
  const DWORD exStyle = WS_EX_CLIENTEDGE;
  const DWORD style   = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPCHILDREN;
  const BOOL  ok      = CWnd::CreateEx(exStyle, L"Scintilla", L"", style, r, this, id);

While this seems to be mostly working, before I get too far I wanted to see if this is the right way to do things with Scintilla, or if there is something obvious I missed.


In case someone's google search leads to this post in the future, remove WS_CLIPCHILDREN.  Part of my drawing issues was due to this flag.

Stéphane

Neil Hodgson

unread,
Apr 13, 2017, 7:31:47 PM4/13/17
to scintilla...@googlegroups.com
Greg Smith:

> SetBufferedDraw(false); // probably right
> SetTwoPhaseDraw(false); // remove if screen corruption

Single phase drawing is not used often so does not receive as much testing. There were several single phase bugs fixed in 3.7.4 that went unnoticed for some time. I recommend that all applications use SC_PHASES_TWO or SC_PHASES_MULTIPLE.

Neil

Reply all
Reply to author
Forward
0 new messages