Refactor HeaderBar

This commit is contained in:
Bleak Grey 2020-06-30 00:43:45 +03:00 committed by GitHub
parent a6f1592809
commit 25e4d870e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 916 additions and 488 deletions

2
.gitignore vendored
View File

@ -1,6 +1,4 @@
_ignore
build
build.sh
build-po.sh
uninstall.sh
*~

View File

@ -3,6 +3,7 @@ Simple [Mastodon](https://github.com/tootsuite/mastodon) client for Linux
![Screenshot](https://raw.githubusercontent.com/bleakgrey/tootle/master/data/screenshot.png)
### Installation
This project is undergoing a major rewrite and will be published in the near future.
@ -10,30 +11,36 @@ To help the project, please build it manually and help test it.
<a href='https://flathub.org/apps/details/com.github.bleakgrey.tootle'><img height='51' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
### Building
To build the app, make sure you have these dependencies:
* meson
* valac
* libgtk-3-dev
* libsoup2.4-dev
* libgranite-dev
* libjson-glib-dev
* libhandy-1.0-dev
Then run `install.sh` in the project directory to install the app.
### Building
If the options above are not available to you, you can build the app from source:
1. Make sure you have these dependencies:
- [x] meson
- [x] valac
- [x] libgtk-3-dev
- [x] libsoup2.4-dev
- [x] libgranite-dev
- [x] libjson-glib-dev
- [ ] libhandy-1.0-dev
*Note: Unchecked items will be installed automatically if not present in the system.*
2. Run `install.sh` in the project directory. The app will launch automatically on success.
### Contributing
You're always welcome to help the project in many ways:
* Donating with [LiberaPay](https://liberapay.com/bleakgrey/) to keep the developer happy and motivated
* Reporting issues and bugs
* Submitting pull requests
Please consider donating with [LiberaPay](https://liberapay.com/bleakgrey/) to keep the developer happy.
<a href="https://liberapay.com/bleakgrey/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
You can always help by reporting bugs, submitting pull requests, and suggesting ideas.
### Credits
* Icon design by [Tobias Bernard](https://github.com/bertob)
* French translation by [@Larnicone](https://github.com/Larnicone)
* Polish translation by [@m4sk1n](https://github.com/m4sk1n)
* German translation by [@koyuawsmbrtn](https://github.com/koyuawsmbrtn)
* Simplified Chinese translation by [@gloomy-ghost](https://github.com/gloomy-ghost)

View File

@ -7,6 +7,13 @@
background: rgba (150, 150, 150, 0.2);
}
.header-title-button {
margin: 0px;
border-radius: 0px;
border-top: none;
border-bottom: none;
}
.padded.app-view {
margin: 32px 0 32px 0;
}

View File

@ -29,7 +29,7 @@
<default>20</default>
</key>
<key name="post-text-size" type="i">
<default>100</default>
<default>120</default>
</key>
<key name="live-updates" type="b">
<default>true</default>

View File

@ -8,6 +8,8 @@
<file preprocess="xml-stripblanks">ui/widgets/status.ui</file>
<file preprocess="xml-stripblanks">ui/widgets/accounts_button.ui</file>
<file preprocess="xml-stripblanks">ui/widgets/accounts_button_item.ui</file>
<file preprocess="xml-stripblanks">ui/widgets/profile_field_row.ui</file>
<file preprocess="xml-stripblanks">ui/widgets/timeline_filter.ui</file>
<file preprocess="xml-stripblanks">ui/dialogs/compose.ui</file>
<file preprocess="xml-stripblanks">ui/dialogs/main.ui</file>
<file preprocess="xml-stripblanks">ui/dialogs/preferences.ui</file>

View File

@ -64,43 +64,99 @@
<property name="visible">True</property>
<property name="title">Tootle</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="back_button">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">go-previous-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="compose_button">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImage">
<object class="TootleWidgetsAccountsButton" id="accounts_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">document-edit-symbolic</property>
<style>
<class name="image-button"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="compose_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">document-edit-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="linked"/>
<class name="horizontal"/>
</style>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="TootleWidgetsAccountsButton" id="accounts_button">
<object class="GtkRevealer" id="view_navigation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="transition_type">slide-right</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="back_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">go-previous-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.36.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.14"/>
<object class="GtkPopover" id="filter_popover">
<property name="can_focus">False</property>
<child>
@ -101,212 +101,10 @@
<property name="row_spacing">8</property>
<property name="column_spacing">8</property>
<child>
<object class="GtkListBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<property name="activate_on_single_click">False</property>
<child>
<object class="GtkListBoxRow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="activatable">False</property>
<property name="selectable">False</property>
<child>
<object class="TootleWidgetsRichLabel" id="note">
<property name="visible">True</property>
<property name="wrap">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="selectable">True</property>
<property name="width_chars">25</property>
</object>
</child>
</object>
</child>
<style>
<class name="preferences"/>
</style>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkListBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<property name="activate_on_single_click">False</property>
<child>
<object class="GtkListBoxRow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="activatable">False</property>
<property name="selectable">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="spacing">8</property>
<child>
<object class="GtkBox">
<property name="height_request">40</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="spacing">32</property>
<child>
<object class="GtkRadioButton" id="posts_tab">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<child>
<object class="GtkLabel" id="posts_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">0 Posts</property>
<property name="use_markup">True</property>
</object>
</child>
<style>
<class name="flat"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="following_tab">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">posts_tab</property>
<child>
<object class="GtkLabel" id="following_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">0 Follows</property>
<property name="use_markup">True</property>
</object>
</child>
<style>
<class name="flat"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="followers_tab">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">posts_tab</property>
<child>
<object class="GtkLabel" id="followers_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">0 Followers</property>
<property name="use_markup">True</property>
</object>
</child>
<style>
<class name="flat"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<style>
<class name="linked"/>
<class name="horizontal"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="filter_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<property name="draw_indicator">True</property>
<property name="popover">filter_popover</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">view-more-symbolic</property>
</object>
</child>
<style>
<class name="flat"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<style>
<class name="preferences"/>
</style>
</object>
</child>
<style>
<class name="preferences"/>
</style>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkListBox">
<object class="GtkListBox" id="profile_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
<property name="activate_on_single_click">False</property>
<child>
@ -328,13 +126,13 @@
<property name="spacing">8</property>
<child>
<object class="TootleWidgetsAvatar" id="avatar">
<property name="visible">True</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="size">128</property>
</object>
<property name="visible">True</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="size">96</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
@ -351,10 +149,10 @@
<property name="orientation">vertical</property>
<property name="spacing">8</property>
<child>
<object class="TootleWidgetsRichLabel" id="name">
<property name="visible">True</property>
<property name="selectable">True</property>
</object>
<object class="TootleWidgetsRichLabel" id="handle">
<property name="visible">True</property>
<property name="selectable">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
@ -362,10 +160,10 @@
</packing>
</child>
<child>
<object class="TootleWidgetsRichLabel" id="handle">
<property name="visible">True</property>
<property name="selectable">True</property>
</object>
<object class="TootleWidgetsRichLabel" id="relationship">
<property name="visible">False</property>
<property name="opacity">0.5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
@ -399,7 +197,7 @@
<property name="margin_bottom">8</property>
<property name="spacing">8</property>
<child>
<object class="GtkLabel" id="relationship">
<object class="GtkLabel" id="old-relationship">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
@ -462,6 +260,27 @@
</child>
</object>
</child>
<child>
<object class="GtkListBoxRow" id="note_row">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="activatable">False</property>
<property name="selectable">False</property>
<child>
<object class="TootleWidgetsRichLabel" id="note">
<property name="visible">True</property>
<property name="wrap">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="selectable">True</property>
<property name="width_chars">25</property>
</object>
</child>
</object>
</child>
<style>
<class name="preferences"/>
</style>
@ -480,11 +299,5 @@
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</interface>

View File

@ -215,7 +215,7 @@
<child>
<object class="TootleWidgetsAvatar" id="avatar">
<property name="visible">True</property>
<property name="size">24</property>
<property name="size">28</property>
</object>
<packing>
<property name="pass_through">True</property>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.36.0 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<template class="TootleViewsProfileField" parent="GtkListBoxRow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_start">8</property>
<property name="margin_end">8</property>
<property name="column_spacing">8</property>
<child>
<object class="TootleWidgetsRichLabel" id="name_label">
<property name="width_request">180</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="vexpand">True</property>
<property name="label">Name</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="xalign">1</property>
<property name="max-width-chars">1</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="TootleWidgetsRichLabel" id="value_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="hexpand">True</property>
<property name="label">Value</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

View File

@ -41,9 +41,8 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="margin_bottom">8</property>
<property name="icon_name">applications-development-symbolic</property>
<property name="icon_name">oops</property>
<property name="icon_size">1</property>
</object>
<packing>
@ -155,63 +154,6 @@
<property name="width">3</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="spacing">8</property>
<child>
<object class="TootleWidgetsRichLabel" id="handle_label">
<property name="visible">True</property>
<property name="label">Handle</property>
<property name="ellipsize">end</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="date_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="opacity">0.5</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Yesterday</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkImage" id="pin_indicator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="icon_name">view-pin-symbolic</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">3</property>
</packing>
</child>
<child>
<object class="GtkRevealer" id="revealer">
<property name="visible">True</property>
@ -224,15 +166,15 @@
<property name="orientation">vertical</property>
<child>
<object class="TootleWidgetsRichLabel" id="revealer_content">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="width_chars">15</property>
<property name="xalign">0</property>
</object>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="width_chars">15</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
@ -241,9 +183,9 @@
</child>
<child>
<object class="TootleWidgetsAttachmentBox" id="attachments">
<property name="visible">True</property>
<property name="margin_top">8</property>
</object>
<property name="visible">True</property>
<property name="margin_top">8</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
@ -261,23 +203,24 @@
</child>
<child>
<object class="TootleWidgetsAvatar" id="avatar">
<property name="width_request">48</property>
<property name="height_request">48</property>
<property name="valign">start</property>
<property name="visible">true</property>
</object>
<property name="width_request">48</property>
<property name="height_request">48</property>
<property name="valign">start</property>
<property name="visible">true</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="height">4</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="TootleWidgetsRichLabel" id="header_label">
<property name="visible">True</property>
<property name="ellipsize">end</property>
<property name="xalign">0</property>
</object>
<property name="visible">True</property>
<property name="ellipsize">end</property>
<property name="xalign">0</property>
<property name="margin-bottom">8</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
@ -286,21 +229,94 @@
</child>
<child>
<object class="TootleWidgetsRichLabel" id="content">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="width_chars">15</property>
<property name="xalign">0</property>
</object>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="width_chars">15</property>
<property name="xalign">0</property>
<property name="margin-top">8</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="width">3</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="vexpand">True</property>
<property name="valign">fill</property>
<property name="row_homogeneous">True</property>
<child>
<object class="TootleWidgetsRichLabel" id="name_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Name</property>
<property name="ellipsize">end</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="TootleWidgetsRichLabel" id="handle_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="no">Handle</property>
<property name="opacity">0.5</property>
<property name="ellipsize">end</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">4</property>
</packing>
</child>
<child>
<object class="GtkImage" id="pin_indicator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_start">8</property>
<property name="icon_name">view-pin-symbolic</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="TootleWidgetsRichLabel" id="date_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="opacity">0.5</property>
<property name="margin_left">8</property>
<property name="margin_start">8</property>
<property name="label" translatable="no">Yesterday</property>
<property name="halign">end</property>
<property name="valign">start</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">3</property>
</packing>
</child>
</object>
</child>
</template>

View File

@ -0,0 +1,345 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.36.0 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkPopover" id="popover">
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_start">8</property>
<property name="margin_end">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">8</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Show:</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">8</property>
<child>
<object class="GtkRadioButton" id="radio_source">
<property name="name">statuses</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">0 Posts</property>
<property name="xalign">0</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_source_following">
<property name="name">following</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">radio_source</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">0 Follows</property>
<property name="xalign">0</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_source_followers">
<property name="name">followers</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">radio_source</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">0 Followers</property>
<property name="xalign">0</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRevealer" id="post_filter">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">FIlter:</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">8</property>
<property name="orientation">vertical</property>
<property name="spacing">8</property>
<child>
<object class="GtkRadioButton" id="radio_post_filter">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">None</property>
<property name="xalign">0</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_post_filter_with_replies">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">radio_post_filter</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Include Replies</property>
<property name="xalign">0</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="radio_post_only_media">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">radio_post_filter</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Only Media</property>
<property name="xalign">0</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<template class="TootleWidgetsTimelineFilter" parent="GtkMenuButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="focus_on_click">False</property>
<property name="receives_default">True</property>
<property name="draw_indicator">True</property>
<property name="popover">popover</property>
<child>
<object class="GtkStack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="transition_duration">100</property>
<property name="transition_type">crossfade</property>
<child>
<object class="GtkLabel" id="title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="ellipsize">end</property>
<property name="single_line_mode">True</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="name">title</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="spacing">8</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">mail-send-symbolic</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="title_scrolled">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">1K</property>
<property name="ellipsize">end</property>
<property name="single_line_mode">True</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="name">title_scrolled</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<style>
<class name="flat"/>
<class name="header-title-buttonx"/>
</style>
</template>
</interface>

View File

@ -1,4 +1,6 @@
meson build --prefix=/usr
clear
meson build
cd build
ninja
sudo ninja install
com.github.bleakgrey.tootle

View File

@ -26,18 +26,23 @@ asresources = gnome.compile_resources(
c_name: 'as'
)
libhandy_dep = dependency('libhandy-1', version: '>= 0.80.0')
libhandy_dep = dependency('libhandy-1', version: '>= 0.80.0', required: false)
if not libhandy_dep.found()
libhandy = subproject(
'libhandy',
install: false,
default_options: [
'examples=false',
'package_subdir=' + meson.project_name(),
'glade_catalog=disabled',
'tests=false',
]
)
libhandy_dep = libhandy.get_variable('libhandy_dep')
libhandy_dep = declare_dependency(
dependencies: [
libhandy.get_variable('libhandy_dep'),
libhandy.get_variable('libhandy_vapi'),
]
)
endif
executable(
@ -58,6 +63,7 @@ executable(
'src/Services/Cache.vala',
'src/Services/Network.vala',
'src/API/Account.vala',
'src/API/AccountField.vala',
'src/API/Relationship.vala',
'src/API/Mention.vala',
'src/API/Tag.vala',
@ -71,6 +77,7 @@ executable(
'src/Widgets/Widgetizable.vala',
'src/Widgets/Avatar.vala',
'src/Widgets/AccountsButton.vala',
'src/Widgets/TimelineFilter.vala',
'src/Widgets/RichLabel.vala',
'src/Widgets/Status.vala',
'src/Widgets/Notification.vala',

View File

@ -1,4 +1,4 @@
public class Tootle.API.Account : Entity {
public class Tootle.API.Account : Entity, Widgetizable {
public string id { get; set; }
public string username { get; set; }
@ -21,6 +21,13 @@ public class Tootle.API.Account : Entity {
public int64 following_count { get; set; }
public int64 statuses_count { get; set; }
public Relationship? rs { get; set; default = null; }
public Gee.ArrayList<API.AccountField>? fields { get; set; default = null; }
public string handle {
owned get {
return "@" + acct;
}
}
public static Account from (Json.Node node) throws Error {
return Entity.from_json (typeof (API.Account), node) as API.Account;
@ -30,6 +37,13 @@ public class Tootle.API.Account : Entity {
return id == accounts.active.id;
}
public override Gtk.Widget to_widget () {
var status = new API.Status.from_account (this);
var w = new Widgets.Status (status);
w.button_press_event.connect (w.open);
return w;
}
public Request get_relationship () {
return new Request.GET ("/api/v1/accounts/relationships")
.with_account (accounts.active)

View File

@ -0,0 +1,7 @@
public class Tootle.API.AccountField : Entity {
public string name { get; set; }
public string val { get; set; }
public string? verified_at { get; set; }
}

View File

@ -36,12 +36,19 @@ public class Tootle.Entity : GLib.Object, Widgetizable, Json.Serializable {
if (obj == null)
throw new Oopsie.PARSING (@"Received Json.Node for $(type.name ()) is not a Json.Object!");
//Replace with something more elegant
var kind = obj.get_member ("type");
if (kind != null) {
obj.set_member ("kind", kind);
obj.remove_member ("type");
}
var val = obj.get_member ("value");
if (val != null) {
obj.set_member ("val", val);
obj.remove_member ("value");
}
return Json.gobject_deserialize (type, node) as Entity;
}
@ -67,6 +74,8 @@ public class Tootle.Entity : GLib.Object, Widgetizable, Json.Serializable {
if (type.is_a (typeof (Gee.ArrayList))) {
Type contains;
//There has to be a better way
switch (prop) {
case "media-attachments":
contains = typeof (API.Attachment);
@ -74,6 +83,9 @@ public class Tootle.Entity : GLib.Object, Widgetizable, Json.Serializable {
case "mentions":
contains = typeof (API.Mention);
break;
case "fields":
contains = typeof (API.AccountField);
break;
default:
contains = typeof (Entity);
break;

View File

@ -12,7 +12,9 @@ public class Tootle.Dialogs.MainWindow: Gtk.Window, ISavedWindow {
protected Stack timeline_stack;
[GtkChild]
protected HeaderBar header;
public HeaderBar header;
[GtkChild]
protected Revealer view_navigation;
[GtkChild]
protected Button back_button;
[GtkChild]
@ -75,6 +77,10 @@ public class Tootle.Dialogs.MainWindow: Gtk.Window, ISavedWindow {
}
public bool open_view (Views.Base widget) {
var curr = view_stack.visible_child as Views.Base;
if (curr != null)
curr.current = false;
var i = get_visible_id ();
i++;
widget.stack_pos = i;
@ -82,6 +88,7 @@ public class Tootle.Dialogs.MainWindow: Gtk.Window, ISavedWindow {
view_stack.add_named (widget, i.to_string ());
view_stack.set_visible_child_name (i.to_string ());
update_header ();
widget.current = true;
return true;
}
@ -92,8 +99,13 @@ public class Tootle.Dialogs.MainWindow: Gtk.Window, ISavedWindow {
var child = view_stack.get_child_by_name (i.to_string ());
view_stack.set_visible_child_name ((i-1).to_string ());
(child as Views.Base).current = false;
child.destroy ();
update_header ();
var curr = view_stack.visible_child as Views.Base;
if (curr != null)
curr.current = true;
return true;
}
@ -136,8 +148,10 @@ public class Tootle.Dialogs.MainWindow: Gtk.Window, ISavedWindow {
bool primary_mode = get_visible_id () == 0;
switcher_navbar.visible = timeline_switcher.sensitive = primary_mode;
timeline_switcher.opacity = primary_mode ? 1 : 0; //Prevent HeaderBar height jitter
compose_button.visible = primary_mode;
back_button.visible = !primary_mode;
view_navigation.reveal_child = !primary_mode;
if (primary_mode)
header.custom_title = timeline_switcher;
}
void on_timeline_changed (ParamSpec spec) {

View File

@ -5,22 +5,6 @@ public class Tootle.Html {
return GLib.Markup.escape_text (all_tags.replace (content, -1, 0, ""));
}
public static string escape_pango_entities (string str) {
return str
.replace ("&nbsp;", " ")
.replace ("'", "&apos;")
.replace ("& ", "&amp;");
}
public static string restore_entities (string str) {
return str
.replace ("&amp;", "&")
.replace ("&lt;", "<")
.replace ("&gt;", ">")
.replace ("&apos;", "'")
.replace ("&quot;", "\"");
}
public static string simplify (string str) {
var divided = str
.replace("<br>", "\n")
@ -35,11 +19,11 @@ public class Tootle.Html {
while (simplified.has_suffix ("\n"))
simplified = simplified.slice (0, simplified.last_index_of ("\n"));
return escape_pango_entities (simplified);
return simplified;
}
public static string uri_encode (string str) {
var restored = restore_entities (str);
var restored = Widgets.RichLabel.restore_entities (str);
return Soup.URI.encode (restored, ";&+");
}

View File

@ -14,7 +14,7 @@ public class Tootle.InstanceAccount : API.Account, IStreamListener {
protected string? stream;
public string handle {
public new string handle {
owned get { return @"@$username@$short_instance"; }
}
public string short_instance {

View File

@ -4,26 +4,26 @@ public class Tootle.Views.Profile : Views.Timeline {
public API.Account profile { get; construct set; }
protected RadioButton filter_all;
protected RadioButton filter_replies;
protected RadioButton filter_media;
ListBox profile_list;
protected Label relationship;
protected Box actions;
protected Button follow_button;
protected MenuButton options_button;
Label relationship;
Box actions;
Button follow_button;
MenuButton options_button;
protected Label posts_label;
protected Label following_label;
protected Label followers_label;
protected RadioButton posts_tab;
protected RadioButton following_tab;
protected RadioButton followers_tab;
Widgets.TimelineFilter filter;
public bool exclude_replies { get; set; default = true; }
public bool only_media { get; set; default = false; }
construct {
profile.notify["rs"].connect (on_rs_updated);
filter = new Widgets.TimelineFilter.with_profile (this);
var builder = new Builder.from_resource (@"$(Build.RESOURCES)ui/views/profile_header.ui");
profile_list = builder.get_object ("profile_list") as ListBox;
var hdr = builder.get_object ("grid") as Grid;
column_view.pack_start (hdr, false, false, 0);
column_view.reorder_child (hdr, 0);
@ -31,21 +31,17 @@ public class Tootle.Views.Profile : Views.Timeline {
var avatar = builder.get_object ("avatar") as Widgets.Avatar;
avatar.url = profile.avatar;
var name = builder.get_object ("name") as Widgets.RichLabel;
profile.bind_property ("display-name", name, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
var label = (string) src;
target.set_string (@"<span size='x-large' weight='bold'>$label</span>");
return true;
});
profile.bind_property ("display-name", filter.title, "label", BindingFlags.SYNC_CREATE);
var handle = builder.get_object ("handle") as Widgets.RichLabel;
profile.bind_property ("acct", handle, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
target.set_string ("@" + (string) src);
profile.bind_property ("acct", handle, "text", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
var text = "@" + (string) src;
target.set_string (@"<span size=\"x-large\" weight=\"bold\">$text</span>");
return true;
});
var note = builder.get_object ("note") as Widgets.RichLabel;
profile.bind_property ("note", note, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
profile.bind_property ("note", note, "text", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
target.set_string (Html.simplify ((string) src));
return true;
});
@ -56,57 +52,49 @@ public class Tootle.Views.Profile : Views.Timeline {
options_button = builder.get_object ("options_button") as MenuButton;
relationship = builder.get_object ("relationship") as Label;
posts_label = builder.get_object ("posts_label") as Label;
profile.bind_property ("statuses_count", posts_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
var val = (int64) src;
target.set_string (_("%s Posts").printf (@"<b>$val</b>"));
return true;
});
following_label = builder.get_object ("following_label") as Label;
profile.bind_property ("following_count", following_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
var val = (int64) src;
target.set_string (_("%s Follows").printf (@"<b>$val</b>"));
return true;
});
followers_label = builder.get_object ("followers_label") as Label;
profile.bind_property ("followers_count", followers_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
var val = (int64) src;
target.set_string (_("%s Followers").printf (@"<b>$val</b>"));
return true;
});
// posts_label = builder.get_object ("posts_label") as Label;
// profile.bind_property ("statuses_count", posts_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
// var val = (int64) src;
// target.set_string (_("%s Posts").printf (@"<b>$val</b>"));
// return true;
// });
// following_label = builder.get_object ("following_label") as Label;
// profile.bind_property ("following_count", following_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
// var val = (int64) src;
// target.set_string (_("%s Follows").printf (@"<b>$val</b>"));
// return true;
// });
// followers_label = builder.get_object ("followers_label") as Label;
// profile.bind_property ("followers_count", followers_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
// var val = (int64) src;
// target.set_string (_("%s Followers").printf (@"<b>$val</b>"));
// return true;
// });
filter_all = builder.get_object ("filter_all") as RadioButton;
filter_all.toggled.connect (on_refresh);
filter_replies = builder.get_object ("filter_replies") as RadioButton;
filter_replies.toggled.connect (on_refresh);
filter_media = builder.get_object ("filter_media") as RadioButton;
filter_media.toggled.connect (on_refresh);
posts_tab = builder.get_object ("posts_tab") as RadioButton;
posts_tab.toggled.connect (() => {
if (posts_tab.active) on_refresh ();
});
following_tab = builder.get_object ("following_tab") as RadioButton;
following_tab.toggled.connect (() => {
if (following_tab.active) on_refresh ();
});
followers_tab = builder.get_object ("followers_tab") as RadioButton;
followers_tab.toggled.connect (() => {
if (followers_tab.active) on_refresh ();
});
rebuild_fields ();
}
public Profile (API.Account acc) {
Object (profile: acc);
Object (
profile: acc,
url: @"/api/v1/accounts/$(acc.id)/statuses"
);
profile.get_relationship ();
}
protected void on_follow_button_clicked () {
public override void on_shown () {
window.header.custom_title = filter;
}
public override void on_hidden () {
window.header.custom_title = null;
}
void on_follow_button_clicked () {
actions.sensitive = false;
profile.set_following (!profile.rs.following);
}
protected void on_rs_updated () {
void on_rs_updated () {
var rs = profile.rs;
var label = "";
if (actions.sensitive = rs != null) {
@ -136,24 +124,15 @@ public class Tootle.Views.Profile : Views.Timeline {
}
relationship.label = label;
relationship.visible = label != "";
}
public override string get_req_url () {
if (page_next != null)
return page_next;
if (following_tab.active)
return @"/api/v1/accounts/$(profile.id)/following";
else if (followers_tab.active)
return @"/api/v1/accounts/$(profile.id)/followers";
else
return @"/api/v1/accounts/$(profile.id)/statuses";
}
public override Request append_params (Request req) {
if (page_next == null) {
req.with_param ("exclude_replies", (!filter_replies.active).to_string ());
req.with_param ("only_media", filter_media.active.to_string ());
if (exclude_replies)
req.with_param ("exclude_replies", "true");
if (only_media)
req.with_param ("only_media", "true");
return base.append_params (req);
}
else
@ -173,4 +152,28 @@ public class Tootle.Views.Profile : Views.Timeline {
});
}
[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/profile_field_row.ui")]
protected class Field : ListBoxRow {
[GtkChild]
Widgets.RichLabel name_label;
[GtkChild]
Widgets.RichLabel value_label;
public Field (API.AccountField field) {
name_label.text = field.name;
value_label.text = field.val;
}
}
void rebuild_fields () {
if (profile.fields != null) {
foreach (Entity e in profile.fields) {
var w = new Field (e as API.AccountField);
profile_list.insert (w, 2);
}
}
}
}

View File

@ -9,10 +9,10 @@ public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Ba
protected InstanceAccount? account = null;
protected bool is_last_page { get; set; default = false; }
protected string? page_next { get; set; }
protected string? page_prev { get; set; }
protected string? stream = null;
public bool is_last_page { get; set; default = false; }
public string? page_next { get; set; }
public string? page_prev { get; set; }
public string? stream = null;
construct {
app.refresh.connect (on_refresh);

View File

@ -5,7 +5,7 @@ public class Tootle.Widgets.Avatar : EventBox {
public string? url { get; set; }
public int size { get; set; default = 48; }
private Cache.Reference? cached;
construct {
@ -20,33 +20,34 @@ public class Tootle.Widgets.Avatar : EventBox {
Object (size: size);
on_redraw ();
}
~Avatar () {
notify["url"].disconnect (on_url_updated);
Screen.get_default ().monitors_changed.disconnect (on_redraw);
cache.unload (cached);
}
private void on_url_updated () {
cached = null;
on_redraw ();
cache.load (url, on_cache_result);
}
private void on_cache_result (Cache.Reference? result) {
cached = result;
on_redraw ();
}
public int get_scaled_size () {
return size * get_scale_factor ();
return size;
//return size * get_scale_factor ();
}
private void on_redraw () {
set_size_request (get_scaled_size (), get_scaled_size ());
queue_draw_area (0, 0, size, size);
}
public override bool draw (Cairo.Context ctx) {
var w = get_allocated_width ();
var h = get_allocated_height ();

View File

@ -5,6 +5,15 @@ public class Tootle.Widgets.RichLabel : Label {
public weak ArrayList<API.Mention>? mentions;
public string text {
get {
return this.label;
}
set {
this.label = escape_entities (Html.simplify (value));
}
}
construct {
use_markup = true;
xalign = 0;
@ -34,10 +43,6 @@ public class Tootle.Widgets.RichLabel : Label {
.replace ("&quot;", "\"");
}
public new void set_label (string text) {
base.set_markup (Html.simplify(escape_entities (text)));
}
public bool open_link (string url) {
if ("tootle://" in url)
return false;

View File

@ -18,9 +18,11 @@ public class Tootle.Widgets.Status : EventBox {
[GtkChild]
public Widgets.Avatar avatar;
[GtkChild]
protected Widgets.RichLabel name_label;
[GtkChild]
protected Widgets.RichLabel handle_label;
[GtkChild]
protected Label date_label;
protected Widgets.RichLabel date_label;
[GtkChild]
protected Image pin_indicator;
[GtkChild]
@ -50,27 +52,26 @@ public class Tootle.Widgets.Status : EventBox {
protected string escaped_spoiler {
owned get {
if (status.formal.has_spoiler) {
var text = Html.simplify (status.formal.spoiler_text ?? "");
var text = status.formal.spoiler_text ?? "";
var label = _("[ Toggle content ]");
text += @" <a href='tootle://toggle'>$label</a>";
text += @" <a href=\"tootle://toggle\">$label</a>";
return text;
}
else
return Html.simplify (status.formal.content);
return status.formal.content;
}
}
protected string escaped_content {
owned get {
return status.formal.has_spoiler ? Html.simplify (status.formal.content) : "";
return status.formal.has_spoiler ? status.formal.content : "";
}
}
protected string handle {
protected string display_name {
owned get {
var name = Html.simplify (status.formal.account.display_name);
var handle = Html.simplify (status.formal.account.acct);
return @"<b>$name</b> @$handle";
return @"<b>$name</b>";
}
}
@ -102,10 +103,11 @@ public class Tootle.Widgets.Status : EventBox {
reply_button.clicked.connect (() => new Dialogs.Compose.reply (status));
bind_property ("escaped-spoiler", content, "label", BindingFlags.SYNC_CREATE);
bind_property ("escaped-content", revealer_content, "label", BindingFlags.SYNC_CREATE);
bind_property ("escaped-spoiler", content, "text", BindingFlags.SYNC_CREATE);
bind_property ("escaped-content", revealer_content, "text", BindingFlags.SYNC_CREATE);
status.formal.account.bind_property ("avatar", avatar, "url", BindingFlags.SYNC_CREATE);
bind_property ("handle", handle_label, "label", BindingFlags.SYNC_CREATE);
status.account.bind_property ("handle", handle_label, "label", BindingFlags.SYNC_CREATE);
bind_property ("display_name", name_label, "text", BindingFlags.SYNC_CREATE);
bind_property ("date", date_label, "label", BindingFlags.SYNC_CREATE);
status.formal.bind_property ("pinned", pin_indicator, "visible", BindingFlags.SYNC_CREATE);

View File

@ -0,0 +1,56 @@
using Gtk;
[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/timeline_filter.ui")]
public class Tootle.Widgets.TimelineFilter : MenuButton {
[GtkChild]
public Label title;
[GtkChild]
public RadioButton radio_source;
[GtkChild]
public Revealer post_filter;
[GtkChild]
public RadioButton radio_post_filter;
[GtkChild]
public RadioButton radio_post_only_media;
public string source { get; set; }
construct {
radio_source.bind_property ("active", post_filter, "reveal-child", BindingFlags.SYNC_CREATE);
}
public TimelineFilter.with_profile (Views.Profile view) {
radio_source.get_group ().@foreach (w => {
w.toggled.connect (() => {
if (w.active) {
source = w.name;
on_changed (view);
}
});
});
radio_post_filter.get_group ().@foreach (w => {
w.toggled.connect (() => {
if (w.active)
on_changed (view);
});
});
}
void on_changed (Views.Profile view) {
var entity = typeof (API.Status);
if (source != "statuses")
entity = typeof (API.Account);
view.exclude_replies = radio_post_filter.active;
view.only_media = radio_post_only_media.active;
view.page_next = view.page_prev = null;
view.url = @"/api/v1/accounts/$(view.profile.id)/$source";
view.accepts = entity;
view.on_refresh ();
}
}

View File

@ -0,0 +1,4 @@
[wrap-git]
directory=libhandy
url=https://gitlab.gnome.org/GNOME/libhandy.git
revision=v0.80.0

4
uninstall.sh Executable file
View File

@ -0,0 +1,4 @@
cd build
sudo ninja uninstall
cd ..
rm -rf build