Commit 695e4630 authored by Christopher Davis's avatar Christopher Davis

widgets: Start naive adaptive switcher implementation

Tobias Bernard's mockups have one adapting Switcher,
that should be re-used for the bottom and top bars. This
is the start of implementing that for Social.

See https://gitlab.gnome.org/Teams/Design/os-mockups/blob/master/view-switcher/adaptive-view-switcher-modes.png
parent 705cb6f1
Pipeline #52350 passed with stage
in 10 minutes and 3 seconds
......@@ -51,7 +51,7 @@ actionbar.bottombar > revealer > box {
border-bottom-width: 0px;
}
.top-switcher > button {
.switcher > button {
margin-top: 0;
margin-bottom: 0;
padding-left: 12px;
......@@ -62,15 +62,17 @@ actionbar.bottombar > revealer > box {
min-height: 38px;
}
.top-switcher > button:hover:not(:checked) {
.switcher > button:hover:not(:checked) {
background-image: none;
}
/*
.top-switcher > button:checked {
.mobile > box > label {
font-size: small;
}
.switcher > button:checked {
font-weight: bold;
}
*/
.post-widget-button {
background-image: none;
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkBox" id="main_content_page">
......@@ -33,6 +33,7 @@
<packing>
<property name="name">home_page</property>
<property name="title" translatable="yes">Home</property>
<property name="icon_name">user-home-symbolic</property>
</packing>
</child>
<child>
......@@ -57,6 +58,7 @@
<packing>
<property name="name">community_page</property>
<property name="title" translatable="yes">Community</property>
<property name="icon_name">system-users-symbolic</property>
<property name="position">1</property>
</packing>
</child>
......@@ -82,6 +84,7 @@
<packing>
<property name="name">world_page</property>
<property name="title" translatable="yes">World</property>
<property name="icon_name">preferences-desktop-locale-symbolic</property>
<property name="position">2</property>
</packing>
</child>
......
......@@ -128,7 +128,7 @@ impl App {
let bottom_switcher = widgets::BottomSwitcher::new(&main_content.timelines_stack);
self.header_stack
.main_header
.set_stack(&main_content.timelines_stack);
.create_switcher(&main_content.timelines_stack);
main_content
.container
.pack_end(&bottom_switcher.container, false, false, 0);
......
......@@ -21,7 +21,7 @@ use gtk::{self, prelude::*};
use std::{cell::RefCell, rc::Rc};
use crate::widgets::{Popovers, TopSwitcher};
use crate::widgets::{Popovers, Switcher};
#[derive(Copy, Clone, Debug)]
pub(crate) enum HeaderState {
......@@ -42,7 +42,6 @@ pub struct MainHeader {
account_button: gtk::MenuButton,
menu_button: gtk::MenuButton,
top_stack: gtk::Stack,
switcher: Rc<TopSwitcher>,
popovers: Rc<Popovers>,
}
......@@ -74,18 +73,18 @@ impl MainHeader {
.account_button
.set_popover(&widget.popovers.account_popover);
let weak_widget = Rc::downgrade(&widget);
widget
.headerbar
.connect_size_allocate(move |_, allocation| {
weak_widget.upgrade().map(|w| {
if allocation.width < 598 {
w.set_header_state(HeaderState::Mobile);
} else {
w.set_header_state(HeaderState::Desktop);
}
});
});
// let weak_widget = Rc::downgrade(&widget);
// widget
// .headerbar
// .connect_size_allocate(move |_, allocation| {
// weak_widget.upgrade().map(|w| {
// if allocation.width < 608 {
// w.set_header_state(HeaderState::Mobile);
// } else {
// w.set_header_state(HeaderState::Desktop);
// }
// });
// });
let foo = RefCell::new(Some(widget.clone()));
widget.headerbar.connect_remove(move |_, _| {
......@@ -95,8 +94,10 @@ impl MainHeader {
widget
}
pub(crate) fn set_stack(&self, stack: &gtk::Stack) {
TopSwitcher::set_stack(&self.switcher, stack);
pub(crate) fn create_switcher(&self, stack: &gtk::Stack) {
let switcher = Switcher::new(stack);
self.top_stack.add_named(&switcher.container, "content_switcher");
self.top_stack.set_visible_child(&switcher.container);
}
pub(crate) fn set_header_state(&self, state: HeaderState) {
......@@ -149,13 +150,11 @@ impl Default for MainHeader {
let popovers = Popovers::new();
// Build the top stack
let switcher = TopSwitcher::new();
let top_stack = gtk::Stack::new();
let title = gtk::Label::new("Social");
title.get_style_context().map(|ctx| ctx.add_class("title"));
top_stack.set_homogeneous(false);
top_stack.add_named(&switcher.container, "content_switcher");
top_stack.add_named(&title, "main_title");
headerbar.set_custom_title(&top_stack);
......@@ -165,7 +164,6 @@ impl Default for MainHeader {
account_button,
menu_button,
top_stack,
switcher,
popovers,
}
}
......
......@@ -24,6 +24,7 @@ mod popovers;
mod register_page;
mod toot_widget;
mod top_switcher;
mod switcher;
// Export for use as widgets::*
pub use self::bottom_switcher::BottomSwitcher;
......@@ -32,3 +33,4 @@ pub use self::popovers::Popovers;
pub use self::register_page::RegisterPage;
pub use self::toot_widget::TootWidget;
pub use self::top_switcher::TopSwitcher;
pub use self::switcher::Switcher;
//
// switcher.rs
//
// Copyright 2019 Christopher Davis <brainblasted@disroot.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: GPL-3.0-or-later
use gtk::prelude::*;
use std::rc::{
Rc,
Weak
};
const DEFAULT_ICON: &'static str = "application-x-executable-symbolic";
pub struct Switcher<'s> {
pub container: gtk::Box,
stack: &'s gtk::Stack,
info_boxes: Vec<gtk::Box>,
}
impl<'s> Switcher<'s> {
pub fn new(stack: &'s gtk::Stack) -> Self {
let mut info_boxes = vec!();
let container = gtk::Box::new(gtk::Orientation::Horizontal, 0);
let mut last_btn = None;
for ref child in stack.get_children() {
let (btn, info) = create_child_button(&stack, child);
container.pack_start(&btn, true, true, 0);
let weak_btn = btn.downgrade();
last_btn.map(move |last: gtk::RadioButton| {
weak_btn.upgrade().map(move |btn| {
btn.join_group(&last);
});
});
last_btn = Some(btn);
info_boxes.push(info);
}
// This is where I'm getting confused regarding the logic
// of changing sizes
container.connect_size_allocate(move |container, alloc| {
let (_, natural_width) = container.get_preferred_width();
if natural_width >= alloc.width {
for child in container.get_children() {
child.get_style_context().map(|ctx| ctx.add_class("mobile"));
child.dynamic_cast::<gtk::Bin>().map(|bin| {
let child = bin.get_child().unwrap();
child.dynamic_cast::<gtk::Box>().map(|gtkbox| {
gtkbox.set_property("orientation", &gtk::Orientation::Vertical).unwrap();
gtkbox.set_property("spacing", &0).unwrap();
}).unwrap();
}).unwrap();
}
} else {
for child in container.get_children() {
child.dynamic_cast::<gtk::Bin>().map(|bin| {
let child = bin.get_child().unwrap();
child.dynamic_cast::<gtk::Box>().map(|gtkbox| {
gtkbox.set_property("orientation", &gtk::Orientation::Horizontal).unwrap();
gtkbox.set_property("spacing", &6).unwrap();
}).unwrap();
}).unwrap();
}
}
});
container.get_style_context().map(|ctx| {
ctx.add_class("switcher");
});
container.set_homogeneous(true);
container.show_all();
let switcher = Self { stack, container, info_boxes };
switcher
}
}
fn create_child_button(
stack: &gtk::Stack,
stack_child: &gtk::Widget) -> (gtk::RadioButton, gtk::Box) {
let info_box = gtk::Box::new(gtk::Orientation::Horizontal, 6);
let radio_btn = gtk::RadioButton::new();
let child_index = stack.get_child_position(stack_child);
let child_title = stack.get_child_title(stack_child).unwrap_or(format!("Page{}", child_index));
let child_icon = stack.get_child_icon_name(stack_child).unwrap_or(DEFAULT_ICON.to_string());
let icon = gtk::Image::new_from_icon_name(child_icon.as_str(), gtk::IconSize::Button.into());
let label = gtk::Label::new(child_title.as_str());
info_box.pack_start(&icon, false, false, 0);
info_box.pack_start(&label, false, false, 0);
info_box.set_halign(gtk::Align::Center);
info_box.set_valign(gtk::Align::Center);
radio_btn.add(&info_box);
radio_btn.set_property_draw_indicator(false);
radio_btn.get_style_context().map(|ctx| {
ctx.add_class("flat");
});
if let Some(child) = stack.get_visible_child() {
if child == *stack_child {
radio_btn.set_active(true);
}
}
let weak_child = stack_child.downgrade();
let weak_stack = stack.downgrade();
let w_c = weak_child.clone();
radio_btn.connect_toggled(move |btn| {
let stack = weak_stack.upgrade().unwrap();
let child = w_c.upgrade().unwrap();
if btn.get_active() {
stack.set_visible_child(&child);
}
});
let weak_btn = radio_btn.downgrade();
stack.connect_property_visible_child_notify(move |stack| {
let btn = weak_btn.upgrade().unwrap();
let stack_child = weak_child.upgrade().unwrap();
if let Some(child) = stack.get_visible_child() {
if child == stack_child {
btn.set_active(true);
}
}
});
(radio_btn, info_box)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment