Skip to content

draft: Migrate Dissent to UI files#372

Draft
DodoLeDev wants to merge 9 commits into
diamondburned:mainfrom
DodoLeDev:migrate-to-uifiles
Draft

draft: Migrate Dissent to UI files#372
DodoLeDev wants to merge 9 commits into
diamondburned:mainfrom
DodoLeDev:migrate-to-uifiles

Conversation

@DodoLeDev

Copy link
Copy Markdown

Hi everyone!
It's been a long time, but I resumed my work on porting the app to Gtk.Builder

This would bring various advantages, especially for design: The static interface organization (pieces of the UI that will not be dynamically generated) can be crafted and edited through a UI designer app: Cambalache

But what it means, technically?

It means that the code has been severely simplified! Just look at the login component file

The way to use UI elements has changed, too:

Instead of manually creating elements:

// ...
item := gtk.NewLabel("Label")
item.SetXAlign(0)
// ...
revealer := gtk.NewRevealer()
revealer.SetTransitionType(gtk.RevealerTransitionTypeSlideDown)
revealer.SetRevealChild(false)
// ...
button = gtk.NewButtonWithLabel("Button")
button.AddCSSClass("suggested-action")
button.AddCSSClass("css-button")
button.SetHExpand(true)

You'll do this instead:

  1. You design the architecture with Cambalache
image

Tip

If you've just created the file, you'd need to add it to the gresource index (/uifiles/uifiles.gresource.xml):

<?xml version='1.0' encoding='UTF-8'?>
<gresources>
 <gresource prefix="/so/libdb/dissent">
   ...
   <file preprocess="xml-stripblanks">your_new_file.ui</file>
 </gresource>
</gresources>
  1. You assign names to objects that need to be accessed through the program logic
image

Tip

Conventionnally, the root object is named rootContent

  1. You create the UiFile object, and access its objects:
import (
    ...
    // Import the required module
    "libdb.so/dissent/internal/gresources"
)

// Create the UiFile object
uiFile := gresources.New("your_new_file.ui")

// Get the root object (you'll need to cast it to the expected gtk.Widget)
rootObject := uiFile.GetRoot().(*adw.ToolbarView)

// Get a specific object by its name
namedObject := uiFile.GetComponent("ProfilePicture").(*gtk.Image)

// Edit it
namedObject.SetFile("/another/custom/path.jpg")

What's also included in this PR

As a PoC, I have migrated and redesigned the login page, to make it more GNOME-ish (opinionated choice, I know... 😬)

Capture.video.du.2025-11-16.16-10-16.mp4

What do you think?

@DodoLeDev

Copy link
Copy Markdown
Author

Some notes

The adw.ToggleGroup, used in the login page has landed in Libadwaita 1.7, which is not usable through the gotk4-adwaita go package (yet).

For now, the compilation is failing because I compile the 1.7 branch of this package locally during build

Comment on lines +45 to +56

func LoadStyles(cssFile string) error {
display := gdk.DisplayGetDefault()
if display == nil {
return errors.New("Unable to apply " + cssFile + " : Could not get default display")
}
provider := gtk.NewCSSProvider()
provider.LoadFromResource(resourcePath + "/" + cssFile)

gtk.StyleContextAddProviderForDisplay(display, provider, uint(gtk.STYLE_PROVIDER_PRIORITY_APPLICATION))
return nil
}

@DodoLeDev DodoLeDev Nov 16, 2025

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the gresources package could be moved to your CSSUtil subpackage, with the help of GoTKit!3 (for the GResource support)

Comment thread uifiles/styles.css
Comment on lines +6 to +7
/* dimensions.go */
/* Use of CSS Variables makes migration impossible */

@DodoLeDev DodoLeDev Nov 16, 2025

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This CSS fragment cannot be moved to styles.css because of CSS Templating (from cssutil), until GoTKit!3 (for the CSS Template support that you provide) got merged

@diamondburned

Copy link
Copy Markdown
Owner

This so far looks great to me; thank you for the work!

Dissent utilizes onlineimage a lot which has a lot of helpers for fetching images from HTTPS URLs asynchronously with graceful error handling. Do you know how well this will play with the XML UI files? Since we can't use subclassing, this may complicate the Builder logic quite a bit.

This goes for the embed viewer too, especially when GTK's video player is broken for streaming files (and therefore downloading in general) and doesn't implement GIF rendering at all. Could we maybe look to migrate some of these to #356 or some other component libraries as well?

@diamondburned

Copy link
Copy Markdown
Owner

Also the new UI designs for the login page and the profile viewer look awesome! I've been meaning to do exactly that myself, so this is perfect.

@DodoLeDev

Copy link
Copy Markdown
Author

Dissent utilizes onlineimage a lot which has a lot of helpers for fetching images from HTTPS URLs asynchronously with graceful error handling. Do you know how well this will play with the XML UI files?

The UI files are used to simplify the UI building where it can be simplified. If it adds up complexity, you can just leave a blank space, assign an id to the parent element and add the missing part from the code 😃

@DodoLeDev

Copy link
Copy Markdown
Author

Also the new UI designs for the login page and the profile viewer look awesome! I've been meaning to do exactly that myself, so this is perfect.

More screens 🙃

When there is a Network problem (commit) Using the integrated encryption method (commit)
image image

@diamondburned

Copy link
Copy Markdown
Owner

Dissent utilizes onlineimage a lot which has a lot of helpers for fetching images from HTTPS URLs asynchronously with graceful error handling. Do you know how well this will play with the XML UI files?

The UI files are used to simplify the UI building where it can be simplified. If it adds up complexity, you can just leave a blank space, assign an id to the parent element and add the missing part from the code 😃

Right, this is mostly about this PR and how it's going to handle onlineimage components, since I noticed it primarily uses gtk.Images.

If the PR keeps the onlineimage and embed components as it is right now, then I'm happy with it.

@DodoLeDev

Copy link
Copy Markdown
Author

Okay the Quick Switcher has been revamped!

It now features the guilds in an horizontal line!

Search with servers Idle screen Direct messages
image image image

⚠️ Note that this guild array is more difficult to reach with keyboard than the channels/DM list

75% of the time I've spent on this work was about keeping the keyboard accessibility as it was, so it should be as easy to use as before.

However I had a hard time trying to understand the quickswitcher code, so this commit is still a bit messy...

Comment on lines +16 to +37
// TODO : ABSOLUTE MESS TO REFACTOR
type channelIndexItem interface {
String() string
ChannelID() discord.ChannelID
Row(context.Context) *gtk.ListBoxRow
}
type guildIndexItem interface {
String() string
GuildID() discord.GuildID
QSItem(context.Context) *gtk.Button
}

type indexItems []indexItem
type guildIndexItems []guildIndexItem
type channelIndexItems []channelIndexItem

func (its guildIndexItems) String(i int) string { return its[i].String() }
func (its guildIndexItems) Len() int { return len(its) }

func (its channelIndexItems) String(i int) string { return its[i].String() }
func (its channelIndexItems) Len() int { return len(its) }

func (its indexItems) String(i int) string { return its[i].String() }
func (its indexItems) Len() int { return len(its) }
// ========================

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the code can be improved I guess...

we may want to fill it with cool things, right?)
@DodoLeDev

Copy link
Copy Markdown
Author

For the craziest of you, guys, here's a nightly build:
dissent_nighly_202511182.zip

It has been compressed twice in order to contains more than 100Mb in only 25Mb (the actual limit of GitHub)

@DodoLeDev

DodoLeDev commented Jan 3, 2026

Copy link
Copy Markdown
Author

New feature coming in this MR: Tabs overview

Useful for mobile navigation and streamlines with what have been seen in other libadwaita apps

image

Some adjustments for mobile users:

image
  • No tab bar
  • Only tab icon
  • Creating a new tab from the overview automatically toggles the sidebar (= 1 click saved)

@diamondburned I've seen that you wanted to use the AdwTabOverview by merging the sidebar into the AdwTabPage [1]

Well, my implementation does not requires that (for now at least).

@DodoLeDev

Copy link
Copy Markdown
Author

Some news about this port: I have revamped the sidebar! It removes the buttons in the header bar and make everything more mobile-friendly! The description of the channel is also partially visible in the header, and the message summaries are more readable thanks to all the space available in the sidebar:

Capture d'écran_20260703_155147 Capture d'écran_20260703_155500

@diamondburned

Copy link
Copy Markdown
Owner

This looks really good!

@DodoLeDev

Copy link
Copy Markdown
Author

The changes are now too big for an easy merge, so there will be a phase where I will resolve all the various conflicts, fix the translations, add comments,...

And first of all, I think I will need your help to know if the coding style is compliant with what you expect, because once merged, I will be less active in this project I guess

@DodoLeDev

Copy link
Copy Markdown
Author

New sidebar feature: recipients list for DM and GroupDM

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants