Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use custom folding icons in QScintilla?

Consider this snippet:

import sys

from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import *

if __name__ == '__main__':
    app = QApplication(sys.argv)
    view = QsciScintilla()

    # http://www.scintilla.org/ScintillaDoc.html#Folding
    view.setFolding(QsciScintilla.BoxedTreeFoldStyle)
    # view.setFolding(QsciScintilla.BoxedFoldStyle)
    # view.setFolding(QsciScintilla.CircledFoldStyle)
    # view.setFolding(QsciScintilla.CircledTreeFoldStyle)
    # view.setFolding(QsciScintilla.NoFoldStyle)
    # view.setFolding(QsciScintilla.PlainFoldStyle)

    lines = [
        (0, "def foo():"),
        (1, "    x = 10"),
        (1, "    y = 20"),
        (1, "    return x+y"),
        (-1, ""),
        (0, "def bar(x):"),
        (1, "    if x > 0:"),
        (2, "        print('this is')"),
        (2, "        print('branch1')"),
        (1, "    else:"),
        (2, "        print('and this')"),
        (2, "        print('is branch2')"),
        (-1, ""),
        (-1, ""),
        (-1, ""),
        (-1, "print('end')"),

    ]

    view.setText("\n".join([b for a, b in lines]))
    MASK = QsciScintilla.SC_FOLDLEVELNUMBERMASK

    for i, tpl in enumerate(lines):
        level, line = tpl
        if level >= 0:
            view.SendScintilla(view.SCI_SETFOLDLEVEL, i, level | QsciScintilla.SC_FOLDLEVELHEADERFLAG)
        else:
            view.SendScintilla(view.SCI_SETFOLDLEVEL, i, 0)

    view.show()
    app.exec_()

I'd like to figure out whether it's possible to change the folding icon to a custom icon different than the ones offered by QScintilla, specifically I'd like to have a down arrow similar to Sublime's:

enter image description here

By looking at the QSciScintilla foldstyle you can see there isn't anything that remains similar.

In fact, not only that, I was also wondering if it'd be possible to achieve this nice subtle effect of fading away the folding icon when the mouse position enters/leaves the margin area, take a look:

enter image description here

Which it's a really nice feature, that way while you're coding you won't get distracted by the "always visible" folding icons.

In this thread it seems there is something called SC_MARK_ARROWDOWN but not sure if that could be used as a folding icon... in any case, i'd still prefer my custom picture as I'll be using a monokai theme and I'd like the icon to look as elegant as Sublime's.

Below you'll find 2 12x12 pngs I've created to represent the dark & light arrowdowns.

enter image description here enter image description here

like image 592
BPL Avatar asked Apr 18 '19 12:04

BPL


People also ask

How does Scintilla work on Windows?

The Windows version of Scintilla is a Windows Control. As such, its primary programming interface is through Windows messages. Early versions of Scintilla emulated much of the API defined by the standard Windows Edit and RichEdit controls but those APIs are now deprecated in favour of Scintilla's own, more consistent API.

How to install QScintilla on PyPI?

The GPL version of QScintilla can be installed from PyPI: The wheels include a statically linked copy of the QScintilla C++ library. pip will also build and install the bindings from the sdist package but Qt’s qmake tool must be on PATH.

How is Scintilla different to normal edit control?

In addition to messages performing the actions of a normal Edit control, Scintilla allows control of syntax styling, folding, markers, autocompletion and call tips. The GTK version also uses messages in a similar way to the Windows version. This is different to normal GTK practice but made it easier to implement rapidly.

How to create custom app icons in flutter?

In Flutter, it’s not that complicated, but there are some things you should be aware of so that you don’t make time-consuming mistakes. Instead of using the generic application icon Flutter provides, you can create your own. To do that, we will need to use a package called Flutter Launcher Icons.


1 Answers

Preliminary version that lacks hovered marker highlighting:

from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import *

def get_arrow(down, size, cmin, cmax):
    from PIL import Image
    from PIL.ImageQt import ImageQt
    from math import atan2, cos, pi
    pict = Image.new("RGBA", (size, size), tuple(cmin + [255]))
    pixl = pict.load()
    step = 3.25**2
    for y in range(pict.size[1]):
        for x in range(pict.size[0]):
            xpos = x - pict.size[0] // 2 + 1
            ypos = y - pict.size[1] // 2 + 1
            retn = 1.5 * atan2(ypos, xpos) + (pi / 4, 0)[not down]
            retn = ((abs(cos(retn))**2.5 + 3) * 2)**2 - xpos**2 - ypos**2
            if (retn < 0):
                retn = cmin
            elif (retn >= step):
                retn = cmax
            else:
                retn = [int((cmax[i] - cmin[i]) * retn / step + cmin[i])
                        for i in range(0, 3)]
            pixl[x, y] = tuple(retn + [255])
    return ImageQt(pict)

def color_to_sc(c):
    return (c[0] & 0xFF) | ((c[1] & 0xFF) << 8) | ((c[2] & 0xFF) << 16)

def set_fold(prev, line, fold, full):
    if (prev[0] >= 0):
        fmax = max(fold, prev[1])
        for iter in range(prev[0], line + 1):
            view.SendScintilla(view.SCI_SETFOLDLEVEL, iter,
                fmax | (0, view.SC_FOLDLEVELHEADERFLAG)[iter + 1 < full])

def line_empty(line):
    return view.SendScintilla(view.SCI_GETLINEENDPOSITION, line) \
        <= view.SendScintilla(view.SCI_GETLINEINDENTPOSITION, line)

def modify(position, modificationType, text, length, linesAdded,
           line, foldLevelNow, foldLevelPrev, token, annotationLinesAdded):
    full = view.SC_MOD_INSERTTEXT | view.SC_MOD_DELETETEXT
    if (~modificationType & full == full):
        return
    prev = [-1, 0]
    full = view.SendScintilla(view.SCI_GETLINECOUNT)
    lbgn = view.SendScintilla(view.SCI_LINEFROMPOSITION, position)
    lend = view.SendScintilla(view.SCI_LINEFROMPOSITION, position + length)
    for iter in range(max(lbgn - 1, 0), -1, -1):
        if ((iter == 0) or not line_empty(iter)):
            lbgn = iter
            break
    for iter in range(min(lend + 1, full), full + 1):
        if ((iter == full) or not line_empty(iter)):
            lend = min(iter + 1, full)
            break
    for iter in range(lbgn, lend):
        if (line_empty(iter)):
            if (prev[0] == -1):
                prev[0] = iter
        else:
            fold = view.SendScintilla(view.SCI_GETLINEINDENTATION, iter)
            fold //= view.SendScintilla(view.SCI_GETTABWIDTH)
            set_fold(prev, iter - 1, fold, full)
            set_fold([iter, fold], iter, fold, full)
            prev = [-1, fold]
    set_fold(prev, lend - 1, 0, full)

def hover(position, xpos, ypos):
    mask = view.SendScintilla(view.SCI_GETMARGINMASKN, 2)
    mask = (mask | view.SC_MASK_FOLDERS, mask & ~view.SC_MASK_FOLDERS) \
           [xpos > StandardMarginWidth]
    view.SendScintilla(view.SCI_SETMARGINMASKN, 2, mask)

if __name__ == '__main__':
    import sys
    import textwrap

    app = QApplication(sys.argv)
    view = QsciScintilla()
    view.SendScintilla(view.SCI_SETMULTIPLESELECTION, True)
    view.SendScintilla(view.SCI_SETMULTIPASTE, 1)
    view.SendScintilla(view.SCI_SETADDITIONALSELECTIONTYPING, True)
    view.SendScintilla(view.SCI_SETINDENTATIONGUIDES, view.SC_IV_REAL);
    view.SendScintilla(view.SCI_SETTABWIDTH, 4)

    view.setFolding(view.BoxedFoldStyle)

    StandardIconSize = 16
    StandardMarginWidth = 20
    StandardBackground = [64, 64, 64]
    StandardForeground = [192, 192, 192] # [R, G, B]

    view.SendScintilla(view.SCI_SETMARGINWIDTHN, 1, 0)
    view.SendScintilla(view.SCI_SETMARGINWIDTHN, 2, StandardMarginWidth)

    view.SendScintilla(view.SCI_STYLESETBACK, view.STYLE_LINENUMBER, color_to_sc(StandardBackground))
    view.SendScintilla(view.SCI_SETFOLDMARGINHICOLOUR, True, color_to_sc(StandardBackground))
    view.SendScintilla(view.SCI_SETFOLDMARGINCOLOUR, True, color_to_sc(StandardBackground))

    view.SendScintilla(view.SCI_RGBAIMAGESETHEIGHT, StandardIconSize)
    view.SendScintilla(view.SCI_RGBAIMAGESETWIDTH, StandardIconSize)
    fldr = get_arrow(0, StandardIconSize, StandardBackground, StandardForeground)
    open = get_arrow(1, StandardIconSize, StandardBackground, StandardForeground)
    view.SendScintilla(view.SCI_MARKERDEFINERGBAIMAGE, view.SC_MARKNUM_FOLDER, fldr)
    view.SendScintilla(view.SCI_MARKERDEFINERGBAIMAGE, view.SC_MARKNUM_FOLDEROPEN, open)

    view.SendScintilla(view.SCI_SETMOUSEDWELLTIME, 50)
    view.SCN_DWELLSTART.connect(hover)
    view.SCN_DWELLEND.connect(hover)

    view.SCN_MODIFIED.connect(modify)

    NUM_CHUNKS = 1
    chunk = textwrap.dedent("""\
        def foo():
            x = 10
            y = 20
            return x+y

        def bar(x):
            if x > 0:
                print('this is')
                print('branch1')
            else:
                print('and this')
                print('is branch2')

        print('end')
    """)
    view.setText("\n".join([chunk for i in range(NUM_CHUNKS)]))
    view.show()
    app.exec_()
like image 84
hidefromkgb Avatar answered Oct 08 '22 08:10

hidefromkgb