// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_BROWSER_UI_GTK_TABS_TAB_RENDERER_GTK_H_ #define CHROME_BROWSER_UI_GTK_TABS_TAB_RENDERER_GTK_H_ #pragma once #include #include #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/string16.h" #include "chrome/browser/ui/gtk/owned_widget_gtk.h" #include "content/common/notification_observer.h" #include "content/common/notification_registrar.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/animation/animation_delegate.h" #include "ui/base/gtk/gtk_signal.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" #include "ui/gfx/rect.h" namespace gfx { class Size; } // namespace gfx class CustomDrawButton; class GtkThemeService; class TabContents; namespace ui { class SlideAnimation; class ThemeProvider; class ThrobAnimation; } class TabRendererGtk : public ui::AnimationDelegate, public NotificationObserver { public: // Possible animation states. enum AnimationState { ANIMATION_NONE, ANIMATION_WAITING, ANIMATION_LOADING }; class LoadingAnimation : public NotificationObserver { public: struct Data { explicit Data(ui::ThemeProvider* theme_provider); Data(int loading, int waiting, int waiting_to_loading); SkBitmap* waiting_animation_frames; SkBitmap* loading_animation_frames; int loading_animation_frame_count; int waiting_animation_frame_count; int waiting_to_loading_frame_count_ratio; }; explicit LoadingAnimation(ui::ThemeProvider* theme_provider); // Used in unit tests to inject specific data. explicit LoadingAnimation(const LoadingAnimation::Data& data); virtual ~LoadingAnimation(); // Advance the loading animation to the next frame, or hide the animation if // the tab isn't loading. Returns |true| if the icon area needs to be // repainted. bool ValidateLoadingAnimation(AnimationState animation_state); AnimationState animation_state() const { return animation_state_; } int animation_frame() const { return animation_frame_; } const SkBitmap* waiting_animation_frames() const { return data_->waiting_animation_frames; } const SkBitmap* loading_animation_frames() const { return data_->loading_animation_frames; } // Provide NotificationObserver implementation. virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); private: scoped_ptr data_; // Used to listen for theme change notifications. NotificationRegistrar registrar_; // Gives us our throbber images. ui::ThemeProvider* theme_service_; // Current state of the animation. AnimationState animation_state_; // The current index into the Animation image strip. int animation_frame_; DISALLOW_COPY_AND_ASSIGN(LoadingAnimation); }; explicit TabRendererGtk(ui::ThemeProvider* theme_provider); virtual ~TabRendererGtk(); // TabContents. If only the loading state was updated, the loading_only flag // should be specified. If other things change, set this flag to false to // update everything. virtual void UpdateData(TabContents* contents, bool app, bool loading_only); // Sets the blocked state of the tab. void SetBlocked(bool pinned); bool is_blocked() const; // Sets the mini-state of the tab. void set_mini(bool mini) { data_.mini = mini; } bool mini() const { return data_.mini; } // Sets the app state of the tab. void set_app(bool app) { data_.app = app; } bool app() const { return data_.app; } // Are we in the process of animating a mini tab state change on this tab? void set_animating_mini_change(bool value) { data_.animating_mini_change = value; } // Updates the display to reflect the contents of this TabRenderer's model. void UpdateFromModel(); // Returns true if the Tab is selected, false otherwise. virtual bool IsSelected() const; // Returns true if the Tab is visible, false otherwise. virtual bool IsVisible() const; // Sets the visibility of the Tab. virtual void SetVisible(bool visible) const; // Paints the tab into |canvas|. virtual void Paint(gfx::Canvas* canvas); // Paints the tab into a SkBitmap. virtual SkBitmap PaintBitmap(); // Paints the tab, and keeps the result server-side. The returned surface must // be freed with cairo_surface_destroy(). virtual cairo_surface_t* PaintToSurface(); // There is no PaintNow available, so the fastest we can do is schedule a // paint with the windowing system. virtual void SchedulePaint(); // Notifies the Tab that the close button has been clicked. virtual void CloseButtonClicked(); // Sets the bounds of the tab. virtual void SetBounds(const gfx::Rect& bounds); // Provide NotificationObserver implementation. virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); // Advance the loading animation to the next frame, or hide the animation if // the tab isn't loading. Returns |true| if the icon area needs to be // repainted. bool ValidateLoadingAnimation(AnimationState animation_state); // Repaint only the area of the tab that contains the favicon. void PaintFaviconArea(GdkEventExpose* event); // Returns whether the Tab should display a favicon. bool ShouldShowIcon() const; // Returns the minimum possible size of a single unselected Tab. static gfx::Size GetMinimumUnselectedSize(); // Returns the minimum possible size of a selected Tab. Selected tabs must // always show a close button and have a larger minimum size than unselected // tabs. static gfx::Size GetMinimumSelectedSize(); // Returns the preferred size of a single Tab, assuming space is // available. static gfx::Size GetStandardSize(); // Returns the width for mini-tabs. Mini-tabs always have this width. static int GetMiniWidth(); // Loads the images to be used for the tab background. static void LoadTabImages(); // Sets the colors used for painting text on the tabs. static void SetSelectedTitleColor(SkColor color); static void SetUnselectedTitleColor(SkColor color); static gfx::Font* title_font() { return title_font_; } // Returns the bounds of the Tab. int x() const { return bounds_.x(); } int y() const { return bounds_.y(); } int width() const { return bounds_.width(); } int height() const { return bounds_.height(); } gfx::Rect bounds() const { return bounds_; } gfx::Rect favicon_bounds() const { return favicon_bounds_; } // Returns the non-mirrored (LTR) bounds of this tab. gfx::Rect GetNonMirroredBounds(GtkWidget* parent) const; // Returns the requested bounds of the tab. gfx::Rect GetRequisition() const; GtkWidget* widget() const { return tab_.get(); } // Start/stop the mini-tab title animation. void StartMiniTabTitleAnimation(); void StopMiniTabTitleAnimation(); void set_vertical_offset(int offset) { background_offset_y_ = offset; } protected: const gfx::Rect& title_bounds() const { return title_bounds_; } const gfx::Rect& close_button_bounds() const { return close_button_bounds_; } // Returns the title of the Tab. string16 GetTitle() const; // enter-notify-event handler that signals when the mouse enters the tab. CHROMEGTK_CALLBACK_1(TabRendererGtk, gboolean, OnEnterNotifyEvent, GdkEventCrossing*); // leave-notify-event handler that signals when the mouse enters the tab. CHROMEGTK_CALLBACK_1(TabRendererGtk, gboolean, OnLeaveNotifyEvent, GdkEventCrossing*); private: class FaviconCrashAnimation; // The data structure used to hold cached bitmaps. We need to manually free // the bitmap in CachedBitmap when we remove it from |cached_bitmaps_|. We // handle this when we replace images in the map and in the destructor. struct CachedBitmap { int bg_offset_x; int bg_offset_y; SkBitmap* bitmap; }; typedef std::map, CachedBitmap> BitmapCache; // Model data. We store this here so that we don't need to ask the underlying // model, which is tricky since instances of this object can outlive the // corresponding objects in the underlying model. struct TabData { TabData() : is_default_favicon(false), loading(false), crashed(false), incognito(false), show_icon(true), mini(false), blocked(false), animating_mini_change(false), app(false) { } SkBitmap favicon; bool is_default_favicon; string16 title; bool loading; bool crashed; bool incognito; bool show_icon; bool mini; bool blocked; bool animating_mini_change; bool app; }; // TODO(jhawkins): Move into TabResources class. struct TabImage { SkBitmap* image_l; SkBitmap* image_c; SkBitmap* image_r; int l_width; int r_width; int y_offset; }; // Overridden from ui::AnimationDelegate: virtual void AnimationProgressed(const ui::Animation* animation); virtual void AnimationCanceled(const ui::Animation* animation); virtual void AnimationEnded(const ui::Animation* animation); // Starts/Stops the crash animation. void StartCrashAnimation(); void StopCrashAnimation(); // Return true if the crash animation is currently running. bool IsPerformingCrashAnimation() const; // Set the temporary offset for the favicon. This is used during animation. void SetFaviconHidingOffset(int offset); void DisplayCrashedFavicon(); void ResetCrashedFavicon(); // Generates the bounds for the interior items of the tab. void Layout(); // Returns the local bounds of the tab. This returns the rect // {0, 0, width(), height()} for now, as we don't yet support borders. gfx::Rect GetLocalBounds(); // Moves the close button widget within the GtkFixed container. void MoveCloseButtonWidget(); // Returns the largest of the favicon, title text, and the close button. static int GetContentHeight(); // A helper method for generating the masked bitmaps used to draw the curved // edges of tabs. We cache the generated bitmaps because they can take a // long time to compute. SkBitmap* GetMaskedBitmap(const SkBitmap* mask, const SkBitmap* background, int bg_offset_x, int bg_offset_y); BitmapCache cached_bitmaps_; // Paints the tab, minus the close button. void PaintTab(GdkEventExpose* event); // Paint various portions of the Tab void PaintTitle(gfx::Canvas* canvas); void PaintIcon(gfx::Canvas* canvas); void PaintTabBackground(gfx::Canvas* canvas); void PaintInactiveTabBackground(gfx::Canvas* canvas); void PaintActiveTabBackground(gfx::Canvas* canvas); void PaintLoadingAnimation(gfx::Canvas* canvas); // Returns the number of favicon-size elements that can fit in the tab's // current size. int IconCapacity() const; // Returns whether the Tab should display a close button. bool ShouldShowCloseBox() const; CustomDrawButton* MakeCloseButton(); // Gets the throb value for the tab. When a tab is not selected the // active background is drawn at |GetThrobValue()|%. This is used for hover // and mini-tab title change effects. double GetThrobValue(); // Handles the clicked signal for the close button. CHROMEGTK_CALLBACK_0(TabRendererGtk, void, OnCloseButtonClicked); // Handles middle clicking the close button. CHROMEGTK_CALLBACK_1(TabRendererGtk, gboolean, OnCloseButtonMouseRelease, GdkEventButton*); // expose-event handler that redraws the tab. CHROMEGTK_CALLBACK_1(TabRendererGtk, gboolean, OnExposeEvent, GdkEventExpose*); // size-allocate handler used to update the current bounds of the tab. CHROMEGTK_CALLBACK_1(TabRendererGtk, void, OnSizeAllocate, GtkAllocation*); // TODO(jhawkins): Move to TabResources. static void InitResources(); static bool initialized_; // The bounds of various sections of the display. gfx::Rect favicon_bounds_; gfx::Rect title_bounds_; gfx::Rect close_button_bounds_; TabData data_; static TabImage tab_active_; static TabImage tab_inactive_; static TabImage tab_alpha_; static gfx::Font* title_font_; static int title_font_height_; static int close_button_width_; static int close_button_height_; static SkColor selected_title_color_; static SkColor unselected_title_color_; // The GtkDrawingArea we draw the tab on. OwnedWidgetGtk tab_; // Whether we're showing the icon. It is cached so that we can detect when it // changes and layout appropriately. bool showing_icon_; // Whether we are showing the close button. It is cached so that we can // detect when it changes and layout appropriately. bool showing_close_button_; // The offset used to animate the favicon location. int favicon_hiding_offset_; // The animation object used to swap the favicon with the sad tab icon. scoped_ptr crash_animation_; // Set when the crashed favicon should be displayed. bool should_display_crashed_favicon_; // The bounds of this Tab. gfx::Rect bounds_; // The requested bounds of this tab. These bounds are relative to the // tabstrip. gfx::Rect requisition_; // Hover animation. scoped_ptr hover_animation_; // Animation used when the title of an inactive mini-tab changes. scoped_ptr mini_title_animation_; // Contains the loading animation state. LoadingAnimation loading_animation_; // The offset used to paint the tab theme images. int background_offset_x_; // The vertical offset used to paint the tab theme images. Controlled by the // tabstrip and plumbed here to offset the theme image by the size of the // alignment in the BrowserTitlebar. int background_offset_y_; GtkThemeService* theme_service_; // The close button. scoped_ptr close_button_; // The current color of the close button. SkColor close_button_color_; // Used to listen for theme change notifications. NotificationRegistrar registrar_; DISALLOW_COPY_AND_ASSIGN(TabRendererGtk); }; #endif // CHROME_BROWSER_UI_GTK_TABS_TAB_RENDERER_GTK_H_