Inheriting from GtkSourceView and implementing automatic file opening

Hello,

I would like to create a custom widget that inherits from GtkSourceView and implements automatic file opening / scrolling. I.e. it will have 2 modes: (a) Edit and (b) View. As the names suggest in the Edit mode it will be possible to edit the source file, and in the View - only view it. Let’s say that for now a source file is just a list of file paths - one per line. That’s what is going to be visible/editable in the Edit mode. Once switched to the View mode - it will hide the source file content with the file paths and instead open and display the content of all those files and append them in the order that the paths appear in the source file. Of course in order not to overload the memory - only what is visible should be added to the View. I.e. once we scroll down and reach the end of the currently displayed file (N) - append the content of the file that appears next in the source (N+1). And in the same time unload/delete from the widget’s memory the content of file (N-1). Some sort of automatic scrolling/opening.

I’m new to Gobject / Gtk and right now go through the Gobject tutorials on YouTube. So far I managed to add relevant .h/.c files for my widget into the current GtkSourceView sources, make “empty” inheritance from GtkSourceView, replace it in the test-widget.c with my new widget and to compile the code.

My question - could you, please, provide me with some sort of road map to achieve my goal - what should I (as newbie) do within the new widget to implement that functionality? At least a direction with some points to pay attention to…

Thank you!

Hi,

Hmm… that sounds quite complex…

Instead of using the same SourceView for both “edit” and “view” modes, I would suggest to use 2 different ones, and switch between them e.g. with a GtkStack or similar.

A few questions:

  • why GtkSourceView instead of GtkTextView? do you need a specific feature like syntax highlighting or showing lines numbers?
  • what should happen if files content are too small for scrolling? will it automatically load the next ones? if yes you may never see some contents…

Regarding the implementation of the “view” mode, it may be possible to use gtk_source_file_loader_new_from_stream() and feed the GInputStream with the content of the files, one by one. But needs some testing.

1 Like

Note that if files are really big, it would be interesting to use memory-mapped files, but AFAIK gtksourceview doesn’t support that, it works with the whole files in memory.

1 Like

Thank you for your response and insights. Very appreciated! As I’m new to it and prefer to avoid mistakes of newbies… especially in design/architecture…

  1. Yes. I need syntax highlighting.
  2. Why?.. Maybe I have not formulated my intention correctly. My custom widget will not scroll down automatically but rather open the next file automatically and enable scrolling (without actually scrolling). Similar to the “infinite scrolling” on websites… once you scroll down to the end it opens new content (in my case - new file… till you reach the end of the paths list).

ah, OK, I see.
And if you scroll upwards, will it reopen/reload the old files that were previously closed/removed from the buffer?

If it’s just continuously appending new content file by file, then probably gtk_source_file_loader_new_from_stream() will be suitable, as said above. It allows for example to read continuously from the stdin, so similar usecase. But if there is some content removing is involved, or, worse, content prepending at the start when navigating upwards, then that will be much more complex, and will need some complex tracking of what and how much has been inserted.

1 Like

What you want to do is basically pagination. Unfortunately neither GtkSourceView nor GtkTextView do this by default. It would be necessary to write a new widget that had this capability. I recently faced this problem myself when trying to write a section-based text editing app.

As I don’t have the technical ability to write an entirely new widget, what I did was use a GtkLisView and load several individual GtkTextView as rows. Due to the nature of the GtkLisView, I can filter and order the GtkTextView, and GtkTextView are only created for what is visible, while the non-visible content is only in the GtkTextBuffers.

There are still many visualization bugs and as a result you lose the ability to select the entire text, in addition to having to write a function yourself to move the cursor between the different GtkTextViews. But for me it was either that or abandon the project, so I’m satisfied.

1 Like

I have replaced GtkSourceView with my new widget and for the beginning wanted it to enable Markdown highlighting by default on the start however when running the test-widget binary - Markdown is not enabled (despite the fact that I’ve verified that gtk_dar_view_init is executed). What do I miss here?

Thank you!

I think I should remove the N-1 file in order to stay efficient. And then prepend it back if the user scrolls upwards…

Maybe I’ll add more details in order to get more precise implementation advices. What I actually try to do is to implement a custom widget for my syntax. One of its elements is file inclusion, so if we have a source file like:

Some text
+</home/user/fileA.txt>
…more text between the files…
+<./fileB.txt>
And text at the end…

→ in the View mode content of files fileA.txt and fileB.txt should be displayed in place of corresponding +<...>. The source file may consist of many such inclusions so I want to implement this properly showing only what’s necessary. What should be the best approach to implement this? Maybe I should somehow rely on the GtkSource’s syntax parser to implement the View mode? Maybe write a callback function that is called once +<...> is detected by the parser and then decide whether to expand it to its content in case it is currently visible? (but on the other hand the source file needs to be re-parsed each time there is scrolling… so doesn’t sound efficient)…

Which design/already available functions would you recommend in such a case?

	GtkTextView *view = GTK_TEXT_VIEW (self);
	GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
	GtkSourceLanguageManager *manager = gtk_source_language_manager_get_default ();
	GtkSourceLanguage *language = gtk_source_language_manager_get_language (manager, "markdown");
	gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer), language);

You created a simple GtkTextView here, instead of a GtkSourceView.
Thus buffer is not a GtkSourceBuffer but a GtkTextBuffer, which does not support set_language.

1 Like

I personally wouldn’t use GtkSourceView at all for this kind of stuff…
For displaying a markdown-like rendering, better convert it to HTLM and display it with GtkWebkit, or similar.

There are some apps for markdown, maybe have a look how they do it:
https://flathub.org/apps/com.github.fabiocolacio.marker
https://flathub.org/apps/me.mitya57.ReText
https://flathub.org/apps/org.gnome.gitlab.somas.Apostrophe

I’m not sure I want to convert it to HTML and GtkWebkit is not that light-weight (which I prefer). Either way regular markdown doesn’t support file inclusion, so I will not find an answer there…

If I were you, I would completely forget about this load/unload on scroll (too painful to implement with gtksourceview).

Instead, load the whole content at once:

  • create a GInputStream
  • initiate the loading with gtk_source_file_loader_new_from_stream()
  • open the master file (typically from GFileInputStream + GDataInputStream), read line by line
    • if there is a file inclusion then open that other file and process it the same way
    • else just feed the line to the GInputStream

Whatever you do, you will need some parsing at some point. Because gtksourceview works on whole files for highlighting, it’s probably better to load everything as described above, instead of trying to paginate.

I changed it to these, but there is still no highlighting:

	GtkSourceView *view = GTK_SOURCE_VIEW (self);
	GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
	GtkSourceLanguageManager *manager = gtk_source_language_manager_get_default ();
	GtkSourceLanguage *language = gtk_source_language_manager_get_language (manager, "markdown");
	gtk_source_buffer_set_language (buffer, language);
	gtk_source_buffer_set_highlight_syntax (buffer, TRUE);

My target documents are whole books. Sometimes very big books. So I kind of hesitate to rely on full upload of all inclusions at once… I’m afraid application will be slow/unusable… So I’m looking for an efficient and elegant solution… the right way. Even if I’ll need to learn/code more… I don’t mind to dive into the way GtkSourceView’s parsing works and extend it if needed. But I admit that I lack the fundamental knowledge of the widget (and the Gobject/GTK in general) right now so I’m not capable of making design decisions…

I now see that all those apps convert markdown to HTML. Two out of three use webkitgtk. I also see that Gnome Builder use it. So it seems to be the way to go… However I need my app to be cross-platform and work also on Windows… Does webkitgtk builds for Windows?.. And if not - is there any cross-platform alternative to it?

Oh, looks like you’re recreating a new view, instead of inheriting the one from the parent’s class.

Should look more like this:

static void
gtk_dar_view_init (GtkDarView *view)
{
	GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
	GtkSourceLanguageManager *manager = gtk_source_language_manager_get_default ();
	GtkSourceLanguage *language = gtk_source_language_manager_get_language (manager, "markdown");
	gtk_source_buffer_set_language (buffer, language);
	gtk_source_buffer_set_highlight_syntax (buffer, TRUE);
}

(disclaimer: I’m mostly a python developer, not used to play with GObject in C)

Thanks for the description, I understand better your needs now.

I was thinking that it may be possible to rely on GtkListView’s auto-recycling to load on-demand the chunks to be rendered, and automatically dispose them on scrolling.

How does that “dar” markup looks like? do you have some real-life examples?
Do you think it could be easy to cut the document in small portions we could pack in a GtkListView?

No, it used to be, but they stopped supporting Windows.

Sure, here is the informal dar syntax definition and here is dar’s real-life usage. Now I actually started to like your suggestion to use HTML conversion and WebKitGTK, since I’ll be able to create really nice and advanced output in this way. As for cross-platform problem - I’ve found this - webview. So now I need to think how to implement file inclusion and subsequent up-/downscrolling using that approach. My intuition suggest that WebKit must have solved the problem of displaying very long (pseudo-“infinite”) HTML documents.

Hmm… still doesn’t work. It shows “Language: none” on the bottom of the test-widget. And also markdown highlighting doesn’t work.

Are you sure that GtkSourceView *view = GTK_SOURCE_VIEW (self); creates a new view? I thought it just casts the existing GTK_DAR_VIEW back int GTK_SOURCE_VIEW… no new object created here. Or am I wrong?