Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions data/dev.geopjr.Tuba.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@
<key name="use-in-app-browser-if-available" type="b">
<default>true</default>
</key>
<key name="collapse-long-posts" type="b">
<default>true</default>
</key>

<key name="window-w" type="i">
<default>600</default>
Expand Down
5 changes: 5 additions & 0 deletions data/ui/dialogs/preferences.ui
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@
<property name="title" translatable="yes">Reveal Spoilers by Default</property>
</object>
</child>
<child>
<object class="AdwSwitchRow" id="collapse_long_posts">
<property name="title" translatable="yes">Collapse Long Posts</property>
</object>
</child>
<child>
<object class="AdwSwitchRow" id="show_preview_cards">
<property name="title" translatable="yes">Show Link Preview Cards</property>
Expand Down
32 changes: 27 additions & 5 deletions data/ui/widgets/status.ui
Original file line number Diff line number Diff line change
Expand Up @@ -296,11 +296,33 @@
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="TubaWidgetsMarkupView" id="content">
<property name="visible">True</property>
<property name="hexpand">False</property>
<property name="extract-last-tags">True</property>
</object>
<object class="GtkOverlay">
<child type="overlay">
<object class="GtkButton">
<!-- translators: as in 'Expand collapsed post'. It's a button label. -->
<property name="label" translatable="yes">Expand</property>
<property name="halign">center</property>
<property name="valign">end</property>
<property name="visible" bind-source="fade_bin" bind-property="faded" bind-flags="sync-create" />
<signal name="clicked" handler="on_fade_reveal" swapped="no" />
<style>
<class name="pill" />
<class name="osd" />
</style>
</object>
</child>
<property name="child">
<object class="TubaWidgetsFadeBin" id="fade_bin">
<property name="child">
<object class="TubaWidgetsMarkupView" id="content">
<property name="visible">True</property>
<property name="hexpand">False</property>
<property name="extract-last-tags">True</property>
</object>
</property>
</object>
</property>
</object>
</child>
</object>
</property>
Expand Down
2 changes: 2 additions & 0 deletions src/Dialogs/Preferences.vala
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ public class Tuba.Dialogs.Preferences : Adw.PreferencesDialog {
[GtkChild] unowned Adw.SwitchRow copy_private_link_reminder;
[GtkChild] unowned Adw.EntryRow proxy_entry;
[GtkChild] unowned Adw.SwitchRow dim_trivial_notifications;
[GtkChild] unowned Adw.SwitchRow collapse_long_posts;

[GtkChild] unowned Adw.SwitchRow new_followers_notifications_switch;
[GtkChild] unowned Adw.SwitchRow new_follower_requests_notifications_switch;
Expand Down Expand Up @@ -266,6 +267,7 @@ public class Tuba.Dialogs.Preferences : Adw.PreferencesDialog {
settings.bind ("dim-trivial-notifications", dim_trivial_notifications, "active", SettingsBindFlags.DEFAULT);
settings.bind ("analytics", analytics_switch, "active", SettingsBindFlags.DEFAULT);
settings.bind ("update-contributors", update_contributors, "active", SettingsBindFlags.DEFAULT);
settings.bind ("collapse-long-posts", collapse_long_posts, "active", SettingsBindFlags.DEFAULT);

post_visibility_combo_row.notify["selected-item"].connect (on_post_visibility_changed);
dlcr_id = default_language_combo_row.notify["selected-item"].connect (dlcr_cb);
Expand Down
4 changes: 3 additions & 1 deletion src/Services/Settings.vala
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public class Tuba.Settings : GLib.Settings {
public string[] contributors { get; set; default = {}; }
public int status_aria_verbosity { get; set; default = 3; }
public bool use_in_app_browser_if_available { get; set; }
public bool collapse_long_posts { get; set; }

private static string[] keys_to_init = {
"active-account",
Expand Down Expand Up @@ -191,7 +192,8 @@ public class Tuba.Settings : GLib.Settings {
"analytics",
"update-contributors",
"status-aria-verbosity",
"use-in-app-browser-if-available"
"use-in-app-browser-if-available",
"collapse-long-posts"
};

public Settings () {
Expand Down
210 changes: 210 additions & 0 deletions src/Widgets/FadeBin.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
public class Tuba.Widgets.FadeBin : Gtk.Widget {
const int MAX_HEIGHT = 300;
const float FADE_HEIGHT = 125f;
const uint ANIMATION_DURATION = 300;

private unowned Gtk.Widget? _child = null;
public Gtk.Widget? child {
get { return _child; }
set {
if (_child != null) _child.unparent ();
_child = value;
if (_child != null) _child.set_parent (this);
}
}

public override void dispose () {
if (this.child != null) {
this.child.unparent ();
this.child = null;
}

base.dispose ();
}

public override Gtk.SizeRequestMode get_request_mode () {
if (this.child != null) return this.child.get_request_mode ();
return Gtk.SizeRequestMode.CONSTANT_SIZE;
}

private bool _reveal = false;
public bool reveal {
get { return _reveal; }
set {
if (_reveal != value) {
_reveal = value;
this.queue_resize ();
}
}
}

public void reveal_animated () {
animation.value_from = 0.0;
animation.value_to = 1.0;
animation.play ();
}

public void hide_animated () {
animation.value_from = 1.0;
animation.value_to = 0.0;
animation.play ();
}

private bool _should_fade = false;
private bool should_fade {
get { return _should_fade; }
set {
if (_should_fade != value) {
_should_fade = value;
this.notify_property ("faded");
}
}
}

public bool faded {
get {
return this.should_fade && !reveal;
}
}

const Gsk.ColorStop[] GRADIENT = {
{ 0f, { 1, 1, 1, 1f } },
{ 1f, { 0, 0, 0, 0f } },
};

Adw.TimedAnimation animation;
construct {
var target = new Adw.CallbackAnimationTarget (animation_target_cb);
animation = new Adw.TimedAnimation (this, 0.0, 1.0, ANIMATION_DURATION, target) {
easing = Adw.Easing.EASE_IN_OUT_QUART
};
animation.done.connect (on_animation_end);
}

private void on_animation_end () {
this.reveal = !this.reveal;
}

private void animation_target_cb (double value) {
this.queue_resize ();
}

private inline double lerp (int a, int b, double p) {
return a * (1.0 - p) + b * p;
}

private inline double inverse_lerp (int a, int r, double p) {
return (r - a * (1.0 - p)) / p;
}

public override void size_allocate (int width, int height, int baseline) {
if (this.child == null) {
this.should_fade = false;
return;
}

int child_min_height;
this.child.measure (Gtk.Orientation.VERTICAL, width, out child_min_height, null, null, null);
var child_height = int.max (height, child_min_height);
this.child.allocate (width, child_height, baseline, null);

this.should_fade = !this.reveal && child_height >= MAX_HEIGHT;
}

public override void measure (Gtk.Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) {
if (this.child == null) {
minimum_baseline = natural_baseline = -1;
minimum = natural = 0;
return;
}

int child_for_size;
if (this.reveal || orientation == Gtk.Orientation.VERTICAL || for_size < MAX_HEIGHT || for_size == -1) {
child_for_size = for_size;
} else if (this.animation.value == 0.0) {
child_for_size = -1;
} else {
child_for_size = (int) Math.floor (inverse_lerp (
MAX_HEIGHT,
for_size,
this.animation.value
));
}

this.child.measure (
orientation,
child_for_size,
out minimum,
out natural,
out minimum_baseline,
out natural_baseline
);

if (orientation == Gtk.Orientation.VERTICAL && !this.reveal) {
minimum_baseline = natural_baseline = -1;

if (minimum > MAX_HEIGHT) {
minimum = (int) Math.ceil (lerp (
MAX_HEIGHT,
minimum,
this.animation.value
));
}

if (natural > MAX_HEIGHT) {
natural = (int) Math.ceil (lerp (
MAX_HEIGHT,
natural,
this.animation.value
));
}
}
}

const float LOTS = 1000f;
public override void snapshot (Gtk.Snapshot snapshot) {
if (this.child == null) {
base.snapshot (snapshot);
return;
}

if (!this.faded) {
this.snapshot_child (this.child, snapshot);
return;
}

var height = this.get_height ();
var width = this.get_width ();

Graphene.Rect clip_rect = Graphene.Rect () {
origin = Graphene.Point () {
x = -LOTS,
y = -LOTS
},
size = Graphene.Size () {
width = width + 2 * LOTS,
height = height + LOTS
}
};
snapshot.push_clip (clip_rect);

snapshot.push_mask (Gsk.MaskMode.ALPHA);
snapshot.append_linear_gradient (
clip_rect,
Graphene.Point () {
x = 0,
y = height - FADE_HEIGHT
},
Graphene.Point () {
x = 0,
y = height
},
GRADIENT
);
snapshot.pop ();

this.snapshot_child (this.child, snapshot);
snapshot.pop ();
snapshot.pop ();
}
}
5 changes: 5 additions & 0 deletions src/Widgets/LabelWithWidgets.vala
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,11 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce
set { label.xalign = value; }
}

public float yalign {
get { return label.yalign; }
set { label.yalign = value; }
}

public bool selectable {
get { return label.selectable; }
set { label.selectable = value; }
Expand Down
1 change: 1 addition & 0 deletions src/Widgets/MarkupView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public class Tuba.Widgets.MarkupView : Gtk.Box {
large_emojis = settings.enlarge_custom_emojis,
use_markup = true,
fix_overflow_hack = true,
yalign = 0f,
// focusable_label = true
};
if (instance_emojis != null) label.instance_emojis = instance_emojis;
Expand Down
5 changes: 5 additions & 0 deletions src/Widgets/RichLabel.vala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public class Tuba.Widgets.RichLabel : Adw.Bin {
set { widget.xalign = value; }
}

public float yalign {
get { return widget.yalign; }
set { widget.yalign = value; }
}

public bool smaller_emoji_pixel_size {
get { return widget.smaller_emoji_pixel_size; }
set { widget.smaller_emoji_pixel_size = value; }
Expand Down
Loading
Loading